fix: stabilize live probes and docs
This commit is contained in:
@@ -72,7 +72,7 @@ Payload:
|
||||
- `wakeMode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check.
|
||||
- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging provider. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped.
|
||||
- `provider` optional (string): The messaging service for delivery. One of: `last`, `whatsapp`, `telegram`, `discord`, `slack`, `signal`, `imessage`, `msteams`. Defaults to `last`.
|
||||
- `to` optional (string): The recipient identifier for the provider (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack). Defaults to the last recipient in the main session.
|
||||
- `to` optional (string): The recipient identifier for the provider (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack, conversation ID for MS Teams). Defaults to the last recipient in the main session.
|
||||
- `model` optional (string): Model override (e.g., `anthropic/claude-3-5-sonnet` or an alias). Must be in the allowed model list if restricted.
|
||||
- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`).
|
||||
- `timeoutSeconds` optional (number): Maximum duration for the agent run in seconds.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
summary: "Broadcast a WhatsApp message to multiple agents"
|
||||
read_when:
|
||||
- Configuring broadcast groups
|
||||
- Debugging multi-agent replies in WhatsApp
|
||||
status: experimental
|
||||
---
|
||||
|
||||
# Broadcast Groups
|
||||
|
||||
**Status:** Experimental
|
||||
@@ -5,7 +13,9 @@
|
||||
|
||||
## Overview
|
||||
|
||||
Broadcast Groups enable multiple agents to process and respond to the same message simultaneously. This allows you to create specialized agent teams that work together in a single WhatsApp group, DM, or channel - all using one phone number.
|
||||
Broadcast Groups enable multiple agents to process and respond to the same message simultaneously. This allows you to create specialized agent teams that work together in a single WhatsApp group or DM — all using one phone number.
|
||||
|
||||
Current scope: **WhatsApp only** (web provider).
|
||||
|
||||
Broadcast groups are evaluated after provider allowlists and group activation rules. In WhatsApp groups, this means broadcasts happen when Clawdbot would normally reply (for example: on mention, depending on your group settings).
|
||||
|
||||
@@ -54,7 +64,9 @@ Agents:
|
||||
|
||||
### Basic Setup
|
||||
|
||||
Add a top-level `broadcast` section (next to `bindings`):
|
||||
Add a top-level `broadcast` section (next to `bindings`). Keys are WhatsApp peer ids:
|
||||
- group chats: group JID (e.g. `120363403215116621@g.us`)
|
||||
- DMs: E.164 phone number (e.g. `+15551234567`)
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ Notes:
|
||||
- `--password <password>`: password override (also sets `CLAWDBOT_GATEWAY_PASSWORD` for the process).
|
||||
- `--tailscale <off|serve|funnel>`: expose the Gateway via Tailscale.
|
||||
- `--tailscale-reset-on-exit`: reset Tailscale serve/funnel config on shutdown.
|
||||
- `--allow-unconfigured`: allow gateway start without `gateway.mode=local` in config.
|
||||
- `--dev`: create a dev config + workspace if missing (skips BOOTSTRAP.md).
|
||||
- `--reset`: reset dev config + credentials + sessions + workspace (requires `--dev`).
|
||||
- `--force`: kill any existing listener on the selected port before starting.
|
||||
|
||||
@@ -52,6 +52,7 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
providers
|
||||
list
|
||||
status
|
||||
logs
|
||||
add
|
||||
remove
|
||||
login
|
||||
@@ -74,6 +75,14 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
health
|
||||
status
|
||||
discover
|
||||
daemon
|
||||
status
|
||||
install
|
||||
uninstall
|
||||
start
|
||||
stop
|
||||
restart
|
||||
logs
|
||||
models
|
||||
list
|
||||
status
|
||||
@@ -83,6 +92,12 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
fallbacks list|add|remove|clear
|
||||
image-fallbacks list|add|remove|clear
|
||||
scan
|
||||
auth add|setup-token|paste-token
|
||||
auth order get|set|clear
|
||||
sandbox
|
||||
list
|
||||
recreate
|
||||
explain
|
||||
wake
|
||||
cron
|
||||
status
|
||||
@@ -106,7 +121,8 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
run
|
||||
notify
|
||||
camera list|snap|clip
|
||||
canvas snapshot
|
||||
canvas snapshot|present|hide|navigate|eval
|
||||
canvas a2ui push|reset
|
||||
screen record
|
||||
location get
|
||||
browser
|
||||
@@ -180,6 +196,7 @@ Options:
|
||||
- `--workspace <dir>`
|
||||
- `--non-interactive`
|
||||
- `--mode <local|remote>`
|
||||
- `--flow <quickstart|advanced>`
|
||||
- `--auth-choice <setup-token|claude-cli|token|openai-codex|openai-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-cloud|minimax-api|minimax|opencode-zen|skip>`
|
||||
- `--token-provider <id>` (non-interactive; used with `--auth-choice token`)
|
||||
- `--token <token>` (non-interactive; used with `--auth-choice token`)
|
||||
@@ -201,9 +218,12 @@ Options:
|
||||
- `--tailscale <off|serve|funnel>`
|
||||
- `--tailscale-reset-on-exit`
|
||||
- `--install-daemon`
|
||||
- `--no-install-daemon` (alias: `--skip-daemon`)
|
||||
- `--daemon-runtime <node|bun>`
|
||||
- `--skip-providers`
|
||||
- `--skip-skills`
|
||||
- `--skip-health`
|
||||
- `--skip-ui`
|
||||
- `--node-manager <npm|pnpm|bun>`
|
||||
- `--json`
|
||||
|
||||
@@ -222,19 +242,20 @@ Options:
|
||||
## Provider helpers
|
||||
|
||||
### `providers`
|
||||
Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage).
|
||||
Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage/MS Teams).
|
||||
|
||||
Subcommands:
|
||||
- `providers list`: show configured chat providers and auth profiles (Claude Code + Codex CLI OAuth sync included).
|
||||
- `providers status`: check gateway reachability and provider health (`--probe` runs extra checks; use `clawdbot health` or `clawdbot status --deep` for gateway health probes).
|
||||
- Tip: `providers status` prints warnings with suggested fixes when it can detect common misconfigurations (then points you to `clawdbot doctor`).
|
||||
- `providers logs`: show recent provider logs from the gateway log file.
|
||||
- `providers add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode.
|
||||
- `providers remove`: disable by default; pass `--delete` to remove config entries without prompts.
|
||||
- `providers login`: interactive provider login (WhatsApp Web only).
|
||||
- `providers logout`: log out of a provider session (WhatsApp Web only).
|
||||
|
||||
Common options:
|
||||
- `--provider <name>`: `whatsapp|telegram|discord|slack|signal|imessage`
|
||||
- `--provider <name>`: `whatsapp|telegram|discord|slack|signal|imessage|msteams`
|
||||
- `--account <id>`: provider account id (default `default`)
|
||||
- `--name <label>`: display name for the account
|
||||
|
||||
@@ -251,6 +272,11 @@ Common options:
|
||||
- `--no-usage`: skip provider usage/quota snapshots (OAuth/API-backed only).
|
||||
- `--json`: output JSON (includes usage unless `--no-usage` is set).
|
||||
|
||||
`providers logs` options:
|
||||
- `--provider <name|all>` (default `all`)
|
||||
- `--lines <n>` (default `200`)
|
||||
- `--json`
|
||||
|
||||
OAuth sync sources:
|
||||
- Claude Code → `anthropic:claude-cli`
|
||||
- macOS: Keychain item "Claude Code-credentials" (choose "Always Allow" to avoid launchd prompts)
|
||||
@@ -336,7 +362,7 @@ Options:
|
||||
- `--session-id <id>`
|
||||
- `--thinking <off|minimal|low|medium|high>`
|
||||
- `--verbose <on|off>`
|
||||
- `--provider <whatsapp|telegram|discord|slack|signal|imessage>`
|
||||
- `--provider <whatsapp|telegram|discord|slack|signal|imessage|msteams>`
|
||||
- `--local`
|
||||
- `--deliver`
|
||||
- `--json`
|
||||
@@ -377,10 +403,12 @@ Show linked session health and recent recipients.
|
||||
|
||||
Options:
|
||||
- `--json`
|
||||
- `--all` (full diagnosis; read-only, pasteable)
|
||||
- `--deep` (probe providers)
|
||||
- `--usage` (show provider usage/quota)
|
||||
- `--timeout <ms>`
|
||||
- `--verbose`
|
||||
- `--debug` (alias for `--verbose`)
|
||||
|
||||
### Usage tracking
|
||||
Clawdbot can surface provider usage/quota when OAuth/API creds are available.
|
||||
@@ -431,8 +459,11 @@ Options:
|
||||
- `--reset` (reset dev config + credentials + sessions + workspace)
|
||||
- `--force` (kill existing listener on port)
|
||||
- `--verbose`
|
||||
- `--claude-cli-logs`
|
||||
- `--ws-log <auto|full|compact>`
|
||||
- `--compact` (alias for `--ws-log compact`)
|
||||
- `--raw-stream`
|
||||
- `--raw-stream-path <path>`
|
||||
|
||||
### `daemon`
|
||||
Manage the Gateway service (launchd/systemd/schtasks).
|
||||
@@ -476,6 +507,7 @@ Subcommands:
|
||||
- `gateway call <method> [--params <json>]`
|
||||
- `gateway health`
|
||||
- `gateway status`
|
||||
- `gateway discover`
|
||||
|
||||
Common RPCs:
|
||||
- `config.apply` (validate + write config + restart + wake)
|
||||
@@ -495,6 +527,10 @@ clawdbot models status
|
||||
### `models` (root)
|
||||
`clawdbot models` is an alias for `models status`.
|
||||
|
||||
Root options:
|
||||
- `--status-json` (alias for `models status --json`)
|
||||
- `--status-plain` (alias for `models status --plain`)
|
||||
|
||||
### `models list`
|
||||
Options:
|
||||
- `--all`
|
||||
@@ -545,12 +581,25 @@ Options:
|
||||
- `--max-candidates <n>`
|
||||
- `--timeout <ms>`
|
||||
- `--concurrency <n>`
|
||||
- `--no-probe`
|
||||
- `--yes`
|
||||
- `--no-input`
|
||||
- `--set-default`
|
||||
- `--set-image`
|
||||
- `--json`
|
||||
|
||||
### `models auth add|setup-token|paste-token`
|
||||
Options:
|
||||
- `add`: interactive auth helper
|
||||
- `setup-token`: `--provider <name>` (default `anthropic`), `--yes`
|
||||
- `paste-token`: `--provider <name>`, `--profile-id <id>`, `--expires-in <duration>`
|
||||
|
||||
### `models auth order get|set|clear`
|
||||
Options:
|
||||
- `get`: `--provider <name>`, `--agent <id>`, `--json`
|
||||
- `set`: `--provider <name>`, `--agent <id>`, `<profileIds...>`
|
||||
- `clear`: `--provider <name>`, `--agent <id>`
|
||||
|
||||
## Cron + wake
|
||||
|
||||
### `wake`
|
||||
|
||||
@@ -24,8 +24,8 @@ Provider selection:
|
||||
Target formats (`--to`):
|
||||
- WhatsApp: E.164 or group JID
|
||||
- Telegram: chat id or `@username`
|
||||
- Discord/Slack: `channel:<id>` or `user:<id>` (raw id is ambiguous for Discord)
|
||||
- Signal: E.164, `group:<id>`, or `signal:+E.164`
|
||||
- Discord/Slack: `channel:<id>` or `user:<id>` (raw `channelId` is also accepted)
|
||||
- Signal: `+E.164`, `group:<id>`, `signal:+E.164`, `signal:group:<id>`, or `username:<name>`/`u:<name>`
|
||||
- iMessage: handle or `chat_id:<id>`
|
||||
- MS Teams: conversation id (`19:...@thread.tacv2`) or `conversation:<id>` or `user:<aad-object-id>`
|
||||
|
||||
@@ -42,61 +42,79 @@ Target formats (`--to`):
|
||||
### Core
|
||||
|
||||
- `send`
|
||||
- Providers: WhatsApp/Telegram/Discord/Slack/Signal/iMessage/MS Teams
|
||||
- Required: `--to`, `--message`
|
||||
- Optional: `--media`, `--reply-to`, `--thread-id`, `--gif-playback`
|
||||
- Telegram only: `--buttons-json` (requires `"inlineButtons"` in `telegram.capabilities` or `telegram.accounts.<id>.capabilities`)
|
||||
- Telegram only: `--thread-id` (forum topic id)
|
||||
- WhatsApp only: `--gif-playback`
|
||||
|
||||
- `poll`
|
||||
- Providers: WhatsApp/Discord/MS Teams
|
||||
- Required: `--to`, `--poll-question`, `--poll-option` (repeat)
|
||||
- Optional: `--poll-multi`, `--poll-duration-hours`, `--message`
|
||||
- Discord only: `--poll-duration-hours`
|
||||
|
||||
- `react`
|
||||
- Providers: Discord/Slack/WhatsApp
|
||||
- Required: `--to`, `--message-id`
|
||||
- Optional: `--emoji`, `--remove`, `--participant`, `--from-me`, `--channel-id`
|
||||
- WhatsApp only: `--participant`, `--from-me`
|
||||
|
||||
- `reactions`
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`, `--message-id`
|
||||
- Optional: `--limit`, `--channel-id`
|
||||
|
||||
- `read`
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`
|
||||
- Optional: `--limit`, `--before`, `--after`, `--around`, `--channel-id`
|
||||
|
||||
- `edit`
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`, `--message-id`, `--message`
|
||||
- Optional: `--channel-id`
|
||||
|
||||
- `delete`
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`, `--message-id`
|
||||
- Optional: `--channel-id`
|
||||
|
||||
- `pin` / `unpin`
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`, `--message-id`
|
||||
- Optional: `--channel-id`
|
||||
|
||||
- `pins` (list)
|
||||
- Providers: Discord/Slack
|
||||
- Required: `--to`
|
||||
- Optional: `--channel-id`
|
||||
|
||||
- `permissions`
|
||||
- Providers: Discord
|
||||
- Required: `--to`
|
||||
- Optional: `--channel-id`
|
||||
|
||||
- `search`
|
||||
- Providers: Discord
|
||||
- Required: `--guild-id`, `--query`
|
||||
- Optional: `--channel-id`, `--channel-ids` (repeat), `--author-id`, `--author-ids` (repeat), `--limit`
|
||||
|
||||
### Threads
|
||||
|
||||
- `thread create`
|
||||
- Providers: Discord
|
||||
- Required: `--thread-name`, `--to` (channel id) or `--channel-id`
|
||||
- Optional: `--message-id`, `--auto-archive-min`
|
||||
|
||||
- `thread list`
|
||||
- Providers: Discord
|
||||
- Required: `--guild-id`
|
||||
- Optional: `--channel-id`, `--include-archived`, `--before`, `--limit`
|
||||
|
||||
- `thread reply`
|
||||
- Providers: Discord
|
||||
- Required: `--to` (thread id), `--message`
|
||||
- Optional: `--media`, `--reply-to`
|
||||
|
||||
@@ -104,18 +122,22 @@ Target formats (`--to`):
|
||||
|
||||
- `emoji list`
|
||||
- Discord: `--guild-id`
|
||||
- Slack: no extra flags
|
||||
|
||||
- `emoji upload`
|
||||
- Providers: Discord
|
||||
- Required: `--guild-id`, `--emoji-name`, `--media`
|
||||
- Optional: `--role-ids` (repeat)
|
||||
|
||||
### Stickers
|
||||
|
||||
- `sticker send`
|
||||
- Providers: Discord
|
||||
- Required: `--to`, `--sticker-id` (repeat)
|
||||
- Optional: `--message`
|
||||
|
||||
- `sticker upload`
|
||||
- Providers: Discord
|
||||
- Required: `--guild-id`, `--sticker-name`, `--sticker-desc`, `--sticker-tags`, `--media`
|
||||
|
||||
### Roles / Channels / Members / Voice
|
||||
|
||||
@@ -107,22 +107,24 @@ clawdbot sandbox recreate --agent alfred
|
||||
|
||||
## Configuration
|
||||
|
||||
Sandbox settings are in `clawdbot.config.json`:
|
||||
Sandbox settings live in `~/.clawdbot/clawdbot.json` under `agents.defaults.sandbox` (per-agent overrides go in `agents.list[].sandbox`):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"agent": {
|
||||
"sandbox": {
|
||||
"mode": "all", // off, non-main, all
|
||||
"scope": "agent", // session, agent, shared
|
||||
"docker": {
|
||||
"image": "clawdbot-sandbox:bookworm-slim",
|
||||
"containerPrefix": "clawdbot-sbx-"
|
||||
// ... more Docker options
|
||||
},
|
||||
"prune": {
|
||||
"idleHours": 24, // Auto-prune after 24h idle
|
||||
"maxAgeDays": 7 // Auto-prune after 7 days
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"sandbox": {
|
||||
"mode": "all", // off, non-main, all
|
||||
"scope": "agent", // session, agent, shared
|
||||
"docker": {
|
||||
"image": "clawdbot-sandbox:bookworm-slim",
|
||||
"containerPrefix": "clawdbot-sbx-"
|
||||
// ... more Docker options
|
||||
},
|
||||
"prune": {
|
||||
"idleHours": 24, // Auto-prune after 24h idle
|
||||
"maxAgeDays": 7 // Auto-prune after 7 days
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2018,7 +2018,7 @@ Mapping notes:
|
||||
- Templates like `{{messages[0].subject}}` read from the payload.
|
||||
- `transform` can point to a JS/TS module that returns a hook action.
|
||||
- `deliver: true` sends the final reply to a provider; `provider` defaults to `last` (falls back to WhatsApp).
|
||||
- If there is no prior delivery route, set `provider` + `to` explicitly (required for Telegram/Discord/Slack/Signal/iMessage).
|
||||
- If there is no prior delivery route, set `provider` + `to` explicitly (required for Telegram/Discord/Slack/Signal/iMessage/MS Teams).
|
||||
- `model` overrides the LLM for this hook run (`provider/model` or alias; must be allowed if `agents.defaults.models` is set).
|
||||
|
||||
Gmail helper config (used by `clawdbot hooks gmail setup` / `run`):
|
||||
@@ -2171,7 +2171,7 @@ Template placeholders are expanded in `tools.audio.transcription.args` (and any
|
||||
| `{{GroupMembers}}` | Group members preview (best effort) |
|
||||
| `{{SenderName}}` | Sender display name (best effort) |
|
||||
| `{{SenderE164}}` | Sender phone number (best effort) |
|
||||
| `{{Provider}}` | Provider hint (whatsapp|telegram|discord|imessage|webchat|…) |
|
||||
| `{{Provider}}` | Provider hint (whatsapp|telegram|discord|slack|signal|imessage|msteams|webchat|…) |
|
||||
|
||||
## Cron (Gateway scheduler)
|
||||
|
||||
|
||||
@@ -25,4 +25,6 @@ Text is supported everywhere; media and reactions vary by provider.
|
||||
- Providers can run simultaneously; configure multiple and Clawdbot will route per chat.
|
||||
- Group behavior varies by provider; see [Groups](/concepts/groups).
|
||||
- DM pairing and allowlists are enforced for safety; see [Security](/gateway/security).
|
||||
- Telegram internals: [grammY notes](/providers/grammy).
|
||||
- Troubleshooting: [Provider troubleshooting](/providers/troubleshooting).
|
||||
- Model providers are documented separately; see [Model Providers](/providers/models).
|
||||
|
||||
@@ -133,7 +133,7 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- Optional tool-calling stress:
|
||||
- `CLAWDBOT_LIVE_GATEWAY_TOOL_PROBE=1` enables an extra “bash writes file → read reads it back → echo nonce” check.
|
||||
- This is specifically meant to catch tool-calling compatibility issues across providers (formatting, history replay, tool_result pairing, etc.).
|
||||
- Optional image send smoke:
|
||||
- Optional image send smoke:
|
||||
- `CLAWDBOT_LIVE_GATEWAY_IMAGE_PROBE=1` sends a real image attachment through the gateway agent pipeline (multimodal message) and asserts the model can read back a per-run code from the image.
|
||||
- Flow (high level):
|
||||
- Test generates a tiny PNG with “CAT” + random code (`src/gateway/live-image-probe.ts`)
|
||||
@@ -142,6 +142,13 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- Embedded agent forwards a multimodal user message to the model
|
||||
- Assertion: reply contains `cat` + the code (OCR tolerance: minor mistakes allowed)
|
||||
|
||||
Tip: to see what you can test on your machine (and the exact `provider/model` ids), run:
|
||||
|
||||
```bash
|
||||
pnpm clawdbot models list
|
||||
pnpm clawdbot models list --json
|
||||
```
|
||||
|
||||
## Live: Anthropic setup-token smoke
|
||||
|
||||
- Test: `src/agents/anthropic.setup-token.live.test.ts`
|
||||
@@ -225,7 +232,7 @@ This is the “common models” run we expect to keep working:
|
||||
- OpenAI (non-Codex): `openai/gpt-5.2` (optional: `openai/gpt-5.1`)
|
||||
- OpenAI Codex: `openai-codex/gpt-5.2` (optional: `openai-codex/gpt-5.2-codex`)
|
||||
- Anthropic: `anthropic/claude-opus-4-5` (or `anthropic/claude-sonnet-4-5`)
|
||||
- Google (Gemini API): `google/gemini-3-pro-preview` and `google/gemini-3-flash-preview`
|
||||
- Google (Gemini API): `google/gemini-3-pro-preview` and `google/gemini-3-flash-preview` (avoid older Gemini 2.x models)
|
||||
- Google (Antigravity): `google-antigravity/claude-opus-4-5-thinking` and `google-antigravity/gemini-3-flash`
|
||||
- Z.AI (GLM): `zai/glm-4.7`
|
||||
- MiniMax: `minimax/minimax-m2.1`
|
||||
|
||||
@@ -37,7 +37,7 @@ clawdbot agent --to +15555550123 --message "Summon reply" --deliver
|
||||
|
||||
- `--local`: run locally (requires provider keys in your shell)
|
||||
- `--deliver`: send the reply to the chosen provider (requires `--to`)
|
||||
- `--provider`: `whatsapp|telegram|discord|slack|signal|imessage` (default: `whatsapp`)
|
||||
- `--provider`: `whatsapp|telegram|discord|slack|signal|imessage|msteams` (default: `whatsapp`)
|
||||
- `--thinking <off|minimal|low|medium|high>`: persist thinking level
|
||||
- `--verbose <on|off>`: persist verbose level
|
||||
- `--timeout <seconds>`: override agent timeout
|
||||
|
||||
@@ -145,11 +145,11 @@ Notes:
|
||||
- Uses the image model directly (independent of the main chat model).
|
||||
|
||||
### `message`
|
||||
Send messages and provider actions across Discord/Slack/Telegram/WhatsApp/Signal/iMessage.
|
||||
Send messages and provider actions across Discord/Slack/Telegram/WhatsApp/Signal/iMessage/MS Teams.
|
||||
|
||||
Core actions:
|
||||
- `send` (text + optional media)
|
||||
- `poll` (WhatsApp/Discord polls)
|
||||
- `poll` (WhatsApp/Discord/MS Teams polls)
|
||||
- `react` / `reactions` / `read` / `edit` / `delete`
|
||||
- `pin` / `unpin` / `list-pins`
|
||||
- `permissions`
|
||||
@@ -166,7 +166,7 @@ Core actions:
|
||||
|
||||
Notes:
|
||||
- `send` routes WhatsApp via the Gateway; other providers go direct.
|
||||
- `poll` uses the Gateway for WhatsApp and direct Discord API for Discord.
|
||||
- `poll` uses the Gateway for WhatsApp and MS Teams; Discord polls go direct.
|
||||
|
||||
### `cron`
|
||||
Manage Gateway cron jobs and wakeups.
|
||||
|
||||
@@ -18,39 +18,6 @@ diff --git a/dist/providers/openai-codex-responses.js b/dist/providers/openai-co
|
||||
index 188a8294f26fe1bfe3fb298a7f58e4d8eaf2a529..a3aeb6a7ff53bc4f7f44362adb950b2c55455332 100644
|
||||
--- a/dist/providers/openai-codex-responses.js
|
||||
+++ b/dist/providers/openai-codex-responses.js
|
||||
@@ -433,9 +433,15 @@ function convertMessages(model, context) {
|
||||
}
|
||||
else if (msg.role === "assistant") {
|
||||
const output = [];
|
||||
+ // OpenAI Responses rejects `reasoning` items that are not followed by a `message`.
|
||||
+ // Tool-call-only turns (thinking + function_call) are valid assistant turns, but
|
||||
+ // their stored reasoning items must not be replayed as standalone `reasoning` input.
|
||||
+ const hasTextBlock = msg.content.some((b) => b.type === "text");
|
||||
for (const block of msg.content) {
|
||||
if (block.type === "thinking" && msg.stopReason !== "error") {
|
||||
if (block.thinkingSignature) {
|
||||
+ if (!hasTextBlock)
|
||||
+ continue;
|
||||
const reasoningItem = JSON.parse(block.thinkingSignature);
|
||||
output.push(reasoningItem);
|
||||
}
|
||||
@@ -470,6 +476,16 @@ function convertMessages(model, context) {
|
||||
}
|
||||
if (output.length === 0)
|
||||
continue;
|
||||
+ // OpenAI rejects standalone reasoning items when replaying a tool-only turn.
|
||||
+ // Only submit reasoning items when we also submit an assistant message item.
|
||||
+ // Repro: pnpm test src/agents/openai-responses.reasoning-replay.test.ts
|
||||
+ const hasMessage = output.some((item) => item?.type === "message");
|
||||
+ if (!hasMessage) {
|
||||
+ for (let i = output.length - 1; i >= 0; i -= 1) {
|
||||
+ if (output[i]?.type === "reasoning")
|
||||
+ output.splice(i, 1);
|
||||
+ }
|
||||
+ }
|
||||
messages.push(...output);
|
||||
}
|
||||
else if (msg.role === "toolResult") {
|
||||
@@ -515,7 +531,7 @@ function convertTools(tools) {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
@@ -60,24 +27,3 @@ index 188a8294f26fe1bfe3fb298a7f58e4d8eaf2a529..a3aeb6a7ff53bc4f7f44362adb950b2c
|
||||
}));
|
||||
}
|
||||
function mapStopReason(status) {
|
||||
diff --git a/dist/providers/openai-responses.js b/dist/providers/openai-responses.js
|
||||
index f07085c64390b211340d6a826b28ea9c2e77302f..523ed38a5a6151d6ff08dd89120315e7aaaf19b6 100644
|
||||
--- a/dist/providers/openai-responses.js
|
||||
+++ b/dist/providers/openai-responses.js
|
||||
@@ -436,6 +436,16 @@ function convertMessages(model, context) {
|
||||
}
|
||||
if (output.length === 0)
|
||||
continue;
|
||||
+ // OpenAI rejects standalone reasoning items when replaying a tool-only turn.
|
||||
+ // Only submit reasoning items when we also submit an assistant message item.
|
||||
+ // Repro: pnpm test src/agents/openai-responses.reasoning-replay.test.ts
|
||||
+ const hasMessage = output.some((item) => item?.type === "message");
|
||||
+ if (!hasMessage) {
|
||||
+ for (let i = output.length - 1; i >= 0; i -= 1) {
|
||||
+ if (output[i]?.type === "reasoning")
|
||||
+ output.splice(i, 1);
|
||||
+ }
|
||||
+ }
|
||||
messages.push(...output);
|
||||
}
|
||||
else if (msg.role === "toolResult") {
|
||||
|
||||
@@ -48,6 +48,16 @@ function isGoogleModelNotFoundError(err: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isModelNotFoundErrorMessage(raw: string): boolean {
|
||||
const msg = raw.trim();
|
||||
if (!msg) return false;
|
||||
if (/\b404\b/.test(msg) && /not[_-]?found/i.test(msg)) return true;
|
||||
if (/not_found_error/i.test(msg)) return true;
|
||||
if (/model:\s*[a-z0-9._-]+/i.test(msg) && /not[_-]?found/i.test(msg))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
describeLive("live models (profile keys)", () => {
|
||||
it(
|
||||
"completes across configured models",
|
||||
@@ -187,6 +197,15 @@ describeLive("live models (profile keys)", () => {
|
||||
},
|
||||
);
|
||||
|
||||
if (res.stopReason === "error") {
|
||||
const msg = res.errorMessage ?? "";
|
||||
if (ALL_MODELS && isModelNotFoundErrorMessage(msg)) {
|
||||
skipped.push({ model: id, reason: msg });
|
||||
continue;
|
||||
}
|
||||
throw new Error(msg || "model returned error with no message");
|
||||
}
|
||||
|
||||
const text = res.content
|
||||
.filter((block) => block.type === "text")
|
||||
.map((block) => block.text.trim())
|
||||
|
||||
@@ -52,7 +52,7 @@ function installFailingFetchCapture() {
|
||||
}
|
||||
|
||||
describe("openai-responses reasoning replay", () => {
|
||||
it("handles tool-call-only turns without requiring reasoning replay", async () => {
|
||||
it("replays reasoning for tool-call-only turns (required by OpenAI)", async () => {
|
||||
const cap = installFailingFetchCapture();
|
||||
try {
|
||||
const model = buildModel();
|
||||
@@ -141,11 +141,11 @@ describe("openai-responses reasoning replay", () => {
|
||||
)
|
||||
.filter((t): t is string => typeof t === "string");
|
||||
|
||||
expect(types).toContain("reasoning");
|
||||
expect(types).toContain("function_call");
|
||||
const reasoningIndex = types.indexOf("reasoning");
|
||||
if (reasoningIndex !== -1) {
|
||||
expect(reasoningIndex).toBeLessThan(types.indexOf("function_call"));
|
||||
}
|
||||
expect(types.indexOf("reasoning")).toBeLessThan(
|
||||
types.indexOf("function_call"),
|
||||
);
|
||||
} finally {
|
||||
cap.restore();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user