Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:

- run: pnpm run test
name: Run tests
env:
ROCKETCHAT_URL: ${{ secrets.ROCKETCHAT_URL }}
ROCKETCHAT_USER_ID: ${{ secrets.ROCKETCHAT_USER_ID }}
ROCKETCHAT_TOKEN: ${{ secrets.ROCKETCHAT_TOKEN }}

- run: pnpm dlx clawhub package publish . --dry-run --json --owner @dodaa08
name: Validate ClawHub publish
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,4 @@ package-lock.json
.vscode
node_modules
dist
.pnpm-store/
ROADMAP.md
preview.md
revert.txt
.pnpm-store/
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,21 @@ You should see `gateway - online` and `runtime - ready`.
| [COMMANDS.md](https://github.com/RocketChat/OpenClaw.Plugin/blob/main/docs/COMMANDS.md) | Complete command reference |
| [SETUP.md](https://github.com/RocketChat/OpenClaw.Plugin/blob/main/docs/SETUP.md) | Full installation, credentials & email setup |
| [CONTRIBUTING.md](https://github.com/RocketChat/OpenClaw.Plugin/blob/main/CONTRIBUTING.md) | Contributors guide |

## Media Storage & Handling

When users send media (images, audio, etc.) in Rocket.Chat, the plugin downloads the files locally to `~/.openclaw/media/inbound/`.

- **Why locally?** This allows the OpenClaw agent to reliably process the actual file bytes from the filesystem rather than struggling with URL authentication or timeouts.
- **Limits**: The plugin currently caps downloads at **20MB** per file and supports `image/`, `audio/`, `video/`, and `application/` MIME types.
- **Cleanup**: Currently, there is no automatic auto-prune for these files. We recommend users set up a cron job to clean up the folder periodically, e.g.: `find ~/.openclaw/media/inbound -type f -mtime +7 -delete`.

## Roadmap / Leftovers

_Future enhancements currently being tracked:_

- [ ] Expanding End-to-End (E2E) and integration test coverage across the repository.
- [ ] Preparing project for official v1 release.
- [ ] Addressing remaining bugs and structural updates from our internal trackers:
- [Notion Bug Tracker](https://deserted-education-78a.notion.site/Bugs-to-solve-3cf53cee1e07801b8a25d518f956af23)
- [GSOC Submission Gist](https://gist.github.com/dodaa08/883e8d7d5e2e2d17dd345dfafe918eb6)
76 changes: 76 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,33 @@ Plugin DDP Client
| **Post** | DDP: send typing stop signal; REST: post message |
| **Attachments** | Download → upload via REST → attach reference |

### Inbound media context

`buildMediaContext()` (`src/service/inbound.ts`) downloads inbound Rocket.Chat file
attachments to temp paths (or keeps URLs) and exposes them to OpenClaw core media
understanding. It emits **both** the legacy `Media*` fields and the newer
`Attachment*` compatibility names, so all the following context keys are available:

| Family | Keys |
| ------------- | -------------------------------------------------------------------------- |
| **Path** | `MediaPath`/`MediaPaths`, `AttachmentPath`/`AttachmentPaths` |
| **URL** | `MediaUrl`/`MediaUrls`, `AttachmentUrl`/`AttachmentUrls` |
| **Type/MIME** | `MediaType`/`MediaTypes`, `AttachmentContentType`/`AttachmentContentTypes` |
| **Directory** | `AttachmentDir`/`AttachmentDirs` (path dirname) |
| **Index** | `AttachmentIndex`/`AttachmentIndexes` |

Media understanding in core reads the `MediaPath`/`MediaUrls`/`MediaType` family
via `normalizeAttachments()`; the `Attachment*` names are the current CLI-template
tokens the docs reference.

> **Why the audio CLI config uses `{{MediaPath}}`, not `{{AttachmentPath}}`**
> For a `whisper-cli` audio CLI entry, core's `resolveCliMediaPath()` transcodes
> non-WAV audio (e.g. Rocket.Chat `.ogg` voice notes) to a 16 kHz mono WAV and sets
> that converted path as `templCtx.MediaPath`. `{{AttachmentPath}}` resolves to the
> **original** (unconverted) file from the inbound context and would bypass that
> transcode. Keep `{{MediaPath}}` in `tools.media.audio.models[].args` so
> whisper-cli always receives the transcoded WAV.

## Commands

Commands are parsed by `CommandParser.parse()` and route three ways:
Expand Down Expand Up @@ -235,6 +262,55 @@ Rocket.Chat Server
- Access control per-bot
- Scale horizontally (add more bots as needed)

## Per-Agent (Per-Bot) Config

### Location

Agent-level config lives in two places:

```
~/.openclaw/
├─ openclaw.json # agents.list[] entries: per-agent model selection
└─ agents/<agent-id>/
└─ agent/
└─ models.json # Per-agent provider/model catalog
```

### Structure

Each entry in `agents.list[]` in `openclaw.json` can carry its own `model` selector:

```json
{
"agents": {
"list": [
{
"id": "rc-openclaw2nd",
"workspace": "/home/me/.openclaw/agents/rc-openclaw2nd",
"agentDir": "/home/me/.openclaw/agents/rc-openclaw2nd/agent",
"model": {
"primary": "nvidia-nim/claude-3-freecc-no-thinking/nvidia_nim/nvidia/nemotron-3-super-120b-a12b",
"fallbacks": ["openrouter/google/gemini-2.0-flash-thinking-exp:free", "ollama/mistral:7b"]
}
}
]
}
}
```

Field meanings:

- `model.primary` — the provider/model ref used first for that agent's replies. Convention: `<provider>/<model-id>`.
- `model.fallbacks` — ordered list of alternate provider/model refs tried automatically on overload, timeout, or availability errors. When a primary fails, OpenClaw walks this chain instead of surfacing the error.
- Omit `model` to inherit `agents.defaults.model` (the global primary).

Per-agent behavior is then resolved as:

1. Agent's own `model` (if set) → overrides `agents.defaults.model`
2. Each ref is a provider/model in `agents.defaults.models` or the agent's own catalog
3. On failure, the runtime advances through `fallbacks` (log marker `model_fallback_decision`)

## Data Deduplication

Prevents message replay after restart or duplicate receipt:
Expand Down
86 changes: 13 additions & 73 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,18 @@ Control how the agent responds.

## Tools & Skills

| Command | Description |
| ------------------ | ------------------------------------- |
| `!tools` | List tools available to the agent |
| `!skills` | List installed skills with usage info |
| `!skill <name>` | Run a specific skill |
| `!skill cron` | Show cron skill help |
| `!skill email` | Show email skill help |
| `!skill configure` | Show setup status for skills |
| Command | Description |
| --------- | -------------------------------------------- |
| `!tools` | List tools available to the agent |
| `!skills` | List installed skills (use via inbound chat) |

### Owner-Only Skills

Certain powerful skills (e.g. bash execution, file manipulation) are restricted strictly to the **Owner** of the bot for security reasons.

- You must be listed in `openclaw.json` under `accounts.<id>.owner` (e.g., `"owner": "admin-user"`).
- To use an owner-only skill, simply instruct the bot in your DM or a private channel where the bot is present.
- Non-owner users who try to invoke owner-only skills will receive an unauthorized error from the bot.

## Cron Jobs

Expand All @@ -124,43 +128,6 @@ Schedule one-shot reminders or repeating tasks.
!cron stop check disk space
```

## Email

Send, fetch, and summarize emails. Requires env vars — see [SETUP.md](SETUP.md#email-skills).

| Command | Description |
| --------------------------------------- | ------------------------------------ |
| `!email send <to> : <subject> : <body>` | Send an email |
| `!email fetch <count> [account]` | Fetch recent emails (max 100) |
| `!email summarize <count> [account]` | Fetch + AI-summarize emails (max 10) |
| `!email` or `!email help` | Show email usage |

**Examples:**

```
!email send alice@example.com : Meeting : Let's meet at 3pm
!email fetch 5
!email fetch 10 user@gmail.com
!email summarize 5
```

**Requirements:**

- **Send:** `AGENTMAIL_API_KEY` or `EMAIL_SMTP_USER` + `EMAIL_SMTP_PASS` env var
- **Fetch:** `GMAIL_APP_PASSWORD` env var + `GMAIL_ACCOUNT` (or pass account as arg)

See [SETUP.md](./SETUP.md) for full reference.

## Configure

Check skill setup status and get configuration steps.

| Command | Description |
| ------------ | ------------------------------------------------------- |
| `!configure` | Show which skills are configured and how to set them up |

Returns the status of email send/fetch and shows the env vars needed for each.

## Permission Model

Commands are split into two tiers:
Expand All @@ -170,37 +137,10 @@ Commands are split into two tiers:
| **Public** | Anyone in a room where the bot is present |
| **Owner** | Only the bot owner (set in `openclaw.json` under `accounts.<id>.owner`) |

Owner-only commands: `add-bot`, `remove-bot`, `add-group`, `revoke`, `access`, `bots`, `email`, `configure`
Owner-only commands: `add-bot`, `remove-bot`, `add-group`, `revoke`, `access`, `bots`

Non-owners see a permission error when trying owner-only commands.

### Owner-only skills (natural language)

Beyond `!commands`, a lent/granted user can also ask the bot to perform actions in natural
language (e.g. "send an email to ..."). To block owner-level skills from non-owners, each bot
carries a guardrail instruction that tells the agent to refuse those skills unless the requester
is the bot owner.

Configure the list per account in `openclaw.json` under `channels.rocketchat.accounts.<id>`:

```json
{
"channels": {
"rocketchat": {
"accounts": {
"<bot-id>": {
"owner": "adminusername",
"ownerOnlySkills": ["email"]
}
}
}
}
}
```

If `ownerOnlySkills` is omitted, it defaults to `["email"]`. The guardrail is injected only for
non-owner senders with valid access; the owner's messages are unaffected.

## Unknown Command

If you type a command that doesn't exist, the bot replies:
Expand Down
Loading
Loading