Commit graph

175 commits

Author SHA1 Message Date
s04
996f66b6a7 feat: add zeroclaw config schema for JSON Schema export
Add a `config schema` subcommand that dumps the full configuration
schema as JSON Schema (draft 2020-12) to stdout. This enables
downstream consumers (like PankoAgent) to programmatically validate
configs, generate forms, and stay in sync with zeroclaw's evolving
config surface without hand-maintaining copies of the schema.

- Add schemars 1.2 dependency and derive JsonSchema on all config
  structs/enums (schema.rs, policy.rs, email_channel.rs)
- Add `Config` subcommand group with `Schema` sub-command
- Output is valid JSON Schema with $defs for all 56 config types
2026-02-19 16:41:21 +08:00
bhagwan
c405cdf19a fix(channel/signal): route UUID senders as direct recipients
Privacy-enabled Signal users have no sourceNumber, so sender()
falls back to their UUID from the source field.  Previously
parse_recipient_target() treated non-E.164 strings without the
group: prefix as group IDs, causing signal-cli to reject the
UUID as an invalid base64 group ID.

Add is_uuid() helper using the already-imported uuid crate and
recognise valid UUIDs as Direct targets alongside E.164 numbers.
2026-02-19 15:19:41 +08:00
Chummy
d8409b0878 fix(channels): include mattermost in launch/list checks 2026-02-19 14:53:58 +08:00
Chummy
bc0be9a3c1 fix(linq): accept prefixed and uppercase webhook signatures 2026-02-19 14:49:52 +08:00
George McCain
361e750576 feat(channels): add Linq channel for iMessage/RCS/SMS support
The existing iMessage channel relies on AppleScript and only works on macOS.
Linq provides a REST API for iMessage, RCS, and SMS — this gives ZeroClaw
native iMessage support on any platform via webhooks.

Implements LinqChannel following the same patterns as WhatsAppChannel:
- Channel trait impl (send, listen, health_check, typing indicators)
- Webhook handler with HMAC-SHA256 signature verification
- Sender allowlist filtering
- Onboarding wizard step with connection testing
- 18 unit tests covering parsing, auth, and signature verification

Resolves #656 — the prior issue was closed without a merged PR, so this
is the actual implementation.
2026-02-19 14:49:52 +08:00
Chummy
d548caa5f3 fix(channel): clamp configurable timeout to minimum 30s 2026-02-19 14:19:49 +08:00
ZeroClaw Contributor
41a6ed30dd feat(channel): make message timeout configurable via channels_config.message_timeout_secs
Add configurable timeout for processing channel messages (LLM + tools).
Default: 300s (optimized for on-device LLMs like Ollama).
Can be overridden in config.toml:

[channels_config]
message_timeout_secs = 600
2026-02-19 14:19:49 +08:00
Alex Gorevski
52dc9fd9e9
Merge pull request #883 from agorevski/fix/cleartext-logging-sensitive-data
fix(security): prevent cleartext logging of sensitive data
2026-02-18 21:11:31 -08:00
Alex Gorevski
bbbcd06cca
Merge pull request #882 from agorevski/fix/hardcoded-crypto-test-values-v2
fix(security): replace hard-coded crypto test values with runtime-generate secrets
2026-02-18 21:11:23 -08:00
Alex Gorevski
4a9fc9b6cc fix(security): prevent cleartext logging of sensitive data
Address CodeQL rust/cleartext-logging alerts by breaking data-flow taint
chains from sensitive variables (api_key, credential, session_id, user_id)
to log/print sinks. Changes include:

- Replace tainted profile IDs in println! with untainted local variables
- Add redact() helper for safe logging of sensitive values
- Redact account identifiers in auth status output
- Rename session_id locals in memory backends to break name-based taint
- Rename user_id/user_id_hint in channels to break name-based taint
- Custom Debug impl for ComputerUseConfig to redact api_key field
- Break taint chain in provider credential factory via string reconstruction
- Remove client IP from gateway rate-limit log messages
- Break taint on auth token extraction and wizard credential flow
- Rename composio account ref variable to break name-based taint

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 20:12:45 -08:00
Alex Gorevski
9a784954f6 fix(security): replace hard-coded crypto test values with runtime-generated secrets
Replace hard-coded string literals used as cryptographic keys/secrets in
gateway webhook and WhatsApp signature verification tests with runtime-
generated random values. This resolves CodeQL rust/hard-coded-cryptographic-value
alerts while maintaining identical test coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 20:03:38 -08:00
Alex Gorevski
925a352454 fix(security): enforce HTTPS for sensitive data transmission
Add URL scheme validation before HTTP requests that transmit sensitive
data (account IDs, phone numbers, user IDs). All endpoints already use
HTTPS URLs, but this explicit check satisfies CodeQL rust/cleartext-
transmission analysis and prevents future regressions if URLs are
changed.

Affected files: composio.rs, whatsapp.rs, qq.rs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 20:03:02 -08:00
Chummy
606f2860a0 fix(matrix): send markdown replies and improve e2ee diagnostics
Enable matrix-sdk markdown support and send Matrix messages with text_markdown so clients can render formatted_body.

Add listener startup diagnostics for device verification and backup state to reduce confusion around matrix_sdk_crypto backup warnings.

Expand Matrix docs with backup-warning interpretation, unverified-device guidance, markdown formatting expectations, and updated log keyword appendix.
2026-02-19 10:23:10 +08:00
Chummy
18b6ea1e79 feat(matrix): enable e2ee flow and add channel operations docs 2026-02-18 22:45:11 +08:00
Chummy
e6029e8cec
test(channels): guard max_tool_iterations wiring for channel runtime (#817)
* test(channels): add regression coverage for configured tool iteration limits

* chore(ci): refresh checks after first-interaction workflow fix

* test(channels): reconcile merged runtime-route and iteration tests
2026-02-18 22:40:22 +08:00
Chummy
8988a069a6 feat(channels): add runtime provider/model switching for telegram and discord 2026-02-18 22:23:13 +08:00
Chummy
ce104bed45 feat(proxy): add scoped proxy configuration and docs runbooks
- add scope-aware proxy schema and runtime wiring for providers/channels/tools

- add agent callable proxy_config tool for fast proxy setup

- standardize docs system with index, template, and playbooks
2026-02-18 22:10:42 +08:00
Chummy
fed8ba21b8 fix(mattermost): handle mention boundary scanning correctly 2026-02-18 21:25:28 +08:00
Vernon Stinebaker
d97866a640 feat(mattermost): add mention_only config for @-mention filtering
Add mention_only support for the Mattermost channel, matching the existing
Discord implementation. When enabled, the bot only processes messages that
contain an @-mention of the bot username, reducing noise in busy channels.

- Add mention_only field to MattermostConfig schema (Option<bool>, default false)
- Rename get_bot_user_id() to get_bot_identity() returning (user_id, username)
- Add contains_bot_mention_mm() with case-insensitive word-boundary matching
  and metadata.mentions array support
- Add normalize_mattermost_content() to strip @-mentions from processed text
- Wire mention_only through channel and cron factory constructors
- Add 23 new tests covering mention detection, stripping, case-insensitivity,
  word boundaries, metadata mentions, empty-after-strip, and disabled passthrough
2026-02-18 21:25:28 +08:00
Chummy
50fd5b81e1 fix(test): stabilize cron output capture and clippy cleanups 2026-02-18 20:29:26 +08:00
Chummy
483acccdb7 feat(memory): add configurable postgres storage backend 2026-02-18 20:29:26 +08:00
Chummy
e10d359cf9 fix(email): preserve legacy poll_interval alias and avoid lock across await 2026-02-18 20:18:39 +08:00
Kieran
b3d5284be1 refactor(channel): remove dead poll_interval_secs from EmailConfig
Field is unused since the IMAP polling loop was replaced with IDLE.
Serde ignores unknown fields on deserialization, so existing configs
with the key set will continue to work without error.

Also add two focused tests for idle_timeout_secs: explicit
deserialization and propagation into EmailChannel.
2026-02-18 20:18:39 +08:00
Kieran
5d9e8705ac refactor(channel): replace hand-rolled IMAP with async-imap IDLE
Replace the blocking, poll-based IMAP client with async-imap and
IMAP IDLE (RFC 2177) for instant push delivery. Key changes:

- Add async-imap dependency with tokio runtime feature
- Rewrite connect/fetch/listen paths to fully async using tokio TLS
- Implement IDLE loop with exponential backoff reconnection (1s–60s cap)
- Add idle_timeout_secs config field (default 1740s per RFC 2177)
- Convert health_check to async connect-and-logout with 10s timeout
- Update affected tests from sync to #[tokio::test]

SMTP send path, allowlist enforcement, and Channel trait contract
are unchanged.
2026-02-18 20:18:39 +08:00
Chummy
cad7fb8f22 fix(channels): enforce reply_target naming consistency 2026-02-18 19:56:31 +08:00
Chummy
cfa7215688 fix(telegram): harden mention-only matching and retry cache 2026-02-18 19:51:42 +08:00
ZeroClaw Contributor
c0a80ad656 feat(channel): add mention_only option for Telegram groups
Adds mention_only config option to Telegram channel, allowing the bot
to only respond to messages that @-mention the bot in group chats.
Direct messages are always processed regardless of this setting.

Behavior:
- When mention_only = true: Bot only responds to group messages containing @botname
- When mention_only = false (default): Bot responds to all allowed messages
- DM/private chats always work regardless of mention_only setting

Implementation:
- Fetch and cache bot username from Telegram API on startup
- Check for @botname mention in group messages
- Strip mention from message content before processing

Config example:
[channels.telegram]
bot_token = "your_token"
mention_only = true

Changes:
- src/config/schema.rs: Add mention_only to TelegramConfig
- src/channels/telegram.rs: Implement mention_only logic + 6 new tests
- src/channels/mod.rs: Update factory calls
- src/cron/scheduler.rs: Update constructor call
- src/onboard/wizard.rs: Update wizard config
- src/daemon/mod.rs: Update test config
- src/integrations/registry.rs: Update test config
- TESTING_TELEGRAM.md: Add mention_only test section
- CHANGELOG.md: Document feature

Risk: medium
Backward compatible: Yes (default: false)
2026-02-18 19:51:42 +08:00
xj
3b75c6cc42 fix(channel): remove HEARTBEAT.md from channel system prompt
HEARTBEAT.md is only relevant to the heartbeat worker, which reads it
directly from disk. Including it in channel system prompts caused LLMs
to emit spurious 'HEARTBEAT_OK' acknowledgments at the start of
channel responses.

The agent prompt (src/agent/prompt.rs) still includes HEARTBEAT.md,
which is correct for agent and heartbeat contexts.
2026-02-18 19:36:46 +08:00
Chummy
5e800c38f1 fix(channel): cancel and join scoped typing task safely 2026-02-18 18:01:29 +08:00
Jayson Reis
12c5473083 fix: Keep typing status on telegram while message is being processed
# Conflicts:
#	src/channels/mod.rs
2026-02-18 18:01:29 +08:00
Chummy
1bfd50bce9 fix(mattermost): preserve threaded default and docs 2026-02-18 17:46:19 +08:00
Vernon Stinebaker
58120b1c69 feat(mattermost): add thread_replies config and typing indicator
Add two Mattermost channel enhancements:

1. thread_replies config option (default: false)
   - When false, replies go to the channel root instead of threading.
   - When true, replies thread on the original post.
   - Existing thread replies always stay in-thread regardless of setting.

2. Typing indicator (start_typing/stop_typing)
   - Implements the Channel trait's typing methods for Mattermost.
   - Fires POST /api/v4/users/me/typing every 4s in a background task.
   - Supports parent_id for threaded typing indicators.
   - Aborts cleanly on stop_typing via JoinHandle.

Updated all MattermostChannel::new call sites (start_channels, scheduler)
and added 9 unit tests covering thread routing and edge cases.
2026-02-18 17:46:19 +08:00
Chummy
219764d4d8 fix(channels): recover malformed invoke/tool_call output in daemon mode 2026-02-18 17:01:36 +08:00
hayoial
58958d9991 fix: add per-sender conversation history for channel messages
Channel messages (Telegram, Discord, etc.) previously had no multi-turn
context — each incoming message was processed with a fresh history
containing only the system prompt and the current user message.

This patch:
- Maintains a per-sender conversation history map (Arc<Mutex<HashMap>>)
- Restores prior turns when processing each new message
- Saves user + assistant turns after successful LLM response
- Caps history at 50 messages per sender to bound memory usage

Fixes the channel context continuity issue where the bot would respond
with 'I have no context' to every follow-up question.
2026-02-18 16:35:38 +08:00
Xiangjun Ma
f1db63219c refactor(telegram): address code review findings
- Add strip_tool_call_tags() to finalize_draft to prevent Markdown
  parse failures from tool-call tags reaching Telegram API
- Deduplicate parse_reply_target() call in update_draft (was called
  twice, discarding thread_id both times)
- Replace body.as_object_mut().unwrap() mutation with separate
  plain_body JSON literal (eliminates unwrap in runtime path)
- Clean up per-chat rate-limit HashMap entry in finalize_draft to
  prevent unbounded growth over long uptimes
- Extract magic number 80 to STREAM_CHUNK_MIN_CHARS constant in
  agent loop
2026-02-18 16:33:33 +08:00
Chummy
e326e12039 test(telegram): cover draft streaming paths and simplify stream modes 2026-02-18 16:33:33 +08:00
Xiangjun Ma
e21fe1ff55 fix(telegram): address Copilot review feedback
- Fix silent parse failures: message_id.parse().unwrap_or(0) replaced
  with match + tracing::warn on parse error (update_draft, finalize_draft)
- Fix UTF-8 panic: byte-based truncation replaced with char_indices()
  safe boundary detection for TELEGRAM_MAX_MESSAGE_LENGTH
- Fix global rate limiter: Mutex<Option<Instant>> replaced with
  Mutex<HashMap<String, Instant>> for per-chat rate limiting so
  concurrent conversations don't interfere with each other
- Document Block variant: clarify it's reserved for future use and
  currently behaves the same as Partial
2026-02-18 16:33:33 +08:00
Xiangjun Ma
118cd53922 feat(channel): stream LLM responses to Telegram via draft message edits
Wire the existing provider-layer streaming infrastructure through the
channel trait and agent loop so Telegram users see tokens arrive
progressively via editMessageText, instead of waiting for the full
response.

Changes:
- Add StreamMode enum (off/partial/block) and draft_update_interval_ms
  to TelegramConfig (backward-compatible defaults: off, 1000ms)
- Add supports_draft_updates/send_draft/update_draft/finalize_draft to
  Channel trait with no-op defaults (zero impact on existing channels)
- Implement draft methods on TelegramChannel using sendMessage +
  editMessageText with rate limiting and Markdown fallback
- Add on_delta mpsc::Sender<String> parameter to run_tool_call_loop
  (None preserves existing behavior)
- Wire streaming in process_channel_message: when channel supports
  drafts, send initial draft, spawn updater task, finalize on completion

Edge cases handled:
- 4096-char limit: finalize draft and fall back to chunked send
- Broken Markdown: use no parse_mode during streaming, apply on finalize
- Edit failures: fall back to sending complete response as new message
- Rate limiting: configurable draft_update_interval_ms (default 1s)
2026-02-18 16:33:33 +08:00
Chummy
83b098d7ac fix(imessage): preserve sqlite conn across polling safely 2026-02-18 14:45:05 +08:00
Alex Gorevski
1ddcb0a573 perf(imessage): reuse persistent SQLite connection across poll cycles
Problem:
The iMessage listener opened a new SQLite connection to the Messages
database on every ~3-second poll cycle via get_max_rowid() and
fetch_new_messages(), creating ~40 connection open/close cycles per
minute. Each cycle incurs filesystem syscalls, WAL header reads,
and potential page cache cold starts.

Fix:
Open a single read-only connection before the poll loop and reuse it
across iterations using the 'shuttle' pattern: the connection is moved
into each spawn_blocking closure and returned alongside the results,
then reassigned for the next iteration. This eliminates per-poll
connection overhead while preserving the spawn_blocking pattern that
keeps SQLite I/O off the Tokio runtime thread.

The standalone get_max_rowid() and fetch_new_messages() helper
functions are retained for use by tests and other callers.

Ref: zeroclaw-labs/zeroclaw#710 (Item 9)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 14:45:05 +08:00
Edvard
8a1e7cc7ef fix(agent): use config max_tool_iterations, add memory relevance filtering, rebalance search weights
Three fixes for conversation quality issues:

1. loop_.rs and channels now read max_tool_iterations from AgentConfig
   instead of using a hardcoded constant of 10, making it configurable.

2. Memory recall now filters entries below a configurable
   min_relevance_score threshold (default 0.4), preventing unrelated
   memories from bleeding into conversation context.

3. Default hybrid search weights rebalanced from 70/30 vector/keyword
   to 40/60, reducing cross-topic semantic bleed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:14:33 +08:00
Chummy
d42cb1e906 fix(auth): rebase PR #200 onto main and restore auth CLI flow 2026-02-18 12:57:44 +08:00
Codex
007368d586 feat(auth): add subscription auth profiles and codex/claude flows 2026-02-18 12:57:44 +08:00
Edvard
63602a262f fix(agent): use config-driven limits in run_tool_call_loop and trim_history
run_tool_call_loop used a hardcoded MAX_TOOL_ITERATIONS (10) and
trim_history/auto_compact_history used a hardcoded MAX_HISTORY_MESSAGES (50),
ignoring the user-configurable agent.max_tool_iterations and
agent.max_history_messages values in config.toml.

Meanwhile, agent.rs correctly reads from config — creating an inconsistency
where CLI single-shot mode respected config but the channel runtime and
interactive CLI loop silently ignored it.

Changes:
- Rename constants to DEFAULT_* to clarify they are fallback defaults
- Add max_tool_iterations parameter to run_tool_call_loop
- Add max_history parameter to trim_history and auto_compact_history
- Thread config.agent.max_tool_iterations through ChannelRuntimeContext
- Both CLI code paths now pass config values to run_tool_call_loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:49:28 +08:00
h1n054ur
1c074d5204 fix(discord): use channel name for reply routing instead of discord channel ID
The Discord channel was setting msg.channel to the numeric Discord
channel ID instead of the literal string 'discord'. This caused
process_channel_message() to fail the channels_by_name lookup since
the map is keyed by channel name (e.g. 'discord', 'telegram', 'slack').

The result: the bot receives messages and generates LLM responses but
never sends them back -- target_channel resolves to None so the send
call is silently skipped.

Every other channel (telegram, slack, whatsapp, matrix, signal, irc,
imessage, lark, dingtalk, qq, email, mattermost) correctly sets this
field to its channel name string. Discord was the only one using the
platform-specific ID.
2026-02-18 12:49:06 +08:00
Edvard
b3b1679218 feat(channels): implement typing indicator for Telegram channel
Add start_typing/stop_typing overrides to TelegramChannel following the
same pattern as DiscordChannel: spawn a tokio task that sends
sendChatAction every 4 seconds (Telegram typing expires after 5s),
and abort it on stop_typing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:06:58 +08:00
ZeroClaw Agent
36062fb1c2 feat(telegram): add forum topic support
Implement Telegram Forum (topic) support to allow the bot to respond
in the same topic where it was mentioned/called.

Changes:
- parse_update_message(): Extract message_thread_id and format reply_target as 'chat_id:thread_id'
- send(): Parse recipient to extract chat_id and optional thread_id
- All send methods now pass thread_id parameter and include message_thread_id in API requests
- Added test for forum topic message parsing

This ensures bot replies stay within the same forum topic thread.
2026-02-18 10:22:40 +08:00
Chummy
e2e431d9e7 style(channels): apply rustfmt drift after main rebase 2026-02-18 00:45:26 +08:00
Chummy
ef02f25c46 refactor(sync): migrate remaining std mutex usage to parking_lot 2026-02-18 00:45:26 +08:00
Chummy
5942caa083 chore(pr539): scope to dingtalk daemon fixes only 2026-02-18 00:42:40 +08:00