docs: complete channels rename sweep
This commit is contained in:
@@ -92,7 +92,7 @@ Tune the boundary via `agents.defaults.blockStreamingBreak` (`text_end` vs `mess
|
||||
Control soft block chunking with `agents.defaults.blockStreamingChunk` (defaults to
|
||||
800–1200 chars; prefers paragraph breaks, then newlines; sentences last).
|
||||
Coalesce streamed chunks with `agents.defaults.blockStreamingCoalesce` to reduce
|
||||
single-line spam (idle-based merging before send). Non-Telegram providers require
|
||||
single-line spam (idle-based merging before send). Non-Telegram channels require
|
||||
explicit `*.blockStreaming: true` to enable block replies.
|
||||
Verbose tool summaries are emitted at tool start (no debounce); Control UI
|
||||
streams tool output via agent events when available.
|
||||
|
||||
@@ -3,7 +3,7 @@ summary: "Behavior and config for WhatsApp group message handling (mentionPatter
|
||||
read_when:
|
||||
- Changing group message rules or mentions
|
||||
---
|
||||
# Group messages (web provider)
|
||||
# Group messages (WhatsApp web channel)
|
||||
|
||||
Goal: let Clawd sit in WhatsApp groups, wake up only when pinged, and keep that thread separate from the personal DM session.
|
||||
|
||||
@@ -18,27 +18,27 @@ Note: `agents.list[].groupChat.mentionPatterns` is now used by Telegram/Discord/
|
||||
- Ephemeral/view-once: we unwrap those before extracting text/mentions, so pings inside them still trigger.
|
||||
- Group system prompt: on the first turn of a group session (and whenever `/activation` changes the mode) we inject a short blurb into the system prompt like `You are replying inside the WhatsApp group "<subject>". Group members: Alice (+44...), Bob (+43...), … Activation: trigger-only … Address the specific sender noted in the message context.` If metadata isn’t available we still tell the agent it’s a group chat.
|
||||
|
||||
## Config for Clawd UK (+447700900123)
|
||||
## Config example (WhatsApp)
|
||||
Add a `groupChat` block to `~/.clawdbot/clawdbot.json` so display-name pings work even when WhatsApp strips the visual `@` in the text body:
|
||||
|
||||
```json5
|
||||
{
|
||||
"whatsapp": {
|
||||
"groups": {
|
||||
"*": { "requireMention": true }
|
||||
channels: {
|
||||
whatsapp: {
|
||||
groups: {
|
||||
"*": { requireMention: true }
|
||||
}
|
||||
}
|
||||
},
|
||||
"agents": {
|
||||
"list": [
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
"id": "main",
|
||||
"groupChat": {
|
||||
"historyLimit": 50,
|
||||
"mentionPatterns": [
|
||||
"@?clawd",
|
||||
"@?clawd\\s*uk",
|
||||
id: "main",
|
||||
groupChat: {
|
||||
historyLimit: 50,
|
||||
mentionPatterns: [
|
||||
"@?clawdbot",
|
||||
"\\+?447700900123"
|
||||
"\\+?15555550123"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,8 @@ Add a `groupChat` block to `~/.clawdbot/clawdbot.json` so display-name pings wor
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The regexes are case-insensitive; they cover `@clawd`, `@clawd uk`, `clawdbot`, and the raw number with or without `+`/spaces.
|
||||
- WhatsApp still sends canonical mentions via `mentionedJids` when someone taps the contact, so the number fallback is rarely needed but is a good safety net.
|
||||
- The regexes are case-insensitive; they cover a display-name ping like `@clawdbot` and the raw number with or without `+`/spaces.
|
||||
- WhatsApp still sends canonical mentions via `mentionedJids` when someone taps the contact, so the number fallback is rarely needed but is a useful safety net.
|
||||
|
||||
### Activation command (owner-only)
|
||||
|
||||
@@ -60,8 +60,8 @@ Use the group chat command:
|
||||
Only the owner number (from `channels.whatsapp.allowFrom`, or the bot’s own E.164 when unset) can change this. Send `/status` as a standalone message in the group to see the current activation mode.
|
||||
|
||||
## How to use
|
||||
1) Add Clawd UK (`+447700900123`) to the group.
|
||||
2) Say `@clawd …` (or `@clawd uk`, `@clawdbot`, or include the number). Only allowlisted senders can trigger it unless you set `groupPolicy: "open"`.
|
||||
1) Add your WhatsApp account (the one running Clawdbot) to the group.
|
||||
2) Say `@clawdbot …` (or include the number). Only allowlisted senders can trigger it unless you set `groupPolicy: "open"`.
|
||||
3) The agent prompt will include recent group context plus the trailing `[from: …]` marker so it can address the right person.
|
||||
4) Session-level directives (`/verbose on`, `/think high`, `/new` or `/reset`, `/compact`) apply only to that group’s session; send them as standalone messages so they register. Your personal DM session remains independent.
|
||||
|
||||
|
||||
@@ -41,17 +41,17 @@ If you want...
|
||||
| Only you can trigger in groups | `groupPolicy: "allowlist"`, `groupAllowFrom: ["+1555..."]` |
|
||||
|
||||
## Session keys
|
||||
- Group sessions use `agent:<agentId>:<provider>:group:<id>` session keys (rooms/channels use `agent:<agentId>:<provider>:channel:<id>`).
|
||||
- Group sessions use `agent:<agentId>:<channel>:group:<id>` session keys (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
|
||||
- Telegram forum topics add `:topic:<threadId>` to the group id so each topic has its own session.
|
||||
- Direct chats use the main session (or per-sender if configured).
|
||||
- Heartbeats are skipped for group sessions.
|
||||
|
||||
## Display labels
|
||||
- UI labels use `displayName` when available, formatted as `<provider>:<token>`.
|
||||
- UI labels use `displayName` when available, formatted as `<channel>:<token>`.
|
||||
- `#room` is reserved for rooms/channels; group chats use `g-<slug>` (lowercase, spaces -> `-`, keep `#@+._-`).
|
||||
|
||||
## Group policy
|
||||
Control how group/room messages are handled per provider:
|
||||
Control how group/room messages are handled per channel:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -107,7 +107,7 @@ Notes:
|
||||
|
||||
Quick mental model (evaluation order for group messages):
|
||||
1) `groupPolicy` (open/disabled/allowlist)
|
||||
2) group allowlists (`*.groups`, `*.groupAllowFrom`, provider-specific allowlist)
|
||||
2) group allowlists (`*.groups`, `*.groupAllowFrom`, channel-specific allowlist)
|
||||
3) mention gating (`requireMention`, `/activation`)
|
||||
|
||||
## Mention gating (default)
|
||||
@@ -155,7 +155,7 @@ Notes:
|
||||
- Per-agent override: `agents.list[].groupChat.mentionPatterns` (useful when multiple agents share a group).
|
||||
- Mention gating is only enforced when mention detection is possible (native mentions or `mentionPatterns` are configured).
|
||||
- Discord defaults live in `channels.discord.guilds."*"` (overridable per guild/channel).
|
||||
- Group history context is wrapped uniformly across providers; use `messages.groupChat.historyLimit` for the global default and `<provider>.historyLimit` (or `<provider>.accounts.*.historyLimit`) for overrides. Set `0` to disable.
|
||||
- Group history context is wrapped uniformly across channels; use `messages.groupChat.historyLimit` for the global default and `channels.<channel>.historyLimit` (or `channels.<channel>.accounts.*.historyLimit`) for overrides. Set `0` to disable.
|
||||
|
||||
## Group allowlists
|
||||
When `channels.whatsapp.groups`, `channels.telegram.groups`, or `channels.imessage.groups` is configured, the keys act as a group allowlist. Use `"*"` to allow all groups while still setting default mention behavior.
|
||||
|
||||
@@ -17,20 +17,20 @@ Inbound message
|
||||
-> routing/bindings -> session key
|
||||
-> queue (if a run is active)
|
||||
-> agent run (streaming + tools)
|
||||
-> outbound replies (provider limits + chunking)
|
||||
-> outbound replies (channel limits + chunking)
|
||||
```
|
||||
|
||||
Key knobs live in configuration:
|
||||
- `messages.*` for prefixes, queueing, and group behavior.
|
||||
- `agents.defaults.*` for block streaming and chunking defaults.
|
||||
- Provider overrides (`channels.whatsapp.*`, `channels.telegram.*`, etc.) for caps and streaming toggles.
|
||||
- Channel overrides (`channels.whatsapp.*`, `channels.telegram.*`, etc.) for caps and streaming toggles.
|
||||
|
||||
See [Configuration](/gateway/configuration) for full schema.
|
||||
|
||||
## Inbound dedupe
|
||||
|
||||
Providers can redeliver the same message after reconnects. Clawdbot keeps a
|
||||
short-lived cache keyed by provider/account/peer/session/message id so duplicate
|
||||
Channels can redeliver the same message after reconnects. Clawdbot keeps a
|
||||
short-lived cache keyed by channel/account/peer/session/message id so duplicate
|
||||
deliveries do not trigger another agent run.
|
||||
|
||||
## Sessions and devices
|
||||
@@ -40,7 +40,7 @@ Sessions are owned by the gateway, not by clients.
|
||||
- Groups/channels get their own session keys.
|
||||
- The session store and transcripts live on the gateway host.
|
||||
|
||||
Multiple devices/providers can map to the same session, but history is not fully
|
||||
Multiple devices/channels can map to the same session, but history is not fully
|
||||
synced back to every client. Recommendation: use one primary device for long
|
||||
conversations to avoid divergent context. The Control UI and TUI always show the
|
||||
gateway-backed session transcript, so they are the source of truth.
|
||||
@@ -50,20 +50,20 @@ Details: [Session management](/concepts/session).
|
||||
## Inbound bodies and history context
|
||||
|
||||
Clawdbot separates the **prompt body** from the **command body**:
|
||||
- `Body`: prompt text sent to the agent. This may include provider envelopes and
|
||||
- `Body`: prompt text sent to the agent. This may include channel envelopes and
|
||||
optional history wrappers.
|
||||
- `CommandBody`: raw user text for directive/command parsing.
|
||||
- `RawBody`: legacy alias for `CommandBody` (kept for compatibility).
|
||||
|
||||
When a provider supplies history, it uses a shared wrapper:
|
||||
When a channel supplies history, it uses a shared wrapper:
|
||||
- `[Chat messages since your last reply - for context]`
|
||||
- `[Current message - respond to this]`
|
||||
|
||||
Directive stripping only applies to the **current message** section so history
|
||||
remains intact. Providers that wrap history should set `CommandBody` (or
|
||||
remains intact. Channels that wrap history should set `CommandBody` (or
|
||||
`RawBody`) to the original message text and keep `Body` as the combined prompt.
|
||||
History buffers are configurable via `messages.groupChat.historyLimit` (global
|
||||
default) and per-provider overrides like `channels.slack.historyLimit` or
|
||||
default) and per-channel overrides like `channels.slack.historyLimit` or
|
||||
`channels.telegram.accounts.<id>.historyLimit` (set `0` to disable).
|
||||
|
||||
## Queueing and followups
|
||||
@@ -71,7 +71,7 @@ default) and per-provider overrides like `channels.slack.historyLimit` or
|
||||
If a run is already active, inbound messages can be queued, steered into the
|
||||
current run, or collected for a followup turn.
|
||||
|
||||
- Configure via `messages.queue` (and `messages.queue.byProvider`).
|
||||
- Configure via `messages.queue` (and `messages.queue.byChannel`).
|
||||
- Modes: `interrupt`, `steer`, `followup`, `collect`, plus backlog variants.
|
||||
|
||||
Details: [Queueing](/concepts/queue).
|
||||
@@ -79,7 +79,7 @@ Details: [Queueing](/concepts/queue).
|
||||
## Streaming, chunking, and batching
|
||||
|
||||
Block streaming sends partial replies as the model produces text blocks.
|
||||
Chunking respects provider text limits and avoids splitting fenced code.
|
||||
Chunking respects channel text limits and avoids splitting fenced code.
|
||||
|
||||
Key settings:
|
||||
- `agents.defaults.blockStreamingDefault` (`on|off`, default off)
|
||||
@@ -87,7 +87,7 @@ Key settings:
|
||||
- `agents.defaults.blockStreamingChunk` (`minChars|maxChars|breakPreference`)
|
||||
- `agents.defaults.blockStreamingCoalesce` (idle-based batching)
|
||||
- `agents.defaults.humanDelay` (human-like pause between block replies)
|
||||
- Provider overrides: `*.blockStreaming` and `*.blockStreamingCoalesce` (non-Telegram providers require explicit `*.blockStreaming: true`)
|
||||
- Channel overrides: `*.blockStreaming` and `*.blockStreamingCoalesce` (non-Telegram channels require explicit `*.blockStreaming: true`)
|
||||
|
||||
Details: [Streaming + chunking](/concepts/streaming).
|
||||
|
||||
@@ -104,6 +104,6 @@ Details: [Thinking + reasoning directives](/tools/thinking) and [Token use](/tok
|
||||
|
||||
Outbound message formatting is centralized in `messages`:
|
||||
- `messages.responsePrefix` (outbound prefix) and `channels.whatsapp.messagePrefix` (WhatsApp inbound prefix)
|
||||
- Reply threading via `replyToMode` and per-provider defaults
|
||||
- Reply threading via `replyToMode` and per-channel defaults
|
||||
|
||||
Details: [Configuration](/gateway/configuration#messages) and provider docs.
|
||||
Details: [Configuration](/gateway/configuration#messages) and channel docs.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Multi-agent routing: isolated agents, provider accounts, and bindings"
|
||||
summary: "Multi-agent routing: isolated agents, channel accounts, and bindings"
|
||||
title: Multi-Agent Routing
|
||||
read_when: "You want multiple isolated agents (workspaces + auth) in one gateway process."
|
||||
status: active
|
||||
@@ -7,7 +7,7 @@ status: active
|
||||
|
||||
# Multi-Agent Routing
|
||||
|
||||
Goal: multiple *isolated* agents (separate workspace + `agentDir` + sessions), plus multiple provider accounts (e.g. two WhatsApps) in one running Gateway. Inbound is routed to an agent via bindings.
|
||||
Goal: multiple *isolated* agents (separate workspace + `agentDir` + sessions), plus multiple channel accounts (e.g. two WhatsApps) in one running Gateway. Inbound is routed to an agent via bindings.
|
||||
|
||||
## What is “one agent”?
|
||||
|
||||
@@ -64,7 +64,7 @@ clawdbot agents list --bindings
|
||||
|
||||
With **multiple agents**, each `agentId` becomes a **fully isolated persona**:
|
||||
|
||||
- **Different phone numbers/accounts** (per provider `accountId`).
|
||||
- **Different phone numbers/accounts** (per channel `accountId`).
|
||||
- **Different personalities** (per-agent workspace files like `AGENTS.md` and `SOUL.md`).
|
||||
- **Separate auth + sessions** (no cross-talk unless explicitly enabled).
|
||||
|
||||
@@ -87,8 +87,8 @@ Example:
|
||||
]
|
||||
},
|
||||
bindings: [
|
||||
{ agentId: "alex", match: { provider: "whatsapp", peer: { kind: "dm", id: "+15551230001" } } },
|
||||
{ agentId: "mia", match: { provider: "whatsapp", peer: { kind: "dm", id: "+15551230002" } } }
|
||||
{ agentId: "alex", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230001" } } },
|
||||
{ agentId: "mia", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230002" } } }
|
||||
],
|
||||
channels: {
|
||||
whatsapp: {
|
||||
@@ -110,21 +110,21 @@ Bindings are **deterministic** and **most-specific wins**:
|
||||
1. `peer` match (exact DM/group/channel id)
|
||||
2. `guildId` (Discord)
|
||||
3. `teamId` (Slack)
|
||||
4. `accountId` match for a provider
|
||||
5. provider-level match (`accountId: "*"`)
|
||||
4. `accountId` match for a channel
|
||||
5. channel-level match (`accountId: "*"`)
|
||||
6. fallback to default agent (`agents.list[].default`, else first list entry, default: `main`)
|
||||
|
||||
## Multiple accounts / phone numbers
|
||||
|
||||
Providers that support **multiple accounts** (e.g. WhatsApp) use `accountId` to identify
|
||||
Channels that support **multiple accounts** (e.g. WhatsApp) use `accountId` to identify
|
||||
each login. Each `accountId` can be routed to a different agent, so one server can host
|
||||
multiple phone numbers without mixing sessions.
|
||||
|
||||
## Concepts
|
||||
|
||||
- `agentId`: one “brain” (workspace, per-agent auth, per-agent session store).
|
||||
- `accountId`: one provider account instance (e.g. WhatsApp account `"personal"` vs `"biz"`).
|
||||
- `binding`: routes inbound messages to an `agentId` by `(provider, accountId, peer)` and optionally guild/team ids.
|
||||
- `accountId`: one channel account instance (e.g. WhatsApp account `"personal"` vs `"biz"`).
|
||||
- `binding`: routes inbound messages to an `agentId` by `(channel, accountId, peer)` and optionally guild/team ids.
|
||||
- Direct chats collapse to `agent:<agentId>:<mainKey>` (per-agent “main”; `session.mainKey`).
|
||||
|
||||
## Example: two WhatsApps → two agents
|
||||
@@ -153,14 +153,14 @@ multiple phone numbers without mixing sessions.
|
||||
|
||||
// Deterministic routing: first match wins (most-specific first).
|
||||
bindings: [
|
||||
{ agentId: "home", match: { provider: "whatsapp", accountId: "personal" } },
|
||||
{ agentId: "work", match: { provider: "whatsapp", accountId: "biz" } },
|
||||
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
|
||||
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
|
||||
|
||||
// Optional per-peer override (example: send a specific group to work agent).
|
||||
{
|
||||
agentId: "work",
|
||||
match: {
|
||||
provider: "whatsapp",
|
||||
channel: "whatsapp",
|
||||
accountId: "personal",
|
||||
peer: { kind: "group", id: "1203630...@g.us" },
|
||||
},
|
||||
@@ -194,7 +194,7 @@ multiple phone numbers without mixing sessions.
|
||||
|
||||
## Example: WhatsApp daily chat + Telegram deep work
|
||||
|
||||
Split by provider: route WhatsApp to a fast everyday agent and Telegram to an Opus agent.
|
||||
Split by channel: route WhatsApp to a fast everyday agent and Telegram to an Opus agent.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -215,17 +215,17 @@ Split by provider: route WhatsApp to a fast everyday agent and Telegram to an Op
|
||||
]
|
||||
},
|
||||
bindings: [
|
||||
{ agentId: "chat", match: { provider: "whatsapp" } },
|
||||
{ agentId: "opus", match: { provider: "telegram" } }
|
||||
{ agentId: "chat", match: { channel: "whatsapp" } },
|
||||
{ agentId: "opus", match: { channel: "telegram" } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- If you have multiple accounts for a provider, add `accountId` to the binding (for example `{ provider: "whatsapp", accountId: "personal" }`).
|
||||
- To route a single DM/group to Opus while keeping the rest on chat, add a `match.peer` binding for that peer; peer matches always win over provider-wide rules.
|
||||
- If you have multiple accounts for a channel, add `accountId` to the binding (for example `{ channel: "whatsapp", accountId: "personal" }`).
|
||||
- To route a single DM/group to Opus while keeping the rest on chat, add a `match.peer` binding for that peer; peer matches always win over channel-wide rules.
|
||||
|
||||
## Example: same provider, one peer to Opus
|
||||
## Example: same channel, one peer to Opus
|
||||
|
||||
Keep WhatsApp on the fast agent, but route one DM to Opus:
|
||||
|
||||
@@ -238,13 +238,13 @@ Keep WhatsApp on the fast agent, but route one DM to Opus:
|
||||
]
|
||||
},
|
||||
bindings: [
|
||||
{ agentId: "opus", match: { provider: "whatsapp", peer: { kind: "dm", id: "+15551234567" } } },
|
||||
{ agentId: "chat", match: { provider: "whatsapp" } }
|
||||
{ agentId: "opus", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551234567" } } },
|
||||
{ agentId: "chat", match: { channel: "whatsapp" } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Peer bindings always win, so keep them above the provider-wide rule.
|
||||
Peer bindings always win, so keep them above the channel-wide rule.
|
||||
|
||||
## Per-Agent Sandbox and Tool Configuration
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ We now serialize command-based auto-replies (WhatsApp Web listener) through a ti
|
||||
- When verbose logging is enabled, queued commands emit a short notice if they waited more than ~2s before starting.
|
||||
- Typing indicators (`onReplyStart`) still fire immediately on enqueue so user experience is unchanged while we wait our turn.
|
||||
|
||||
## Queue modes (per provider)
|
||||
## Queue modes (per channel)
|
||||
Inbound messages can steer the current run, wait for a followup turn, or do both:
|
||||
- `steer`: inject immediately into the current run (cancels pending tool calls after the next tool boundary). If not streaming, falls back to followup.
|
||||
- `followup`: enqueue for the next agent turn after the current run ends.
|
||||
@@ -30,12 +30,12 @@ Inbound messages can steer the current run, wait for a followup turn, or do both
|
||||
Steer-backlog means you can get a followup response after the steered run, so
|
||||
streaming surfaces can look like duplicates. Prefer `collect`/`steer` if you want
|
||||
one response per inbound message.
|
||||
Send `/queue collect` as a standalone command (per-session) or set `messages.queue.byProvider.discord: "collect"`.
|
||||
Send `/queue collect` as a standalone command (per-session) or set `messages.queue.byChannel.discord: "collect"`.
|
||||
|
||||
Defaults (when unset in config):
|
||||
- All surfaces → `collect`
|
||||
|
||||
Configure globally or per provider via `messages.queue`:
|
||||
Configure globally or per channel via `messages.queue`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -45,7 +45,7 @@ Configure globally or per provider via `messages.queue`:
|
||||
debounceMs: 1000,
|
||||
cap: 20,
|
||||
drop: "summarize",
|
||||
byProvider: { discord: "collect" }
|
||||
byChannel: { discord: "collect" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ Goal: small, hard-to-misuse tool set so agents can list sessions, fetch history,
|
||||
|
||||
## Key Model
|
||||
- Main direct chat bucket is always the literal key `"main"` (resolved to the current agent’s main key).
|
||||
- Group chats use `agent:<agentId>:<provider>:group:<id>` or `agent:<agentId>:<provider>:channel:<id>` (pass the full key).
|
||||
- Group chats use `agent:<agentId>:<channel>:group:<id>` or `agent:<agentId>:<channel>:channel:<id>` (pass the full key).
|
||||
- Cron jobs use `cron:<job.id>`.
|
||||
- Hooks use `hook:<uuid>` unless explicitly set.
|
||||
- Node bridge uses `node-<nodeId>` unless explicitly set.
|
||||
@@ -40,14 +40,14 @@ Behavior:
|
||||
Row shape (JSON):
|
||||
- `key`: session key (string)
|
||||
- `kind`: `main | group | cron | hook | node | other`
|
||||
- `provider`: `whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`
|
||||
- `channel`: `whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`
|
||||
- `displayName` (group display label if available)
|
||||
- `updatedAt` (ms)
|
||||
- `sessionId`
|
||||
- `model`, `contextTokens`, `totalTokens`
|
||||
- `thinkingLevel`, `verboseLevel`, `systemSent`, `abortedLastRun`
|
||||
- `sendPolicy` (session override if set)
|
||||
- `lastProvider`, `lastTo`
|
||||
- `lastChannel`, `lastTo`
|
||||
- `transcriptPath` (best-effort path derived from store dir + sessionId)
|
||||
- `messages?` (only when `messageLimit > 0`)
|
||||
|
||||
@@ -85,17 +85,17 @@ Behavior:
|
||||
- Max turns is `session.agentToAgent.maxPingPongTurns` (0–5, default 5).
|
||||
- Once the loop ends, Clawdbot runs the **agent‑to‑agent announce step** (target agent only):
|
||||
- Reply exactly `ANNOUNCE_SKIP` to stay silent.
|
||||
- Any other reply is sent to the target provider.
|
||||
- Any other reply is sent to the target channel.
|
||||
- Announce step includes the original request + round‑1 reply + latest ping‑pong reply.
|
||||
|
||||
## Provider Field
|
||||
- For groups, `provider` is the provider recorded on the session entry.
|
||||
- For direct chats, `provider` maps from `lastProvider`.
|
||||
- For cron/hook/node, `provider` is `internal`.
|
||||
- If missing, `provider` is `unknown`.
|
||||
## Channel Field
|
||||
- For groups, `channel` is the channel recorded on the session entry.
|
||||
- For direct chats, `channel` maps from `lastChannel`.
|
||||
- For cron/hook/node, `channel` is `internal`.
|
||||
- If missing, `channel` is `unknown`.
|
||||
|
||||
## Security / Send Policy
|
||||
Policy-based blocking by provider/chat type (not per session id).
|
||||
Policy-based blocking by channel/chat type (not per session id).
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -103,7 +103,7 @@ Policy-based blocking by provider/chat type (not per session id).
|
||||
"sendPolicy": {
|
||||
"rules": [
|
||||
{
|
||||
"match": { "provider": "discord", "chatType": "group" },
|
||||
"match": { "channel": "discord", "chatType": "group" },
|
||||
"action": "deny"
|
||||
}
|
||||
],
|
||||
@@ -122,7 +122,7 @@ Enforcement points:
|
||||
- auto-reply delivery logic
|
||||
|
||||
## sessions_spawn
|
||||
Spawn a sub-agent run in an isolated session and announce the result back to the requester chat provider.
|
||||
Spawn a sub-agent run in an isolated session and announce the result back to the requester chat channel.
|
||||
|
||||
Parameters:
|
||||
- `task` (required)
|
||||
@@ -143,7 +143,7 @@ Behavior:
|
||||
- Sub-agents default to the full tool set **minus session tools** (configurable via `tools.subagents.tools`).
|
||||
- Sub-agents are not allowed to call `sessions_spawn` (no sub-agent → sub-agent spawning).
|
||||
- Always non-blocking: returns `{ status: "accepted", runId, childSessionKey }` immediately.
|
||||
- After completion, Clawdbot runs a sub-agent **announce step** and posts the result to the requester chat provider.
|
||||
- After completion, Clawdbot runs a sub-agent **announce step** and posts the result to the requester chat channel.
|
||||
- Reply exactly `ANNOUNCE_SKIP` during the announce step to stay silent.
|
||||
- Sub-agent sessions are auto-archived after `agents.defaults.subagents.archiveAfterMinutes` (default: 60).
|
||||
- Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).
|
||||
|
||||
@@ -18,7 +18,7 @@ All session state is **owned by the gateway** (the “master” Clawdbot). UI cl
|
||||
- Store file: `~/.clawdbot/agents/<agentId>/sessions/sessions.json` (per agent).
|
||||
- Transcripts: `~/.clawdbot/agents/<agentId>/sessions/<SessionId>.jsonl` (Telegram topic sessions use `.../<SessionId>-topic-<threadId>.jsonl`).
|
||||
- The store is a map `sessionKey -> { sessionId, updatedAt, ... }`. Deleting entries is safe; they are recreated on demand.
|
||||
- Group entries may include `displayName`, `provider`, `subject`, `room`, and `space` to label sessions in UIs.
|
||||
- Group entries may include `displayName`, `channel`, `subject`, `room`, and `space` to label sessions in UIs.
|
||||
- Clawdbot does **not** read legacy Pi/Tau session folders.
|
||||
|
||||
## Session pruning
|
||||
@@ -33,11 +33,11 @@ the workspace is writable. See [Memory](/concepts/memory) and
|
||||
|
||||
## Mapping transports → session keys
|
||||
- Direct chats collapse to the per-agent primary key: `agent:<agentId>:<mainKey>`.
|
||||
- Multiple phone numbers and providers can map to the same agent main key; they act as transports into one conversation.
|
||||
- Group chats isolate state: `agent:<agentId>:<provider>:group:<id>` (rooms/channels use `agent:<agentId>:<provider>:channel:<id>`).
|
||||
- Multiple phone numbers and channels can map to the same agent main key; they act as transports into one conversation.
|
||||
- Group chats isolate state: `agent:<agentId>:<channel>:group:<id>` (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
|
||||
- Telegram forum topics append `:topic:<threadId>` to the group id for isolation.
|
||||
- Legacy `group:<id>` keys are still recognized for migration.
|
||||
- Inbound contexts may still use `group:<id>`; the provider is inferred from `Provider` and normalized to the canonical `agent:<agentId>:<provider>:group:<id>` form.
|
||||
- Inbound contexts may still use `group:<id>`; the channel is inferred from `Provider` and normalized to the canonical `agent:<agentId>:<channel>:group:<id>` form.
|
||||
- Other sources:
|
||||
- Cron jobs: `cron:<job.id>`
|
||||
- Webhooks: `hook:<uuid>` (unless explicitly set by the hook)
|
||||
@@ -56,7 +56,7 @@ Block delivery for specific session types without listing individual ids.
|
||||
session: {
|
||||
sendPolicy: {
|
||||
rules: [
|
||||
{ action: "deny", match: { provider: "discord", chatType: "group" } },
|
||||
{ action: "deny", match: { channel: "discord", chatType: "group" } },
|
||||
{ action: "deny", match: { keyPrefix: "cron:" } }
|
||||
],
|
||||
default: "allow"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
summary: "Streaming + chunking behavior (block replies, draft streaming, limits)"
|
||||
read_when:
|
||||
- Explaining how streaming or chunking works on providers
|
||||
- Changing block streaming or provider chunking behavior
|
||||
- Explaining how streaming or chunking works on channels
|
||||
- Changing block streaming or channel chunking behavior
|
||||
- Debugging duplicate/early block replies or draft streaming
|
||||
---
|
||||
# Streaming + chunking
|
||||
|
||||
Clawdbot has two separate “streaming” layers:
|
||||
- **Block streaming (providers):** emit completed **blocks** as the assistant writes. These are normal provider messages (not token deltas).
|
||||
- **Block streaming (channels):** emit completed **blocks** as the assistant writes. These are normal channel messages (not token deltas).
|
||||
- **Token-ish streaming (Telegram only):** update a **draft bubble** with partial text while generating; final message is sent at the end.
|
||||
|
||||
There is **no real token streaming** to external provider messages today. Telegram draft streaming is the only partial-stream surface.
|
||||
There is **no real token streaming** to external channel messages today. Telegram draft streaming is the only partial-stream surface.
|
||||
|
||||
## Block streaming (provider messages)
|
||||
## Block streaming (channel messages)
|
||||
|
||||
Block streaming sends assistant output in coarse chunks as it becomes available.
|
||||
|
||||
@@ -24,20 +24,20 @@ Model output
|
||||
│ └─ chunker emits blocks as buffer grows
|
||||
└─ (blockStreamingBreak=message_end)
|
||||
└─ chunker flushes at message_end
|
||||
└─ provider send (block replies)
|
||||
└─ channel send (block replies)
|
||||
```
|
||||
Legend:
|
||||
- `text_delta/events`: model stream events (may be sparse for non-streaming models).
|
||||
- `chunker`: `EmbeddedBlockChunker` applying min/max bounds + break preference.
|
||||
- `provider send`: actual outbound messages (block replies).
|
||||
- `channel send`: actual outbound messages (block replies).
|
||||
|
||||
**Controls:**
|
||||
- `agents.defaults.blockStreamingDefault`: `"on"`/`"off"` (default off).
|
||||
- Provider overrides: `*.blockStreaming` (and per-account variants) to force `"on"`/`"off"` per provider.
|
||||
- Channel overrides: `*.blockStreaming` (and per-account variants) to force `"on"`/`"off"` per channel.
|
||||
- `agents.defaults.blockStreamingBreak`: `"text_end"` or `"message_end"`.
|
||||
- `agents.defaults.blockStreamingChunk`: `{ minChars, maxChars, breakPreference? }`.
|
||||
- `agents.defaults.blockStreamingCoalesce`: `{ minChars?, maxChars?, idleMs? }` (merge streamed blocks before send).
|
||||
- Provider hard cap: `*.textChunkLimit` (e.g., `channels.whatsapp.textChunkLimit`).
|
||||
- Channel hard cap: `*.textChunkLimit` (e.g., `channels.whatsapp.textChunkLimit`).
|
||||
- Discord soft cap: `channels.discord.maxLinesPerMessage` (default 17) splits tall replies to avoid UI clipping.
|
||||
|
||||
**Boundary semantics:**
|
||||
@@ -54,7 +54,7 @@ Block chunking is implemented by `EmbeddedBlockChunker`:
|
||||
- **Break preference:** `paragraph` → `newline` → `sentence` → `whitespace` → hard break.
|
||||
- **Code fences:** never split inside fences; when forced at `maxChars`, close + reopen the fence to keep Markdown valid.
|
||||
|
||||
`maxChars` is clamped to the provider `textChunkLimit`, so you can’t exceed per-provider caps.
|
||||
`maxChars` is clamped to the channel `textChunkLimit`, so you can’t exceed per-channel caps.
|
||||
|
||||
## Coalescing (merge streamed blocks)
|
||||
|
||||
@@ -68,7 +68,7 @@ progressive output.
|
||||
(final flush always sends remaining text).
|
||||
- Joiner is derived from `blockStreamingChunk.breakPreference`
|
||||
(`paragraph` → `\n\n`, `newline` → `\n`, `sentence` → space).
|
||||
- Provider overrides are available via `*.blockStreamingCoalesce` (including per-account configs).
|
||||
- Channel overrides are available via `*.blockStreamingCoalesce` (including per-account configs).
|
||||
- Default coalesce `minChars` is bumped to 1500 for Signal/Slack/Discord unless overridden.
|
||||
|
||||
## Human-like pacing between blocks
|
||||
@@ -84,11 +84,11 @@ more natural.
|
||||
## “Stream chunks or everything”
|
||||
|
||||
This maps to:
|
||||
- **Stream chunks:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"` (emit as you go). Non-Telegram providers also need `*.blockStreaming: true`.
|
||||
- **Stream chunks:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"` (emit as you go). Non-Telegram channels also need `*.blockStreaming: true`.
|
||||
- **Stream everything at end:** `blockStreamingBreak: "message_end"` (flush once, possibly multiple chunks if very long).
|
||||
- **No block streaming:** `blockStreamingDefault: "off"` (only final reply).
|
||||
|
||||
**Provider note:** For non-Telegram providers, block streaming is **off unless**
|
||||
**Channel note:** For non-Telegram channels, block streaming is **off unless**
|
||||
`*.blockStreaming` is explicitly set to `true`. Telegram can stream drafts
|
||||
(`channels.telegram.streamMode`) without block replies.
|
||||
|
||||
@@ -97,14 +97,14 @@ Config location reminder: the `blockStreaming*` defaults live under
|
||||
|
||||
## Telegram draft streaming (token-ish)
|
||||
|
||||
Telegram is the only provider with draft streaming:
|
||||
Telegram is the only channel with draft streaming:
|
||||
- Uses Bot API `sendMessageDraft` in **private chats with topics**.
|
||||
- `channels.telegram.streamMode: "partial" | "block" | "off"`.
|
||||
- `partial`: draft updates with the latest stream text.
|
||||
- `block`: draft updates in chunked blocks (same chunker rules).
|
||||
- `off`: no draft streaming.
|
||||
- Draft chunk config (only for `streamMode: "block"`): `channels.telegram.draftChunk` (defaults: `minChars: 200`, `maxChars: 800`).
|
||||
- Draft streaming is separate from block streaming; block replies are off by default and only enabled by `*.blockStreaming: true` on non-Telegram providers.
|
||||
- Draft streaming is separate from block streaming; block replies are off by default and only enabled by `*.blockStreaming: true` on non-Telegram channels.
|
||||
- Final reply is still a normal message.
|
||||
- `/reasoning stream` writes reasoning into the draft bubble (Telegram only).
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ read_when:
|
||||
---
|
||||
# Typing indicators
|
||||
|
||||
Typing indicators are sent to the chat provider while a run is active. Use
|
||||
Typing indicators are sent to the chat channel while a run is active. Use
|
||||
`agents.defaults.typingMode` to control **when** typing starts and `typingIntervalSeconds`
|
||||
to control **how often** it refreshes.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user