Commit graph

26 commits

Author SHA1 Message Date
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
Chummy
c70d9b181d test: stabilize cron shell output capture and gemini warmup noop 2026-02-18 19:26:07 +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
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
431287184b style(tests): apply rustfmt to brittle-test hardening changes 2026-02-18 14:17:58 +08:00
Alex Gorevski
45cdd25b3d fix(tests): harden brittle tests for cross-platform stability and refactoring resilience
## Problem

The test suite contained several categories of latent brittleness
identified in docs/testing-brittle-tests.md that would surface during
refactoring or cross-platform (Windows) CI execution:

1. Hardcoded Unix paths: \Path::new("/tmp")\ and \PathBuf::from("/tmp")\
   used as workspace directories in agent tests, which fail on Windows
   where /tmp does not exist.

2. Exact string match assertions: ~20 \ssert_eq!(response, "exact text")\
   assertions in agent unit and e2e tests that break on any mock wording
   change, even when the underlying orchestration behavior is correct.

3. Fragile error message string matching: \.contains("specific message")\
   assertions coupled to internal error wording rather than testing the
   error category or behavioral outcome.

## What Changed

### Hardcoded paths → platform-agnostic temp dirs (4 files, 7 locations)
- \src/agent/tests.rs\: Replaced all 4 instances of \Path::new("/tmp")\
  and \PathBuf::from("/tmp")\ with \std::env::temp_dir()\ in
  \make_memory()\, \uild_agent_with()\, \uild_agent_with_memory()\,
  and \uild_agent_with_config()\ helpers.
- \	ests/agent_e2e.rs\: Replaced all 3 instances in \make_memory()\,
  \uild_agent()\, and \uild_agent_xml()\ helpers.

### Exact string assertions → behavioral checks (2 files, ~20 locations)
- \src/agent/tests.rs\: Converted 10 \ssert_eq!(response, "...")\ to
  \ssert!(!response.is_empty(), "descriptive message")\ across tests for
  text pass-through, tool execution, tool failure recovery, XML dispatch,
  mixed text+tool responses, multi-tool batch, and run_single delegation.
- \	ests/agent_e2e.rs\: Converted 9 exact-match assertions to behavioral
  checks. Multi-turn test now uses \ssert_ne!(r1, r2)\ to verify
  sequential responses are distinct without coupling to exact wording.
- Provider error propagation test simplified to \ssert!(result.is_err())\
  without asserting on the error message string.

### Fragile error message assertions → structural checks (2 files)
- \src/tools/git_operations.rs\: Replaced fragile OR-branch string match
  (\contains("git repository") || contains("Git command failed")\) with
  structural assertions: checks \!result.success\, error is non-empty,
  and error does NOT mention autonomy/read-only (verifying the failure
  is git-related, not permission-related).
- \src/cron/scheduler.rs\: Replaced \contains("agent job failed:")\ with
  \!success\ and \!output.is_empty()\ checks that verify failure behavior
  without coupling to exact log format.

## What Was NOT Changed (and why)
- \src/agent/loop_.rs\ parser tests: Exact string assertions are the
  contract for XML tool call parsing — the exact output IS the spec.
- \src/providers/reliable.rs\: Error message assertions test the error
  format contract (provider/model attribution in failure messages).
- \src/service/mod.rs\: Already platform-gated with \#[cfg]\; XML escape
  test is a formatting contract where exact match is appropriate.
- \src/config/schema.rs\: TOML test strings use /tmp as data values for
  deserialization tests, not filesystem access; HOME tests already use
  \std::env::temp_dir()\.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 14:17:58 +08:00
Alex Gorevski
21c5f58363 perf(cron): wrap record_run INSERT+DELETE in explicit transaction
Problem:
In record_run(), an INSERT into cron_runs followed by a pruning DELETE
ran as separate implicit transactions. If the INSERT succeeded but the
DELETE failed (e.g., due to disk pressure or lock contention), the run
table would grow unboundedly since the pruning step was lost while the
new row persisted.

Fix:
Wrap both statements in an explicit transaction using
conn.unchecked_transaction(). If either statement fails, the entire
transaction is rolled back, maintaining the invariant that the run
history stays bounded by max_run_history.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 14:07:31 +08:00
Alex Gorevski
9967eeb954 perf(cron): add composite index on cron_runs(job_id, started_at)
Problem:
The pruning query in record_run uses WHERE job_id = ?1 with
ORDER BY started_at DESC, but only single-column indexes exist
for job_id and started_at separately. SQLite must scan one index
and then sort or scan the other, which is suboptimal for the
combined filter + sort pattern used during pruning.

Fix:
Add a composite index CREATE INDEX IF NOT EXISTS
idx_cron_runs_job_started ON cron_runs(job_id, started_at).
This lets SQLite satisfy the WHERE job_id = ?1 ORDER BY
started_at DESC subquery in a single index scan without a
separate sort step. The existing single-column indexes are
retained for other queries that filter on only one column.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-18 14:06:59 +08:00
fettpl
7de052c7d2 fix(cron): add timeout and bounded execution for due jobs 2026-02-18 12:55:21 +08:00
Alex Gorevski
5f5cb27690
fix(cron): handle ALTER TABLE race condition in schema migration
Problem: add_column_if_missing() checks PRAGMA table_info for column existence, then issues ALTER TABLE ADD COLUMN if not found. When two concurrent processes both pass the check before either executes the ALTER, the second process fails with a 'duplicate column name' error.

Fix: Catch the 'duplicate column name' SQLite error after the ALTER TABLE and treat it as a benign no-op. Also explicitly drop statement/rows handles before ALTER to release locks.

Ref: #710 (Item 8)
2026-02-17 23:50:08 -05: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
Vernon Stinebaker
5b5d9fe77f
feat(discord): add mention_only config for @-mention trigger (#529)
When mention_only is true, the bot only responds to messages that
@-mention the bot. Other messages in the guild are silently ignored.
Also strips the bot mention from content before processing.

Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com>
2026-02-17 08:01:27 -05:00
Chummy
37df8f6b33 style(cron): apply rustfmt ordering for exports 2026-02-17 17:06:28 +08:00
mai1015
fb2d1cea0b Implement cron job management tools and types
- Added `JobType`, `SessionTarget`, `Schedule`, `DeliveryConfig`, `CronJob`, `CronRun`, and `CronJobPatch` types in `src/cron/types.rs` for cron job configuration and management.
- Introduced `CronAddTool`, `CronListTool`, `CronRemoveTool`, `CronRunTool`, `CronRunsTool`, and `CronUpdateTool` in `src/tools` for adding, listing, removing, running, and updating cron jobs.
- Updated the `run` function in `src/daemon/mod.rs` to conditionally start the scheduler based on the cron configuration.
- Modified command-line argument parsing in `src/lib.rs` and `src/main.rs` to support new cron job commands.
- Enhanced the onboarding wizard in `src/onboard/wizard.rs` to include cron configuration.
- Added tests for cron job tools to ensure functionality and error handling.
2026-02-17 17:06:28 +08:00
Argenis
15e1d50a5d
fix: replace std::sync::Mutex with parking_lot::Mutex (#350)
Merges #422
2026-02-16 15:02:46 -05:00
Chummy
80da3e64e9
feat: unify scheduled tasks from #337 and #338 with security-first integration
Unifies scheduled task capabilities and consolidates overlapping implementations from #337 and #338 into a single security-first integration path.\n\nCo-authored-by: Edvard <ecschoye@stud.ntnu.no>\nCo-authored-by: stawky <stakeswky@gmail.com>
2026-02-16 23:38:29 +08:00
Argenis
1530a8707d
feat: add Git operations tool for structured repository management
Implements #214 - Add git_operations tool that provides safe, parsed
git operations with JSON output and security policy integration.

Features:
- Operations: status, diff, log, branch, commit, add, checkout, stash
- Structured JSON output (parsed status, diff hunks, commit history)
- SecurityPolicy integration with autonomy-aware controls
- Command injection protection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 05:53:29 -05:00
Chummy
3bdabdc7ec
fix(security): enforce action guards in file_write and scheduler (#269) 2026-02-16 01:57:58 -05:00
argenis de la rosa
085b57aa30 refactor: consolidate CLI command definitions to lib.rs
- Move all CLI command enums (ChannelCommands, SkillCommands, CronCommands, IntegrationCommands, MigrateCommands, ServiceCommands) to lib.rs
- Add clap derives for use in main.rs CLI parsing
- Update all modules to use crate:: prefix instead of super:: for command types
- Add mod util; to main.rs for binary compilation
- Export Config type from lib.rs for main.rs

This refactoring eliminates code duplication between library modules and binary, centralizing all CLI command definitions in one place.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:52:33 -05:00
argenis de la rosa
04a35144e8 feat: integrate open-skills library and cleanup clippy warnings
- Add open-skills auto-clone/pull/sync support in skills loader
  - Clone https://github.com/besoeasy/open-skills to ~/open-skills
  - Weekly sync via .zeroclaw-open-skills-sync marker
  - Env controls: ZEROCLAW_OPEN_SKILLS_ENABLED, ZEROCLAW_OPEN_SKILLS_DIR
  - Load open-skills markdown files before workspace skills
  - Track Skill.location for accurate prompt rendering

- Update system prompt to render skill.location with fallback
  - Use actual file path when available
  - Maintain backward compatibility with workspace SKILL.md path

- Fix clippy warnings across tests and supporting files
  - Readable timestamp literals
  - Remove underscore bindings in tests
  - Use struct update syntax for Config::default() patterns
  - Fix module inception, duplicate attributes, manual strip
  - Clean raw string hashes and empty string construction

Resolves: #77
2026-02-14 20:25:07 -05:00
argenis de la rosa
153d6ff149 fix: resolve clippy warnings and formatting issues for CI
- Fix doc_markdown warnings in WhatsApp channel
- Fix needless_pass_by_value in cron, health, migration, service modules
- Fix match_same_arms in migration.rs
- Fix too_many_lines in skills/mod.rs
- Fix manual_let_else in tools/file_write.rs
- Apply cargo fmt formatting fixes

All 435 tests pass, clippy clean.
2026-02-14 15:36:19 -05:00
argenis de la rosa
acea042bdb feat: add AIEOS identity support and harden cron scheduler security
- Add IdentityConfig with format=openclaw|aieos, aieos_path, and aieos_inline
- Implement AIEOS v1.1 JSON parser and system prompt injection
- Add build_system_prompt_with_identity() supporting both OpenClaw markdown and AIEOS JSON
- Harden cron scheduler with SecurityPolicy checks (command allowlist, forbidden path arguments)
- Skip retries on deterministic security policy violations
- Add comprehensive tests for AIEOS config and cron security edge cases
- Update README with AIEOS documentation and schema overview
- Add .dockerignore tests for build context security validation
2026-02-14 13:26:08 -05:00
argenis de la rosa
ec2d5cc93d feat: enhance agent personality, tool guidance, and memory hygiene
- Expand communication style presets (professional, expressive, custom)
- Enrich SOUL.md with human-like tone and emoji-awareness guidance
- Add crash recovery and sub-task scoping guidance to AGENTS.md scaffold
- Add 'Use when / Don't use when' guidance to TOOLS.md and runtime prompts
- Implement memory hygiene system with configurable archiving and retention
- Add MemoryConfig options: hygiene_enabled, archive_after_days, purge_after_days, conversation_retention_days
- Archive old daily memory and session files to archive subdirectories
- Purge old archives and prune stale SQLite conversation rows
- Add comprehensive tests for new features
2026-02-14 11:28:39 -05:00
argenis de la rosa
05cb353f7f feat: initial release — ZeroClaw v0.1.0
- 22 AI providers (OpenRouter, Anthropic, OpenAI, Mistral, etc.)
- 7 channels (CLI, Telegram, Discord, Slack, iMessage, Matrix, Webhook)
- 5-step onboarding wizard with Project Context personalization
- OpenClaw-aligned system prompt (SOUL.md, IDENTITY.md, USER.md, AGENTS.md, etc.)
- SQLite memory backend with auto-save
- Skills system with on-demand loading
- Security: autonomy levels, command allowlists, cost limits
- 532 tests passing, 0 clippy warnings
2026-02-13 12:19:14 -05:00