Include DingTalk in daemon supervised channel detection so the listener starts in daemon mode.
Handle CALLBACK stream frames, subscribe to bot message topic, and improve session webhook routing for private/group replies.
Add regression tests for supervised-channel detection and DingTalk payload/chat-id parsing.
Strip XML-style tool call tags from messages before sending to Telegram to prevent Markdown parsing failures (status 400).
Fixes#503
Co-Authored-By: ayush-thakur02 <ayush.th2002@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactor the Channel trait to accept a SendMessage struct instead of
separate message and recipient string parameters. This enables passing
additional metadata like email subjects.
Changes:
- Add SendMessage struct with content, recipient, and optional subject
- Update Channel::send() signature to accept &SendMessage
- Update all 12 channel implementations
- Update call sites in channels/mod.rs and gateway/mod.rs
Subject field usage:
- Email: uses subject for email subject line
- DingTalk: uses subject as markdown message title
- All others: ignore subject (no native platform support)
Implement QQ Official messaging channel using OAuth2 authentication
with Discord-like WebSocket gateway protocol for events.
- Add QQChannel with send/listen/health_check support
- Add QQConfig (app_id, app_secret, allowed_users)
- OAuth2 token refresh and WebSocket heartbeat management
- Message deduplication with capacity-based eviction
- Support both C2C (private) and group AT messages
- Integrate with onboard wizard, integrations registry, and channel
list/doctor commands
- Include unit tests for user allowlist, deduplication, and config
Integrate cloud endpoint behavior into existing ollama provider flow, avoid a separate standalone doc, and keep configuration minimal via api_url/api_key.
Also align reply_target and memory trait call sites needed for current baseline compatibility.
* fix(providers): add CN/global endpoint variants for Chinese vendors
* fix(onboard): deduplicate provider key-url match arms
* chore(i18n): normalize non-English literals to English
Adds a new Signal messaging channel that connects to a running
signal-cli daemon's native HTTP API (JSON-RPC + SSE).
[channels_config.signal]
http_url = "http://127.0.0.1:8686"
account = "+1234567890"
group_id = "group_id" # optional, omit for all
allowed_from = ["+1111111111"]
ignore_attachments = true
ignore_stories = true
Implementation:
- SSE listener at /api/v1/events for incoming messages
- JSON-RPC sends via /api/v1/rpc (method: send)
- Health check via /api/v1/check
- Typing indicators via sendTyping RPC
- Supports DMs and group messages (room_id filtering)
- Allowlist-based sender filtering (E.164 or wildcard)
- Optional attachment/story filtering
- Fixed has_supervised_channels() to include signal + irc/lark/dingtalk
Registered in channel list, doctor, start, integrations registry, and
daemon supervisor gate. Includes unit tests for config serde, sender
filtering, room matching, envelope processing, and deserialization.
No new dependencies (uses existing uuid, futures-util, reqwest).
The existing Copilot provider passes a static Bearer token, but the
Copilot API requires short-lived session tokens obtained via GitHub's
OAuth device code flow, plus mandatory editor headers.
This replaces the stub with a dedicated CopilotProvider that:
- Runs the OAuth device code flow on first use (same client ID as VS Code)
- Exchanges the OAuth token for a Copilot API key via
api.github.com/copilot_internal/v2/token
- Sends required Editor-Version/Editor-Plugin-Version headers
- Caches tokens to disk (~/.config/zeroclaw/copilot/) with auto-refresh
- Uses Mutex to prevent concurrent refresh races / duplicate device prompts
- Writes token files with 0600 permissions (owner-only)
- Respects GitHub's polling interval and code expiry from device flow
- Sanitizes error messages to prevent token leakage
- Uses async filesystem I/O (tokio::fs) throughout
- Optionally accepts a pre-supplied GitHub token via config api_key
Fixes: 403 'Access to this endpoint is forbidden'
Fixes: 400 'missing Editor-Version header for IDE auth'
* feat(channels): add channel capabilities to system prompt
Add channel capabilities section to system prompt so the agent knows
it can send Discord messages directly without asking permission.
Also reminds agent not to repeat or echo credentials.
Co-authored-by: Vernon Stinebaker <vernon.stinebaker@gmail.com>
* feat(agent): scrub credentials from tool output
* chore: fix clippy and formatting for scrubbing
Add Astrai (https://as-trai.com) as a first-class OpenAI-compatible
provider. Astrai is an AI inference router with built-in cost
optimization, PII stripping, and compliance logging.
- Register ASTRAI_API_KEY env var in resolve_api_key
- Add "astrai" entry in provider factory → as-trai.com/v1
- Add factory_astrai unit test
- Add Astrai to compatible provider test list
- Update README provider count (22+ → 23+) and list
Co-authored-by: Maya Walcher <maya.walcher@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* test(security): add HTTP hostname canonicalization edge-case tests
Document that Rust's IpAddr::parse() rejects non-standard IP notations
(octal, hex, decimal integer, zero-padded) which provides defense-in-depth
against SSRF bypass attempts. Tests only — no production code changes.
Closes#515
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style: apply rustfmt to providers/mod.rs
Fix pre-existing formatting issue from main.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The CLI --port and --host args had hardcoded defaults (8080, 127.0.0.1)
that always overrode the user's config.toml [gateway] settings (port=3000,
host=127.0.0.1). Changed both args to Option types and fall back to
config.gateway.port / config.gateway.host when not explicitly provided.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>