docs: document embedded agent runtime
This commit is contained in:
@@ -155,7 +155,7 @@ Optional: enable/configure clawd’s dedicated browser control (defaults are alr
|
|||||||
- [Configuration Guide](./docs/configuration.md)
|
- [Configuration Guide](./docs/configuration.md)
|
||||||
- [Gateway runbook](./docs/gateway.md)
|
- [Gateway runbook](./docs/gateway.md)
|
||||||
- [Discovery + transports](./docs/discovery.md)
|
- [Discovery + transports](./docs/discovery.md)
|
||||||
- [Agent Integration](./docs/agents.md)
|
- [Agent Runtime](./docs/agent.md)
|
||||||
- [Group Chats](./docs/group-messages.md)
|
- [Group Chats](./docs/group-messages.md)
|
||||||
- [Security](./docs/security.md)
|
- [Security](./docs/security.md)
|
||||||
- [Troubleshooting](./docs/troubleshooting.md)
|
- [Troubleshooting](./docs/troubleshooting.md)
|
||||||
@@ -221,7 +221,7 @@ In chat, send `/status` to see if the agent is reachable, how much context the s
|
|||||||
|
|
||||||
### Sessions, surfaces, and WebChat
|
### Sessions, surfaces, and WebChat
|
||||||
|
|
||||||
- Direct chats now share a canonical session key `main` by default (configurable via `inbound.reply.session.mainKey`). Groups stay isolated as `group:<jid>`.
|
- Direct chats now share a canonical session key `main` by default (configurable via `inbound.session.mainKey`). Groups stay isolated as `group:<jid>`.
|
||||||
- WebChat attaches to `main` and hydrates history from `~/.clawdis/sessions/<SessionId>.jsonl`, so desktop view mirrors WhatsApp/Telegram turns.
|
- WebChat attaches to `main` and hydrates history from `~/.clawdis/sessions/<SessionId>.jsonl`, so desktop view mirrors WhatsApp/Telegram turns.
|
||||||
- Inbound contexts carry a `Surface` hint (e.g., `whatsapp`, `webchat`, `telegram`) for logging; replies still go back to the originating surface deterministically.
|
- Inbound contexts carry a `Surface` hint (e.g., `whatsapp`, `webchat`, `telegram`) for logging; replies still go back to the originating surface deterministically.
|
||||||
- Every inbound message is wrapped for the agent as `[Surface FROM HOST/IP TIMESTAMP] body`:
|
- Every inbound message is wrapped for the agent as `[Surface FROM HOST/IP TIMESTAMP] body`:
|
||||||
|
|||||||
@@ -3,59 +3,19 @@ summary: "Design notes for a direct `clawdis agent` CLI subcommand without Whats
|
|||||||
read_when:
|
read_when:
|
||||||
- Adding or modifying the agent CLI entrypoint
|
- Adding or modifying the agent CLI entrypoint
|
||||||
---
|
---
|
||||||
# Plan: `clawdis agent` (direct-to-agent invocation)
|
# `clawdis agent` (direct-to-agent invocation)
|
||||||
|
|
||||||
Goal: Add a CLI subcommand that talks directly to the configured agent command (no WhatsApp send), while reusing the same session handling and config clawdis already uses for auto-replies.
|
`clawdis agent` lets you talk to the **embedded** agent runtime directly (no chat send unless you opt in), while reusing the same session store and thinking/verbose persistence as inbound auto-replies.
|
||||||
|
|
||||||
## Why
|
|
||||||
- Sometimes we want to poke the agent directly (same prompt templates/sessions) without sending a WhatsApp message.
|
|
||||||
- Current flows (`send`, gateway, directives) always route through WhatsApp or add wrapping text; we need a clean “talk to agent now” tool.
|
|
||||||
|
|
||||||
## Behavior
|
## Behavior
|
||||||
- Command: `clawdis agent`
|
|
||||||
- Required: `--message <text>`
|
- Required: `--message <text>`
|
||||||
- Session selection:
|
- Session selection:
|
||||||
- If `--session-id` given, use it.
|
- If `--session-id` is given, reuse it.
|
||||||
- Else if `--to <e164>` given, derive session key like auto-reply (`per-sender`, same normalization) and load/create session id from `session store` path in config.
|
- Else if `--to <e164>` is given, derive the session key from `inbound.session.scope` (direct chats collapse to `inbound.session.mainKey`).
|
||||||
- Else error (“need --to or --session-id”).
|
- Runs the embedded Pi agent (configured via `inbound.agent`).
|
||||||
- Runs the same external command as auto-reply: `inbound.reply.command` from config (honors `reply.session` options: sendSystemOnce, sendSystemOnce=false, typing, timeouts, etc.).
|
|
||||||
- Uses the same templating rules for Body as command mode, but **skips** WhatsApp-specific wrappers (group intro, media hints). Keep session intro/bodyPrefix if sendSystemOnce is false, otherwise follow session config.
|
|
||||||
- Thinking/verbose:
|
- Thinking/verbose:
|
||||||
- Accept flags `--thinking <off|minimal|low|medium|high>` and `--verbose <on|off>`.
|
- Flags `--thinking <off|minimal|low|medium|high>` and `--verbose <on|off>` persist into the session store.
|
||||||
- Persist into session store (like directive-only flow) and inject into the command invocation.
|
|
||||||
- Output:
|
- Output:
|
||||||
- Default: print the agent’s text reply to stdout.
|
- Default: prints text (and `MEDIA:<url>` lines) to stdout.
|
||||||
- `--json` flag: print full payload (text, any media URL, timing).
|
- `--json`: prints structured payloads + meta.
|
||||||
- Does **not** send anything to WhatsApp; purely local agent run.
|
- Optional: `--deliver` sends the reply back to the selected provider (requires `--to` for WhatsApp).
|
||||||
|
|
||||||
## Flags (proposed)
|
|
||||||
- `--message, -m <text>` (required)
|
|
||||||
- `--to, -t <e164>` (derive session)
|
|
||||||
- `--session-id <uuid>` (override)
|
|
||||||
- `--thinking <off|minimal|low|medium|high>`
|
|
||||||
- `--verbose <on|off>`
|
|
||||||
- `--json` (structured output)
|
|
||||||
- `--timeout <sec>` (override command timeout)
|
|
||||||
|
|
||||||
## Implementation steps
|
|
||||||
1) CLI:
|
|
||||||
- Add subcommand in `src/cli/program.ts`.
|
|
||||||
- Wire options, setVerbose, createDefaultDeps.
|
|
||||||
2) Command handler (new file `src/commands/agent.ts`):
|
|
||||||
- Load config.
|
|
||||||
- Resolve session store + session id (reuse `deriveSessionKey`, `loadSessionStore`, `saveSessionStore`).
|
|
||||||
- Apply thinking/verbose overrides and persist to session entry.
|
|
||||||
- Build command body (no WhatsApp wrappers; honor sessionIntro/bodyPrefix as per config).
|
|
||||||
- Call `runCommandWithTimeout` (same as auto-reply) and parse response (reuse splitter for MEDIA, etc.).
|
|
||||||
- Return text (and mediaUrl) to stdout / JSON.
|
|
||||||
3) Share logic:
|
|
||||||
- Extract helper(s) from `auto-reply/reply.ts` if needed (session + thinking persistence) to avoid duplication.
|
|
||||||
4) Tests:
|
|
||||||
- Unit tests for handler: session creation, thinking persistence, resume with `--session-id`, JSON output.
|
|
||||||
- Snapshot of command args to ensure no WhatsApp wrappers.
|
|
||||||
5) Docs:
|
|
||||||
- Add usage examples to CLI help and README.
|
|
||||||
|
|
||||||
## Out of scope (for now)
|
|
||||||
- Chat directives `/cmd` in WhatsApp. (Can reuse the same handler later.)
|
|
||||||
- Media input/attachments. Start text-only; extend later if needed.
|
|
||||||
|
|||||||
48
docs/agent.md
Normal file
48
docs/agent.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
summary: "Agent runtime (embedded Pi), workspace contract, and session bootstrap"
|
||||||
|
read_when:
|
||||||
|
- Changing agent runtime, workspace bootstrap, or session behavior
|
||||||
|
---
|
||||||
|
<!-- {% raw %} -->
|
||||||
|
# Agent Runtime 🤖
|
||||||
|
|
||||||
|
CLAWDIS runs a single agent runtime: **Pi (embedded, in-process)**.
|
||||||
|
|
||||||
|
## Workspace (required)
|
||||||
|
|
||||||
|
You must set an agent home directory via `inbound.workspace`. CLAWDIS uses this as the agent’s **only** working directory (`cwd`) for tools and context.
|
||||||
|
|
||||||
|
Recommended: use `clawdis setup` to create `~/.clawdis/clawdis.json` if missing and initialize the workspace files.
|
||||||
|
|
||||||
|
## Bootstrap files (injected)
|
||||||
|
|
||||||
|
Inside `inbound.workspace`, CLAWDIS expects these user-editable files:
|
||||||
|
- `AGENTS.md` — operating instructions + “memory”
|
||||||
|
- `SOUL.md` — persona, boundaries, tone
|
||||||
|
- `TOOLS.md` — user-maintained tool notes (e.g. `imsg`, `sag`, conventions)
|
||||||
|
|
||||||
|
On the first turn of a new session, CLAWDIS injects the contents of these files directly into the agent context.
|
||||||
|
|
||||||
|
If a file is missing, CLAWDIS injects a single “missing file” marker line (and `clawdis setup` will create a safe default template).
|
||||||
|
|
||||||
|
## Built-in tools (internal)
|
||||||
|
|
||||||
|
Pi’s embedded core tools (read/bash/edit/write and related internals) are defined in code and always available. `TOOLS.md` does **not** control which tools exist; it’s guidance for how *you* want them used.
|
||||||
|
|
||||||
|
## Sessions
|
||||||
|
|
||||||
|
Session transcripts are stored as JSONL at:
|
||||||
|
- `~/.clawdis/sessions/<SessionId>.jsonl`
|
||||||
|
|
||||||
|
The session ID is stable and chosen by CLAWDIS.
|
||||||
|
|
||||||
|
## Configuration (minimal)
|
||||||
|
|
||||||
|
At minimum, set:
|
||||||
|
- `inbound.workspace`
|
||||||
|
- `inbound.allowFrom` (strongly recommended)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Next: [Group Chats](./group-messages.md)* 🦞
|
||||||
|
<!-- {% endraw %} -->
|
||||||
109
docs/agents.md
109
docs/agents.md
@@ -1,109 +0,0 @@
|
|||||||
---
|
|
||||||
summary: "Current agent integration: Pi as the sole coding agent with config examples"
|
|
||||||
read_when:
|
|
||||||
- Changing agent invocation or defaults
|
|
||||||
---
|
|
||||||
<!-- {% raw %} -->
|
|
||||||
# Agent Integration 🤖
|
|
||||||
|
|
||||||
CLAWDIS ships with a single coding-agent path: **Pi** (RPC mode). Legacy Claude/Codex/Gemini/Opencode integrations have been removed.
|
|
||||||
|
|
||||||
## Default behavior
|
|
||||||
|
|
||||||
If you don’t configure `inbound.reply`, CLAWDIS uses the bundled Pi binary in RPC mode:
|
|
||||||
- command: `pi --mode rpc {{BodyStripped}}`
|
|
||||||
- per-sender sessions (stored under `~/.clawdis/sessions/*.jsonl`)
|
|
||||||
- `/new` starts a fresh session
|
|
||||||
|
|
||||||
This is usually enough for a personal assistant setup; add `inbound.allowFrom` to restrict who can trigger it.
|
|
||||||
|
|
||||||
If you keep an `AGENTS.md` (and optional “memory” files) for the agent, set `inbound.workspace` (preferred) or `inbound.reply.cwd` so Pi runs with the right context.
|
|
||||||
|
|
||||||
## Custom agent command (still Pi)
|
|
||||||
|
|
||||||
To override the agent command, configure `inbound.reply.mode: "command"`:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
inbound: {
|
|
||||||
reply: {
|
|
||||||
mode: "command",
|
|
||||||
command: ["pi", "--mode", "rpc", "{{BodyStripped}}"],
|
|
||||||
timeoutSeconds: 1800,
|
|
||||||
agent: { kind: "pi", format: "json" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
- CLAWDIS forces `--mode rpc` for Pi invocations (even if you pass `--mode json/text`).
|
|
||||||
- If your `command` array omits `{{Body}}`/`{{BodyStripped}}`, CLAWDIS still synthesizes the prompt body for RPC mode.
|
|
||||||
|
|
||||||
## Sessions
|
|
||||||
|
|
||||||
Session behavior lives under `inbound.reply.session`:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
inbound: {
|
|
||||||
reply: {
|
|
||||||
session: {
|
|
||||||
scope: "per-sender",
|
|
||||||
resetTriggers: ["/new", "/reset"],
|
|
||||||
idleMinutes: 10080,
|
|
||||||
sendSystemOnce: true,
|
|
||||||
sessionIntro: "You are Clawd. Be a good lobster."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Defaults when `session` is enabled:
|
|
||||||
- Session files are written to `~/.clawdis/sessions/{{SessionId}}.jsonl`.
|
|
||||||
- Resume adds `--continue` automatically (Pi needs it to load prior messages).
|
|
||||||
|
|
||||||
## Heartbeats
|
|
||||||
|
|
||||||
If you enable `inbound.reply.heartbeatMinutes`, CLAWDIS periodically runs a heartbeat prompt (default: `HEARTBEAT /think:high`).
|
|
||||||
|
|
||||||
- If the agent replies with `HEARTBEAT_OK` (exact token), CLAWDIS suppresses outbound delivery for that heartbeat.
|
|
||||||
- If you want a different command for heartbeats, set `inbound.reply.heartbeatCommand`.
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
inbound: {
|
|
||||||
reply: {
|
|
||||||
heartbeatMinutes: 30,
|
|
||||||
heartbeatCommand: ["pi", "--mode", "rpc", "HEARTBEAT /think:high"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tool streaming (RPC)
|
|
||||||
|
|
||||||
RPC mode emits structured tool lifecycle events (start/result) and assistant output. These are:
|
|
||||||
- logged to `/tmp/clawdis/…`
|
|
||||||
- streamed over the Gateway WS to clients like WebChat and the macOS app
|
|
||||||
|
|
||||||
## Browser helpers
|
|
||||||
|
|
||||||
If you enable the clawd-managed browser (default on), the agent can use:
|
|
||||||
- `clawdis browser status` / `tabs` / `open <url>` / `screenshot [targetId]`
|
|
||||||
- `clawdis browser snapshot --format ai` (returns an AI snapshot with `[ref=…]` ids)
|
|
||||||
- `clawdis browser click <ref>` (click by ref from an AI snapshot)
|
|
||||||
|
|
||||||
This uses a dedicated Chrome/Chromium profile (lobster-orange by default) so it doesn’t interfere with your daily browser.
|
|
||||||
|
|
||||||
## Debugging `clawdis-mac` errors
|
|
||||||
|
|
||||||
When the agent runs `clawdis-mac` (often over SSH), the CLI prints compact, human-readable errors by default.
|
|
||||||
|
|
||||||
- To get the full `NSError` dump (domain/code/userInfo), rerun with `CLAWDIS_MAC_VERBOSE_ERRORS=1` in the environment.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Next: [Group Chats](./group-messages.md)* 🦞
|
|
||||||
<!-- {% endraw %} -->
|
|
||||||
@@ -90,20 +90,17 @@ Now message the assistant number from your allowlisted phone.
|
|||||||
|
|
||||||
## Give the agent a workspace (AGENTS.md)
|
## Give the agent a workspace (AGENTS.md)
|
||||||
|
|
||||||
Pi (the bundled coding agent) will read operating instructions and “memory” from its current working directory.
|
Clawd reads operating instructions and “memory” from its workspace directory.
|
||||||
|
|
||||||
By default, Clawdis uses `~/.clawdis/workspace` as the agent workspace, and will create it (plus a starter `AGENTS.md`) automatically on first agent run.
|
By default, Clawdis uses `~/clawd` as the agent workspace, and will create it (plus starter `AGENTS.md`, `SOUL.md`, `TOOLS.md`) automatically on setup/first agent run.
|
||||||
|
|
||||||
Tip: treat this folder like Clawd’s “memory” and make it a git repo (ideally private) so your `AGENTS.md` + memory files are backed up.
|
Tip: treat this folder like Clawd’s “memory” and make it a git repo (ideally private) so your `AGENTS.md` + memory files are backed up.
|
||||||
|
|
||||||
From the CLAWDIS repo:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.clawdis/workspace
|
clawdis setup
|
||||||
cp docs/AGENTS.default.md ~/.clawdis/workspace/AGENTS.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional: choose a different workspace with `inbound.workspace` (supports `~`). `inbound.reply.cwd` still works and overrides it.
|
Optional: choose a different workspace with `inbound.workspace` (supports `~`).
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
@@ -115,8 +112,8 @@ Optional: choose a different workspace with `inbound.workspace` (supports `~`).
|
|||||||
|
|
||||||
## The config that turns it into “an assistant”
|
## The config that turns it into “an assistant”
|
||||||
|
|
||||||
CLAWDIS defaults to a good Pi setup even without `inbound.reply`, but you’ll usually want to tune:
|
CLAWDIS defaults to a good assistant setup, but you’ll usually want to tune:
|
||||||
- session intro (personality + instructions)
|
- persona/instructions in `SOUL.md`
|
||||||
- thinking defaults (if desired)
|
- thinking defaults (if desired)
|
||||||
- heartbeats (once you trust it)
|
- heartbeats (once you trust it)
|
||||||
|
|
||||||
@@ -127,25 +124,24 @@ Example:
|
|||||||
logging: { level: "info" },
|
logging: { level: "info" },
|
||||||
inbound: {
|
inbound: {
|
||||||
allowFrom: ["+15555550123"],
|
allowFrom: ["+15555550123"],
|
||||||
|
workspace: "~/clawd",
|
||||||
groupChat: {
|
groupChat: {
|
||||||
requireMention: true,
|
requireMention: true,
|
||||||
mentionPatterns: ["@clawd", "clawd"]
|
mentionPatterns: ["@clawd", "clawd"]
|
||||||
},
|
},
|
||||||
reply: {
|
agent: {
|
||||||
mode: "command",
|
provider: "anthropic",
|
||||||
// Pi is bundled; CLAWDIS forces --mode rpc for Pi runs.
|
model: "claude-opus-4-5",
|
||||||
command: ["pi", "--mode", "rpc", "{{BodyStripped}}"],
|
thinkingDefault: "high",
|
||||||
timeoutSeconds: 1800,
|
timeoutSeconds: 1800,
|
||||||
bodyPrefix: "/think:high ",
|
|
||||||
session: {
|
|
||||||
scope: "per-sender",
|
|
||||||
resetTriggers: ["/new"],
|
|
||||||
idleMinutes: 10080,
|
|
||||||
sendSystemOnce: true,
|
|
||||||
sessionIntro: "You are Clawd, a helpful space lobster assistant. Be concise for chat, save long output to files, and be careful with secrets."
|
|
||||||
},
|
|
||||||
// Start with 0; enable later.
|
// Start with 0; enable later.
|
||||||
heartbeatMinutes: 0
|
heartbeatMinutes: 0
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
scope: "per-sender",
|
||||||
|
resetTriggers: ["/new"],
|
||||||
|
idleMinutes: 10080,
|
||||||
|
mainKey: "main"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,17 +155,15 @@ Example:
|
|||||||
|
|
||||||
## Heartbeats (proactive mode)
|
## Heartbeats (proactive mode)
|
||||||
|
|
||||||
When `heartbeatMinutes > 0`, CLAWDIS periodically runs a heartbeat prompt (default: `HEARTBEAT /think:high`).
|
When `inbound.agent.heartbeatMinutes > 0`, CLAWDIS periodically runs a heartbeat prompt (default: `HEARTBEAT`).
|
||||||
|
|
||||||
- If the agent replies with `HEARTBEAT_OK` (exact token), CLAWDIS suppresses outbound delivery for that heartbeat.
|
- If the agent replies with `HEARTBEAT_OK` (exact token), CLAWDIS suppresses outbound delivery for that heartbeat.
|
||||||
- If you want a special command for heartbeats, set `inbound.reply.heartbeatCommand`.
|
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
inbound: {
|
inbound: {
|
||||||
reply: {
|
agent: {
|
||||||
heartbeatMinutes: 30,
|
heartbeatMinutes: 30
|
||||||
heartbeatCommand: ["pi", "--mode", "rpc", "HEARTBEAT /think:high"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,20 @@ read_when:
|
|||||||
|
|
||||||
CLAWDIS reads an optional **JSON5** config from `~/.clawdis/clawdis.json` (comments + trailing commas allowed).
|
CLAWDIS reads an optional **JSON5** config from `~/.clawdis/clawdis.json` (comments + trailing commas allowed).
|
||||||
|
|
||||||
If the file is missing, CLAWDIS uses safe-ish defaults (bundled Pi in RPC mode + per-sender sessions). You usually only need a config to:
|
If the file is missing, CLAWDIS uses safe-ish defaults (embedded Pi agent + per-sender sessions + workspace `~/clawd`). You usually only need a config to:
|
||||||
- restrict who can trigger the bot (`inbound.allowFrom`)
|
- restrict who can trigger the bot (`inbound.allowFrom`)
|
||||||
- tune group mention behavior (`inbound.groupChat`)
|
- tune group mention behavior (`inbound.groupChat`)
|
||||||
- customize the agent command (`inbound.reply.command`)
|
- set the agent’s workspace (`inbound.workspace`)
|
||||||
- set the agent’s identity (`identity`)
|
- tune the embedded agent (`inbound.agent`) and session behavior (`inbound.session`)
|
||||||
|
- set the agent’s identity (`identity`)
|
||||||
|
|
||||||
## Minimal config (recommended starting point)
|
## Minimal config (recommended starting point)
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
inbound: {
|
inbound: {
|
||||||
allowFrom: ["+15555550123"]
|
allowFrom: ["+15555550123"],
|
||||||
|
workspace: "~/clawd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -33,7 +35,6 @@ Optional agent identity used for defaults and UX. This is written by the macOS o
|
|||||||
If set, CLAWDIS derives defaults (only when you haven’t set them explicitly):
|
If set, CLAWDIS derives defaults (only when you haven’t set them explicitly):
|
||||||
- `inbound.responsePrefix` from `identity.emoji`
|
- `inbound.responsePrefix` from `identity.emoji`
|
||||||
- `inbound.groupChat.mentionPatterns` from `identity.name` (so “@Samantha” works in groups)
|
- `inbound.groupChat.mentionPatterns` from `identity.name` (so “@Samantha” works in groups)
|
||||||
- `inbound.reply.session.sessionIntro` when `inbound.reply` is present (and for default Pi runs)
|
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
@@ -78,58 +79,57 @@ Group messages default to **require mention** (either metadata mention or regex
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `inbound.reply`
|
### `inbound.workspace`
|
||||||
|
|
||||||
Controls how CLAWDIS produces replies. Two modes:
|
Sets the **single global workspace directory** used by the agent for file operations.
|
||||||
- `mode: "text"` — static reply from config (useful for testing)
|
|
||||||
- `mode: "command"` — run a local command and use its stdout as the reply (typical)
|
|
||||||
|
|
||||||
If you **omit** `inbound.reply`, CLAWDIS defaults to the bundled Pi binary in **RPC** mode:
|
Default: `~/clawd`.
|
||||||
- command (base): `pi --mode rpc {{BodyStripped}}`
|
|
||||||
- per-sender sessions + `/new` resets
|
|
||||||
|
|
||||||
Safety default: when invoking Pi, CLAWDIS always passes `--provider` and `--model` (unless you already specified them).
|
```json5
|
||||||
|
{
|
||||||
|
inbound: { workspace: "~/clawd" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Example command-mode config:
|
### `inbound.agent`
|
||||||
|
|
||||||
|
Controls the embedded agent runtime (provider/model/thinking/verbose/timeouts).
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
inbound: {
|
inbound: {
|
||||||
// Preferred: the agent workspace directory (used as default cwd for agent runs; supports ~).
|
workspace: "~/clawd",
|
||||||
workspace: "~/.clawdis/workspace",
|
agent: {
|
||||||
reply: {
|
provider: "anthropic",
|
||||||
mode: "command",
|
model: "claude-opus-4-5",
|
||||||
// Example: run the bundled agent (Pi) in RPC mode
|
thinkingDefault: "low",
|
||||||
command: ["pi", "--mode", "rpc", "{{BodyStripped}}"],
|
verboseDefault: "off",
|
||||||
// Optional override: working directory for this reply command (supports ~).
|
timeoutSeconds: 600,
|
||||||
// If omitted, `inbound.workspace` is used.
|
mediaMaxMb: 5,
|
||||||
cwd: "~/.clawdis/workspace",
|
|
||||||
timeoutSeconds: 1800,
|
|
||||||
heartbeatMinutes: 30,
|
heartbeatMinutes: 30,
|
||||||
// Optional: override the command used for heartbeat runs
|
contextTokens: 200000
|
||||||
heartbeatCommand: ["pi", "--mode", "rpc", "HEARTBEAT /think:high"],
|
|
||||||
session: {
|
|
||||||
scope: "per-sender",
|
|
||||||
idleMinutes: 10080,
|
|
||||||
resetTriggers: ["/new"],
|
|
||||||
sessionIntro: "You are Clawd. Be a good lobster."
|
|
||||||
},
|
|
||||||
agent: {
|
|
||||||
kind: "pi",
|
|
||||||
format: "json",
|
|
||||||
// Only used for status/usage labeling (Pi may report its own model)
|
|
||||||
provider: "anthropic",
|
|
||||||
model: "claude-opus-4-5",
|
|
||||||
contextTokens: 200000
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
### `inbound.session`
|
||||||
- `inbound.workspace` sets the default working directory for agent runs (supports `~` and is resolved to an absolute path).
|
|
||||||
- `inbound.reply.cwd` overrides the working directory for that specific reply command.
|
Controls session scoping, idle expiry, reset triggers, and where the session store is written.
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
inbound: {
|
||||||
|
session: {
|
||||||
|
scope: "per-sender",
|
||||||
|
idleMinutes: 60,
|
||||||
|
resetTriggers: ["/new"],
|
||||||
|
store: "~/.clawdis/sessions/sessions.json",
|
||||||
|
mainKey: "main"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `browser` (clawd-managed Chrome)
|
### `browser` (clawd-managed Chrome)
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ Defaults:
|
|||||||
|
|
||||||
## Template variables
|
## Template variables
|
||||||
|
|
||||||
Template placeholders are expanded in `inbound.reply.command`, `sessionIntro`, `bodyPrefix`, and other templated strings.
|
Template placeholders are expanded in `inbound.transcribeAudio.command` (and any future templated command fields).
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
@@ -193,5 +193,5 @@ Cron is a Gateway-owned scheduler for wakeups and scheduled jobs. See [Cron + wa
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Next: [Agent Integration](./agents.md)* 🦞
|
*Next: [Agent Runtime](./agent.md)* 🦞
|
||||||
<!-- {% endraw %} -->
|
<!-- {% endraw %} -->
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Last updated: 2025-12-13
|
|||||||
## Context
|
## Context
|
||||||
|
|
||||||
Clawdis already has:
|
Clawdis already has:
|
||||||
- A **periodic reply heartbeat** that runs the agent with `HEARTBEAT /think:high` and suppresses `HEARTBEAT_OK` (`src/web/auto-reply.ts`).
|
- A **periodic reply heartbeat** that runs the agent with `HEARTBEAT` and suppresses `HEARTBEAT_OK` (`src/web/auto-reply.ts`).
|
||||||
- A lightweight, in-memory **system event queue** (`enqueueSystemEvent`) that is injected into the next **main session** turn (`drainSystemEvents` in `src/auto-reply/reply.ts`).
|
- A lightweight, in-memory **system event queue** (`enqueueSystemEvent`) that is injected into the next **main session** turn (`drainSystemEvents` in `src/auto-reply/reply.ts`).
|
||||||
- A WebSocket **Gateway** daemon that is intended to be always-on (`docs/gateway.md`).
|
- A WebSocket **Gateway** daemon that is intended to be always-on (`docs/gateway.md`).
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ read_when:
|
|||||||
---
|
---
|
||||||
# Heartbeat polling plan (2025-11-26)
|
# Heartbeat polling plan (2025-11-26)
|
||||||
|
|
||||||
Goal: add a simple heartbeat poll for command-based auto-replies (Pi) that only notifies users when something matters, using the `HEARTBEAT_OK` sentinel. The heartbeat body we send is `HEARTBEAT /think:high` so the model can easily spot it.
|
Goal: add a simple heartbeat poll for the embedded agent that only notifies users when something matters, using the `HEARTBEAT_OK` sentinel. The heartbeat body we send is `HEARTBEAT` so the model can easily spot it.
|
||||||
|
|
||||||
## Prompt contract
|
## Prompt contract
|
||||||
- Extend the Pi system/identity text to explain: “If this is a heartbeat poll and nothing needs attention, reply exactly `HEARTBEAT_OK` and nothing else. For any alert, do **not** include `HEARTBEAT_OK`; just return the alert text.” Heartbeat prompt body is `HEARTBEAT /think:high`.
|
- Extend the agent system prompt to explain: “If this is a heartbeat poll and nothing needs attention, reply exactly `HEARTBEAT_OK` and nothing else. For any alert, do **not** include `HEARTBEAT_OK`; just return the alert text.” Heartbeat prompt body is `HEARTBEAT`.
|
||||||
- Keep existing WhatsApp length guidance; forbid burying the sentinel inside alerts.
|
- Keep existing WhatsApp length guidance; forbid burying the sentinel inside alerts.
|
||||||
|
|
||||||
## Config & defaults
|
## Config & defaults
|
||||||
- New config key: `inbound.reply.heartbeatMinutes` (number of minutes; `0` or undefined disables).
|
- New config key: `inbound.agent.heartbeatMinutes` (number of minutes; `0` disables).
|
||||||
- Default: 30 minutes when a command-mode reply is configured.
|
- Default: 30 minutes.
|
||||||
- New optional idle override for heartbeats: `inbound.reply.session.heartbeatIdleMinutes` (defaults to `idleMinutes`). Heartbeat skips do **not** update the session `updatedAt` so idle expiry still works.
|
- New optional idle override for heartbeats: `inbound.session.heartbeatIdleMinutes` (defaults to `idleMinutes`). Heartbeat skips do **not** update the session `updatedAt` so idle expiry still works.
|
||||||
|
|
||||||
## Poller behavior
|
## Poller behavior
|
||||||
- When gateway runs with command-mode auto-reply, start a timer with the resolved heartbeat interval.
|
- When gateway runs with command-mode auto-reply, start a timer with the resolved heartbeat interval.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ CLAWDIS is now **web-only** (Baileys). This document captures the current media
|
|||||||
## Web Provider Behavior
|
## Web Provider Behavior
|
||||||
- Input: local file path **or** HTTP(S) URL.
|
- Input: local file path **or** HTTP(S) URL.
|
||||||
- Flow: load into a Buffer, detect media kind, and build the correct payload:
|
- Flow: load into a Buffer, detect media kind, and build the correct payload:
|
||||||
- **Images:** resize & recompress to JPEG (max side 2048px) targeting `inbound.reply.mediaMaxMb` (default 5 MB), capped at 6 MB.
|
- **Images:** resize & recompress to JPEG (max side 2048px) targeting `inbound.agent.mediaMaxMb` (default 5 MB), capped at 6 MB.
|
||||||
- **Audio/Voice/Video:** pass-through up to 16 MB; audio is sent as a voice note (`ptt: true`).
|
- **Audio/Voice/Video:** pass-through up to 16 MB; audio is sent as a voice note (`ptt: true`).
|
||||||
- **Documents:** anything else, up to 100 MB, with filename preserved when available.
|
- **Documents:** anything else, up to 100 MB, with filename preserved when available.
|
||||||
- MIME detection prefers magic bytes, then headers, then file extension.
|
- MIME detection prefers magic bytes, then headers, then file extension.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ read_when:
|
|||||||
---
|
---
|
||||||
# Web Chat (macOS app)
|
# Web Chat (macOS app)
|
||||||
|
|
||||||
The macOS menu bar app embeds the WebChat UI in a WKWebView and reuses the **primary Clawd session** (`main` by default, configurable via `inbound.reply.session.mainKey`).
|
The macOS menu bar app embeds the WebChat UI in a WKWebView and reuses the **primary Clawd session** (`main` by default, configurable via `inbound.session.mainKey`).
|
||||||
|
|
||||||
- **Local mode**: loads the gateway’s loopback WebChat HTTP server (default port 18788, see `webchat.port`).
|
- **Local mode**: loads the gateway’s loopback WebChat HTTP server (default port 18788, see `webchat.port`).
|
||||||
- **Remote mode**: serves the WebChat assets locally from the mac app bundle (via `WebChatServer`) and only forwards the gateway WebSocket control port over SSH.
|
- **Remote mode**: serves the WebChat assets locally from the mac app bundle (via `WebChatServer`) and only forwards the gateway WebSocket control port over SSH.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ read_when:
|
|||||||
---
|
---
|
||||||
# Session Management
|
# Session Management
|
||||||
|
|
||||||
Clawdis treats **one session as primary**. By default the canonical key is `main` for every direct chat; no configuration is required. You can rename it via `inbound.reply.session.mainKey` if you really want, but there is still only a single primary session. Older/local sessions can stay on disk, but only the primary key is used for desktop/web chat and direct agent calls.
|
Clawdis treats **one session as primary**. By default the canonical key is `main` for every direct chat; no configuration is required. You can rename it via `inbound.session.mainKey` if you really want, but there is still only a single primary session. Older/local sessions can stay on disk, but only the primary key is used for desktop/web chat and direct agent calls.
|
||||||
|
|
||||||
## Gateway is the source of truth
|
## Gateway is the source of truth
|
||||||
All session state is **owned by the gateway** (the “master” Clawdis). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
|
All session state is **owned by the gateway** (the “master” Clawdis). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
|
||||||
@@ -25,7 +25,7 @@ All session state is **owned by the gateway** (the “master” Clawdis). UI cli
|
|||||||
- Group chats still isolate state with `group:<jid>` keys; do not reuse the primary key for groups.
|
- Group chats still isolate state with `group:<jid>` keys; do not reuse the primary key for groups.
|
||||||
|
|
||||||
## Lifecyle
|
## Lifecyle
|
||||||
- Idle expiry: `inbound.reply.session.idleMinutes` (default 60). After the timeout a new `sessionId` is minted on the next message.
|
- Idle expiry: `inbound.session.idleMinutes` (default 60). After the timeout a new `sessionId` is minted on the next message.
|
||||||
- Reset triggers: exact `/new` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through.
|
- Reset triggers: exact `/new` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through.
|
||||||
- Manual reset: delete specific keys from the store or remove the JSONL transcript; the next message recreates them.
|
- Manual reset: delete specific keys from the store or remove the JSONL transcript; the next message recreates them.
|
||||||
|
|
||||||
@@ -34,14 +34,12 @@ All session state is **owned by the gateway** (the “master” Clawdis). UI cli
|
|||||||
// ~/.clawdis/clawdis.json
|
// ~/.clawdis/clawdis.json
|
||||||
{
|
{
|
||||||
inbound: {
|
inbound: {
|
||||||
reply: {
|
session: {
|
||||||
session: {
|
scope: "per-sender", // keep group keys separate
|
||||||
scope: "per-sender", // keep group keys separate
|
idleMinutes: 120,
|
||||||
idleMinutes: 120,
|
resetTriggers: ["/new"],
|
||||||
resetTriggers: ["/new"],
|
store: "~/.clawdis/sessions/sessions.json",
|
||||||
store: "~/.clawdis/sessions/sessions.json",
|
mainKey: "main" // optional rename; still a single primary
|
||||||
mainKey: "main" // optional rename; still a single primary
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ read_when:
|
|||||||
## Resolution order
|
## Resolution order
|
||||||
1. Inline directive on the message (applies only to that message).
|
1. Inline directive on the message (applies only to that message).
|
||||||
2. Session override (set by sending a directive-only message).
|
2. Session override (set by sending a directive-only message).
|
||||||
3. Global default (`inbound.reply.thinkingDefault` in config).
|
3. Global default (`inbound.agent.thinkingDefault` in config).
|
||||||
4. Fallback: off.
|
4. Fallback: off.
|
||||||
|
|
||||||
## Setting a session default
|
## Setting a session default
|
||||||
@@ -26,7 +26,7 @@ read_when:
|
|||||||
- Confirmation reply is sent (`Thinking level set to high.` / `Thinking disabled.`). If the level is invalid (e.g. `/thinking big`), the command is rejected with a hint and the session state is left unchanged.
|
- Confirmation reply is sent (`Thinking level set to high.` / `Thinking disabled.`). If the level is invalid (e.g. `/thinking big`), the command is rejected with a hint and the session state is left unchanged.
|
||||||
|
|
||||||
## Application by agent
|
## Application by agent
|
||||||
- **Pi**: injects `--thinking <level>` (skipped for `off`). Other agent paths have been removed.
|
- **Embedded Pi**: the resolved level is passed to the in-process Pi agent runtime.
|
||||||
|
|
||||||
## Verbose directives (/verbose or /v)
|
## Verbose directives (/verbose or /v)
|
||||||
- Levels: `on|full` or `off` (default).
|
- Levels: `on|full` or `off` (default).
|
||||||
@@ -35,7 +35,7 @@ read_when:
|
|||||||
- When verbose is on, agents that emit structured tool results (Pi, other JSON agents) send each tool result back as its own metadata-only message, prefixed with `[🛠️ <tool-name> <arg>]` when available (path/command); the tool output itself is not forwarded.
|
- When verbose is on, agents that emit structured tool results (Pi, other JSON agents) send each tool result back as its own metadata-only message, prefixed with `[🛠️ <tool-name> <arg>]` when available (path/command); the tool output itself is not forwarded.
|
||||||
|
|
||||||
## Heartbeats
|
## Heartbeats
|
||||||
- Heartbeat probe body is `HEARTBEAT /think:high`, so it always asks for max thinking on the probe. Inline directive wins; session/global defaults are used only when no directive is present.
|
- Heartbeat probe body is `HEARTBEAT`. Inline directives in a heartbeat message apply as usual (but avoid changing session defaults from heartbeats).
|
||||||
|
|
||||||
## Web chat UI
|
## Web chat UI
|
||||||
- The web chat thinking selector mirrors the session's stored level from the inbound session store/config when the page loads.
|
- The web chat thinking selector mirrors the session's stored level from the inbound session store/config when the page loads.
|
||||||
|
|||||||
Reference in New Issue
Block a user