Commit graph

123 commits

Author SHA1 Message Date
Edvard
6cbdef8c16 fix(channel): save original user text to conversation history
Previously, the memory-enriched message (with [Memory context] block
prepended) was saved to per-sender conversation history. On subsequent
turns the LLM saw stale memory fragments with raw keys baked into
prior "user" messages, creating compounding noise.

Save the original msg.content instead. Memory context is still injected
for the current LLM call but no longer persists across turns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:48:18 +08:00
Edvard
ea2ff7c53b fix(memory): add minimum-length filter for auto-save messages
Every user message was auto-saved to memory regardless of length,
flooding the store with trivial entries like "ok", "thanks", "hi".
These noise entries competed with real memories during recall, degrading
relevance — especially with keyword-only search.

Skip auto-saving messages shorter than 20 characters. Applied to both
the channel path (channels/mod.rs) and CLI agent path (agent/loop_.rs).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:26:31 +08:00
Chummy
63a59e3735 test(channels): assert single tool protocol block in final prompt 2026-02-20 10:25:48 +08:00
Edvard
35a3520621 fix(channel): remove duplicated tool protocol from system prompt
build_system_prompt() included a "## Tool Use Protocol" section with
the tag format and usage instructions. build_tool_instructions() then
appended another identical "## Tool Use Protocol" section with full
JSON schemas. This wasted ~1-2K tokens on every API call.

Remove the duplicate protocol block from build_system_prompt(), keeping
only the compact tool name/description list. The complete protocol with
schemas is provided by build_tool_instructions().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:25:48 +08:00
Edvard
3a8a1754ef fix(channel): replace hardcoded Discord bot text with generic channel text
The Channel Capabilities section in build_system_prompt() was hardcoded
to say "You are running as a Discord bot" for ALL channels, including
Telegram. This caused the LLM to misidentify itself and reference
Discord-specific features regardless of the actual channel.

Replace with generic "messaging bot" text. Per-channel delivery
instructions already exist via channel_delivery_instructions().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:25:07 +08:00
Alex Gorevski
4a7dff6ef1 docs(code): add module-level doc blocks to providers, channels, tools, and security
Each major subsystem mod.rs now includes a //! doc block explaining the
subsystem purpose, trait-driven architecture, factory registration pattern,
and extension guidance. This improves the generated rustdoc experience for
developers navigating ZeroClaw's modular architecture.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-19 13:19:46 -08:00
Chummy
3733856093 Fix skill instruction/tool injection in system prompts 2026-02-20 02:16:41 +08:00
Chummy
f2ffd653de fix(channel): preserve trailing user turn in normalization 2026-02-20 02:01:42 +08:00
Chummy
c5834b1077 fix(channel): normalize telegram history for MiniMax 2026-02-20 02:01:42 +08:00
Chummy
ef82c7dbcd fix(channels): interrupt in-flight telegram requests on newer sender messages 2026-02-20 01:54:07 +08:00
Chummy
d9a94fc763 fix(skills): escape inlined skill XML content 2026-02-20 01:28:49 +08:00
Edvard
8a4da141d6 fix(skills): inject skill prompts and tools into agent system prompt
Skill prompts and tool definitions from SKILL.toml were parsed and stored
correctly but never included in the agent's system prompt. Both prompt-building
paths (channels/mod.rs and agent/prompt.rs) only emitted skill metadata (name,
description, location), telling the LLM to "read" the SKILL.toml on demand.
This caused the agent to attempt manual file reads that often failed, leaving
skills effectively ignored.

Now both paths inline <instructions> and <tools> blocks inside each <skill>
XML element, so the agent receives full skill context without extra tool calls.

Closes #877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:28:49 +08:00
Chummy
14fb3fbcae fix(composio): resolve connected account refs after OAuth 2026-02-20 01:28:19 +08:00
Chummy
d714d3984e fix(memory): stop autosaving assistant summaries and filter legacy entries 2026-02-20 01:14:08 +08:00
Chummy
05404c6e7a perf(build): gate Matrix channel for faster iteration 2026-02-19 21:29:53 +08:00
Chummy
ce6ba36f4e test: account for ellipsis when compacting channel history 2026-02-19 21:25:21 +08:00
Chummy
dcd0bf641d feat: add multimodal image marker support with Ollama vision 2026-02-19 21:25:21 +08:00
Chummy
a5d7911923 feat(runtime): add reasoning toggle for ollama 2026-02-19 21:05:19 +08:00
Chummy
2016382f42 fix(channels): compact sender history and filter oversized memory context 2026-02-19 20:05:35 +08:00
Chummy
2c07fb1792 fix: fail fast on context-window overflow and reset channel history 2026-02-19 19:38:28 +08:00
Daniel Willitzer
9afe4f28e7 feat(channels): add threading support to message channels
Add optional thread_ts field to ChannelMessage and SendMessage for
platform-specific threading (e.g. Slack threads, Discord threads).

- ChannelMessage.thread_ts captures incoming thread context
- SendMessage.thread_ts propagates thread context to replies
- SendMessage::in_thread() builder for fluent API
- Slack: send with thread_ts, capture ts from incoming messages
- All reply paths in runtime preserve thread context via in_thread()
- All other channels initialize thread_ts: None (forward-compatible)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:52:30 +08:00
Jayson Reis
f1ca73d3d2 chore: Remove more blocking io calls 2026-02-19 18:04:55 +08:00
mmacedoeu
c2a1eb1088 feat(channels): implement WhatsApp Web channel with wa-rs integration
- Add wa-rs dependencies with custom rusqlite storage backend
- Implement functional WhatsApp Web channel using wa-rs Bot
- Integrate TokioWebSocketTransportFactory and UreqHttpClient
- Add message handling via Bot event loop with proper shutdown
- Create WhatsApp storage trait implementations for wa-rs
- Add WhatsApp config schema and onboarding support
- Implement Meta webhook verification for WhatsApp Cloud API
- Add webhook signature verification for security
- Generate unique message keys for WhatsApp conversations
- Remove unused Node.js whatsapp-web-bridge stub

Supersedes: baileys-based bridge approach in favor of native Rust wa-rs
2026-02-19 18:03:09 +08:00
Chummy
d8409b0878 fix(channels): include mattermost in launch/list checks 2026-02-19 14:53:58 +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
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
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
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
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
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
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
Chummy
4243d8ec86 fix(agent): parse tool-call alias tags in channel runtime 2026-02-18 00:28:08 +08:00
Vernon Stinebaker
7e3f5ff497 feat(channels): add Mattermost integration for sovereign communication 2026-02-18 00:19:20 +08:00
Chummy
cd0dd13476 fix(channels): complete SendMessage migration after rebase 2026-02-17 23:28:08 +08:00
Kieran
dbebd48dfe refactor(channel): accept SendMessage struct in Channel::send()
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)
2026-02-17 23:28:08 +08:00
stawky
ab561baa97 feat(approval): interactive approval workflow for supervised mode (#215)
- Add auto_approve / always_ask fields to AutonomyConfig
- New src/approval/ module: ApprovalManager with session-scoped allowlist,
  ApprovalRequest/Response types, audit logging, CLI interactive prompt
- Insert approval hook in agent_turn before tool execution
- Non-CLI channels auto-approve; CLI shows Y/N/A prompt
- Skip approval for read-only tools (file_read, memory_recall) by default
- 15 unit tests covering all approval logic
2026-02-17 23:06:12 +08:00