Breaking changes:
- Quick setup now requires --provider flag (no default)
- TUI wizard requires provider selection (no pre-selection)
- Docker compose requires PROVIDER env var
- .env.example no longer defaults to openrouter
Changes:
- wizard.rs: Remove hardcoded "openrouter" defaults, require explicit provider
- tui.rs: Add provider placeholder, require selection before proceeding
- .env.example: Use provider-neutral placeholders
- docker-compose.yml: Require PROVIDER to be explicitly set
- docs: Update examples to be provider-agnostic
This makes ZeroClaw truly provider-agnostic - users must choose
their preferred LLM provider rather than being pushed toward OpenRouter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace electric-brand-mark placeholder with actual logo image
- Update dist assets with new build
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The web/dist was not rebuilt after PR #2804 merge. This adds the
Electric Blue CSS classes to the built assets.
- index-CKdA0Apd.css now contains: electric-card, hero-panel, status-pill
- index-DsBWCyCx.js updated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use entry().or_insert() for Cache-Control so SSE no-cache is preserved
- Add security_headers_are_set_on_error_responses test
- Addresses CodeRabbit review feedback on #2476
Add production-ready CSS styling for the embedded web dashboard
with electric theme, collapsible sections, and responsive layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add language links to main README for es/pt/it
- Create docs/i18n/es/README.md with Spanish translation
- Create docs/i18n/pt/README.md with Portuguese translation
- Create docs/i18n/it/README.md with Italian translation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Initialize channel runtime providers through routed provider construction so model_routes, hint defaults, and route-scoped credentials are honored.
Add a regression test that verifies start_channels succeeds when global provider credentials are absent but route-level config is present.
Refs #2537
Wait for either SIGINT or SIGTERM on Unix so daemon mode behaves correctly under container and process-manager termination flows.
Record signal-specific shutdown reasons and add unit tests for shutdown signal labeling.
Refs #2529
On Windows, file paths use backslashes (\) but the test expected forward
slashes (/). The render_skill_location function now normalizes all path
separators to forward slashes for consistent XML output across platforms.
This fixes the failing test:
prompt_skills_compact_mode_omits_instructions_and_tools
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The test `run_tool_call_loop_uses_non_cli_session_grant_without_waiting_for_prompt`
was expecting session grant to bypass interactive approval for shell tool, but
session grant only bypasses non-interactive approvals by design.
Fix: add shell to auto_approve list in test config so echo hi doesn't require
interactive approval, allowing session grant to work as intended.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Previously, the one-time `non_cli_allow_all_once` token only bypassed
non-interactive approvals due to a condition that required
`!requires_interactive_approval`. This caused tools like `shell`
(which require interactive approval in supervised mode) to be blocked
even when the one-time bypass token was consumed.
Fix: Separate the bypass logic:
- One-time bypass token: bypass ALL approvals (including interactive)
- Session grant: bypass only non-interactive approvals (unchanged)
This fixes the failing test
`run_tool_call_loop_consumes_one_time_non_cli_allow_all_token`.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix missing canary_tokens_enabled parameter in run_tool_call_loop_with_non_cli_approval_context test call
- Remove unused imports in symlink_tests.rs (Config, handle_command, load_skills_with_config, SkillCommands)
- Remove unused import async_trait in plugins/loader.rs tests
- Prefix unused approval_manager variables with underscore in 5 channel test functions
Resolves compilation errors that prevented cargo test from completing and clears all unused import/variable warnings.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Track actual success status from tool execution results instead of
assuming all tool calls succeed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The release trigger guard uses git ls-remote and git fetch against a
bare HTTPS URL, which fails on private repos (403). Use the checkout
directory's configured auth headers instead.
The release_trigger_guard.py requires gh CLI to verify CI Required Gate
status in publish mode. Self-hosted hetzner runners don't have gh
pre-installed, causing the guard to fail with exit code 3.
Add a gh CLI install step before the guard runs, with a skip if gh is
already available.
Two pre-existing issues blocking Pub Release builds:
1. x86_64-unknown-linux-gnu binary grew to 24MB, exceeding the 23MB
hard limit. Bump Linux safeguard from 23MB to 26MB to accommodate
recent feature growth. Binary size investigation deferred to follow-up.
2. armv7-unknown-linux-gnueabihf fails compiling ring/aws-lc-sys due to
missing libc6-dev-armhf-cross headers. Add libc dev package install
for armv7 and aarch64 cross-compile targets.
- Switch one-click bootstrap docs and scripts to the new zeroclawlabs.ai install.sh domain-based installer
- Refine dashboard pairing UX, sidebar branding, and layout; wire Vite proxy to gateway /pair on the default port for reliable 6-digit pairing
Made-with: Cursor
The release safety gates branch inadvertently replaced all matrix os
labels with self-hosted Linux runner arrays, including macOS and Windows
targets that require GitHub-hosted runners. This caused all three
cross-platform builds to fail: macOS builds attempted C compilation with
GNU cc (missing -arch flag), and Windows MSVC builds failed without
lib.exe.
Restore the original GitHub-hosted labels:
- macos-15-intel for x86_64-apple-darwin
- macos-14 for aarch64-apple-darwin
- windows-latest for x86_64-pc-windows-msvc
Windows platforms have a default stack size (1-2MB) that is too small
for the heavy JsonSchema derives in config/schema.rs (133 derives).
This causes "thread 'main' has overflowed its stack" on startup.
Changes:
- Increase stack size to 8MB for x86_64-pc-windows-msvc
- Increase stack size to 8MB for aarch64-pc-windows-msvc
- Remove unused ErrorKind import in src/update.rs
Fixes: cargo run --bin zeroclaw stack overflow on Windows
The test environment uses local-only commits that don't exist on
GitHub, causing HTTP 422 "No commit found". Distinguish this from
real API failures by checking stderr for the specific error message
and downgrading to a warning. Real API errors still fail closed in
publish mode.
When publish_release=true, treat api_error and gh_not_found as
violations (blocking) instead of warnings. In verify mode, these
remain advisory warnings. This ensures publish cannot proceed without
verified CI status.
Addresses CodeRabbit review feedback on PR #2604.
- Dry-run gate: use server-side query params instead of client-side jq
filtering to avoid pagination issues
- Post-release validation: use artifact contract JSON for expected asset
count instead of hardcoded magic number
- Post-release validation: use grep -Fq for fixed-string version match
to avoid regex interpretation
- cut_release_tag.sh: clarify CI gate comment header
- Split GH_TOKEN away from binary smoke-test step to prevent token
exfiltration via compromised release artifact
- Wrap gh subprocess calls in try/except FileNotFoundError so the
guard degrades gracefully when gh CLI is not installed
- Remove stderr suppression from cargo check --locked so diagnostics
are visible on failure
The CI green gate queried gh api for check-run status, but in test
environments the commit SHA doesn't exist on GitHub, causing HTTP 422.
Downgrade api_error from violation to warning so the guard remains
functional in offline/test contexts while still blocking on real CI
failures (pending, not_found on actual repos, non-success conclusions).
- release_trigger_guard.py: block publish if CI Required Gate hasn't
passed on the tag commit; warn if no prior dry-run exists
- cut_release_tag.sh: check CI status via gh api before creating tag;
run cargo check --locked to catch stale Cargo.lock locally
- ci-post-release-validation.yml: new workflow triggered on release
publish — validates asset count, SHA256 checksums, and binary version
When using feishu with scheduled tasks, the system could not deliver
announcements because it only looked for [channels_config.feishu] and
did not fall back to [channels_config.lark] with use_feishu=true.
This change allows feishu announcements to use the lark config as a
fallback when use_feishu is enabled, fixing the delivery path for users
who configure under the lark section.
Fixes inability to send feishu messages when channel is configured as Lark.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Classify websocket idle/no-response conditions as transport-unavailable so Codex auto mode can fall back to SSE.
Keep partial-output timeout cases as stream errors and add focused regression tests for classification behavior.
Refs #2551
Fixes arbitrary file write via path traversal in rollup <4.59.0
(GHSA-mw96-cpmx-2vgc). Transitive dev dependency via vite in web/.
Supersedes #1883 (Dependabot could not rebase/recreate due to
removed dependabot.yml entry).
Co-authored-by: xj <gh-xj@users.noreply.github.com>
The contributor-tier-issues job was triggering on both opened and labeled
events, while labeled-routes also triggers on labeled events. This caused
duplicate workflow runs with "Canceling since a higher priority waiting
request" message.
Fix by limiting contributor-tier-issues to only run for opened/reopened
events, since contributor tier evaluation should happen when the issue/PR
is first created, not when labels are added later.
Fixes#2352
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The approval prompt message uses backticks for code formatting
but was missing the parse_mode field, Telegram displays the
backticks literally instead of rendering them as code.
Add "parse_mode": "Markdown" to the sendMessage request body
to enable proper formatting.
Fixes#2359
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route /approve-allow through a runtime-only pending-request path so it does not
persist approval policy changes for normal tools.
This preserves always_ask semantics for Telegram inline HITL approvals while
keeping /approve and /approve-request + /approve-confirm as the persistent
approval flows. Update docs and regression assertions accordingly.
* fix(gateway): rebuild web/dist assets to fix dashboard WebSocket auth
Commit 2ecfa0d2 updated web/src/lib/ws.ts to use Sec-WebSocket-Protocol
for bearer token auth, but web/dist/ was never rebuilt. The embedded
assets still sent the token as a query parameter, which the server
rejects — breaking the Agent tab WebSocket connection for all users.
Rebuild web/dist from current web/src to align embedded assets with the
server-side WebSocket auth contract.
Closes#2168
* ci: retrigger stalled checks
---------
Co-authored-by: Chummy <chumyin0912@gmail.com>
Co-authored-by: chumyin <chumyin@users.noreply.github.com>
* feat(deploy): Add initial multitenant deployment script
* feat(deploy): Harden security for multitenant script
- Change gateway host to 127.0.0.1 to prevent direct access.
- Integrate Certbot for automatic HTTPS configuration.
- Improve password handling by using htpasswd directly.
- Rename credentials file and add security warnings.
- Update firewall rules to only allow SSH and Nginx traffic.
- Add comments and improve script readability.
* feat(deploy): Migrate process management to systemd
- Add a systemd service template 'zeroclaw@.service' for robust process management.
- Rebuild 'zeroclaw-ctl' to use systemctl for start, stop, restart, enable, and disable actions.
- Redirect ZeroClaw logs to systemd-journald for centralized logging.
- Update 'zeroclaw-ctl logs' and 'zeroclaw-ctl pairing' to read from journalctl.
- Enhance security of the systemd service with PrivateTmp and ProtectSystem.
- Simplify user-facing commands in the management tool.
* fix(deploy): Improve robustness and user experience
- Add automatic creation of a swap file to prevent out-of-memory issues.
- Revamp 'zeroclaw-ctl status' to display a rich, formatted table including
status, PID, memory usage, enabled state, and the latest pairing code.
- Confirm that the user ID format mismatch bug was resolved in prior stages.
- Add final polish and comments to the script.
* docs(deploy): Add detailed README for multitenant script
---------
Co-authored-by: GitHub Action <action@github.com>
* docs: fix web_fetch provider list to include tavily
The code supports `tavily` as a web_fetch provider (see
src/tools/web_fetch.rs:376-377 and src/config/schema.rs:1603),
but the config reference was missing this provider in the list.
This updates the documentation to reflect the actual implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: retrigger intake after metadata update
* ci: retrigger stalled checks
* ci: retrigger stale checks
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Chummy <chumyin0912@gmail.com>
- regenerate web/dist from current web/src with npm run build\n- fix AgentChat history typing so web build is type-clean\n- keep websocket auth via Sec-WebSocket-Protocol bearer token and session_id path parity\n\nCloses #2168
Add .claude directory to .gitignore to exclude Claude Code
configuration and cache files from version control.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace single read with deadline-bounded loop that skips JSON-RPC messages
where id is None (server notifications like notifications/initialized).
Some MCP servers send notifications/initialized after the initialize response
but before the tools/list response. The old code would read this notification
as the tools/list reply, see result: None, and report 0 tools registered.
The fix uses a deadline-bounded loop to skip any JSON-RPC message where
id is None while preserving the total timeout across all iterations.
Fixes: zeroclaw-labs/zeroclaw#2327
Add `capabilities()` method to HookHandler trait so the runner can check
whether a hook has ModifyToolResults permission before allowing it to
mutate tool results. Without this, any registered hook could flip success,
rewrite output, or suppress errors with no gate.
The generic `API_KEY` environment variable unconditionally overwrote the
api_key loaded from config.toml, even when a valid key was already
configured. Since `API_KEY` is a very common env var name set by many
unrelated tools, this caused silent auth failures when the unrelated
value was sent to the configured provider.
Change the precedence so that `ZEROCLAW_API_KEY` always wins (explicit
intent), while `API_KEY` is only used as a fallback when the config has
no api_key set.
- Switch from sessionWebhook to /v1.0/robot/oToMessages/batchSend API
- Add access_token caching with automatic refresh (60s buffer)
- Enable cron job delivery to DingTalk (no user interaction required)
This change allows DingTalk to actively send messages (e.g., cron
reminders) without requiring the user to send a message first.
Add SkillToolHandler that converts SKILL.toml definitions into native
tool schemas, enabling skills to be invoked as standard tools through
the agent's tool-use protocol.
Made-with: Cursor
- Replace brittle split("state=") with parse_query_params utility
- Use const PROFILE_MISMATCH_PREFIX with starts_with instead of fragile contains
Made-with: Cursor
Add stale pending login detection (auto-cleanup after 24h), improved
device-code flow error messages with Cloudflare/403 detection, shared
OAuth helpers, and Box::pin fixes for large async futures.
Made-with: Cursor
Add `hooks: Option<&crate::hooks::HookRunner>` as the last parameter
to the public `agent::run()` (re-exported from `loop_::run`).
This enables library consumers to inject custom HookHandler
implementations (before_tool_call, on_after_tool_call) without
patching the crate. The hooks are threaded through to
`run_tool_call_loop` which already accepts and dispatches them.
All existing call sites pass `None`, preserving backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two root causes were addressed:
1. `wasm-tools` (wasmtime 28 + cranelift JIT) was listed in `default`
features. wasmtime's JIT backend has macOS version dependencies that
break builds and/or runtime on Catalina. The feature is now opt-in;
the default build is free of JIT dependencies and Catalina-safe.
Users on macOS 11+ can still enable it with `--features wasm-tools`.
2. `.cargo/config.toml` had no macOS target entries, so the binary's
minimum deployment version was left to toolchain defaults (which can
be set to macOS 11+ on newer hosts). Added explicit
`-mmacosx-version-min=10.15` for `x86_64-apple-darwin` and
`-mmacosx-version-min=11.0` for `aarch64-apple-darwin` (no Catalina
hardware exists for Apple Silicon).
Also added a "macOS Catalina (10.15) Compatibility" section to
`docs/troubleshooting.md` covering symptoms, root causes, and fixes.
https://claude.ai/code/session_01L2arD1QmRH1cRejbCmhyRf
Adds async background tool execution with auto-injection of completed results:
- BgRunTool: Dispatches any tool in background, returns job_id immediately
- BgStatusTool: Queries job status by ID or lists all jobs
- BgJobStore: In-memory job tracking per session
- Auto-injection: Completed jobs appear as <bg_result> XML in agent history
Security hardening (Track C):
- MAX_CONCURRENT_JOBS=5 prevents resource exhaustion
- XML escaping prevents injection attacks in format_bg_result_for_injection
- Recursion guard blocks bg_run spawning itself or bg_status
- Hard 600s timeout per job guaranteed
- One-time delivery prevents duplicate injection
- 5-minute auto-expiry bounds memory growth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add CachedCredentials with 50-minute TTL that transparently refreshes
from the ECS container credential endpoint, env vars, or EC2 IMDS.
- Add from_ecs() to credential resolve chain for ECS/Fargate support
- Move streaming credential fetch into async context for TTL validation
- Remove sync credential fallback (all paths now use TTL-aware cache)
- Double-checked locking prevents thundering herd on refresh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 14 test functions for ACP channel (allowlist logic and JSON-RPC structures)
- Fix mutex guard across await in send() method using take() pattern
- Add acp: None default fields to ChannelsConfig in schema.rs
- Integrate ACP channel into channels/mod.rs and collect_configured_channels()
- Update channels-reference.md documentation
- Resolve merge conflicts with upstream/dev
All 17 ACP tests pass successfully.
Replace raw TOML textarea with a structured form editor supporting
48 config sections (~200 fields), masked sensitive values, category
pill navigation, two-column responsive grid, and search filtering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remote channels (Telegram, Discord, Slack, etc.) previously either
auto-approved tool calls requiring approval (in the else-branch) or
bypassed the approval check entirely (by passing None for the
ApprovalManager). Both paths allowed unapproved tool execution.
This fix:
- Wires ApprovalManager into ChannelRuntimeContext so remote channels
actually enter the approval check
- Changes the non-CLI branch from auto-approve to deny-by-default
- Adds a tracing::warn log and descriptive error message guiding users
to configure auto_approve or set autonomy level to Full
- Updates stale doc comment on prompt_cli
Co-authored-by: xj <gh-xj@users.noreply.github.com>
Update the project description to better reflect ZeroClaw's role as an operating system for agentic workflows rather than just assistant infrastructure.
- add "channel.bluebubbles" to SUPPORTED_PROXY_SERVICE_KEYS so proxy
scope = "services" can target BlueBubbles via exact service key
(addresses final CodeRabbit finding on PR #2271)
- apply cargo fmt to auth_profile.rs and quota_tools.rs (pre-existing
formatting drift that would block cargo fmt --check in CI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#2275
The Quick Start section was previously removed, leaving a broken anchor link.
This restores essential installation instructions directly in the README:
- Homebrew install (one command)
- Clone + bootstrap (recommended for most users)
- Cargo install (for Rust developers)
- First run commands
Users no longer need to hunt through docs/ to find basic install steps.
Fixes formatting issues in cron/store.rs, memory/sqlite.rs, and
tools/git_operations.rs that cause cargo fmt --check to fail.
Closes#2279
Co-authored-by: xj <gh-xj@users.noreply.github.com>
* fix(web-fetch): remove dead feature gates, add noise stripping, add docstrings
The nanohtml2text and fast_html2md providers were both guarded by
cfg(feature) checks for features (web-fetch-plaintext, web-fetch-html2md)
that are never declared in Cargo.toml. This caused every web_fetch call
to silently return an error instead of fetching content.
Changes:
- Add strip_noise_elements() which removes <script>, <style>, <nav>,
<header>, <footer>, <aside>, <noscript>, <form>, <button> blocks
before text extraction, eliminating menu/ad/boilerplate noise.
- Fix fast_html2md path: when web-fetch-html2md feature is not compiled
in, fall through to nanohtml2text rather than returning an error.
- Fix nanohtml2text path: remove dead cfg(feature = "web-fetch-plaintext")
gate; nanohtml2text is a direct dependency and needs no feature flag.
- Both previously gated tests (html_to_markdown_conversion_preserves_structure,
html_to_plaintext_conversion_removes_html_tags) are now always-on.
Added strip_noise_removes_nav_scripts_footer test.
- Add docstrings to all public/private methods to meet coverage threshold.
Tavily and firecrawl providers are unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web-fetch): align default provider to nanohtml2text, remove dead feature
- Change empty-provider default from deprecated 'fast_html2md' to
'nanohtml2text' to match WEB_FETCH_PROVIDER_HELP and PR description.
- Remove dead 'web-fetch-plaintext' feature from Cargo.toml (no code
references it after the feature-gate removal).
- Apply cargo fmt to strip_noise_elements array formatting.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: xj <gh-xj@users.noreply.github.com>
The Copilot API proxy for Claude models (Opus 4.6, Opus 4.6-1m) splits
text content and tool_calls into separate choices. Previously only
choices[0] was read, causing all tool calls to be silently dropped
when they appeared in choices[1].
Merge text and tool_calls from all choices so tool calling works
regardless of how the proxy splits the response.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(rfc): add AWW (Agent Wide Web) proposal
Add RFC 001 proposing AWW (Agent Wide Web), a decentralized
experience exchange network for AI Agents, analogous to the
World Wide Web for human knowledge sharing.
Key features:
- Structured experience pages (similar to HTML)
- AWP protocol (Agent Web Protocol, similar to HTTP)
- AWW URL format for experience addressing
- ZeroClaw integration with auto-publish/query
- Phased roadmap from protocol to ecosystem
Vision: Enable agents to learn from each other's experiences,
building collective intelligence over time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(rfc): fix lint and reference quality for AWW proposal
* chore(pr): retrigger intake after template and linear updates
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: argenis de la rosa <theonlyhennygod@gmail.com>
SQLite WAL mode requires shared-memory (mmap/shm) which is unavailable
on many network and virtual shared filesystems (NFS, SMB/CIFS,
UTM/VirtioFS, VirtualBox shared folders), causing xShmMap I/O errors
at startup.
Add `sqlite_journal_mode` config option under `[memory]` that accepts
"wal" (default) or "delete". When set to "delete", SQLite uses the
legacy DELETE journal mode and disables mmap, allowing ZeroClaw to run
with workspaces on shared/network filesystems.
Usage:
[memory]
sqlite_journal_mode = "delete"
Changes:
- config/schema.rs: Add sqlite_journal_mode field to MemoryConfig
- memory/sqlite.rs: Add with_options() supporting journal mode selection
- memory/mod.rs: Pass journal_mode from config to SqliteMemory
- onboard/wizard.rs: Include new field in default MemoryConfig
- Register 4 new tools (ManageAuthProfileTool, CheckProviderQuotaTool,
SwitchProviderTool, EstimateQuotaCostTool) in all_tools_with_runtime
- SwitchProviderTool now loads config from disk and calls save() to
persist default_provider/default_model to config.toml
- Inject Provider & Budget Context section into system prompt when
Config is available
- Remove emoji from tool output for cleaner parsing
- Replace format! push_str with std::fmt::Write for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire QuotaMetadata into ChatResponse for all provider implementations,
enabling quota tracking data to flow from API responses through the
agent loop to quota monitoring tools.
Depends on: circuit breaker (#1842) + quota monitoring (#1904)
Made-with: Cursor
Add ProviderHealthTracker and BackoffStore for circuit breaker pattern
that tracks provider failures, enforces cooldown periods, and enables
automatic fallback to healthy providers.
Co-authored-by: Cursor <cursoragent@cursor.com>
WebDriver's execute() wraps the script as a function body. The snapshot
script used an IIFE without a top-level return, so the IIFE's return
value was discarded and the WebDriver function returned undefined (null).
All other execute() calls in the file (scroll, scrollIntoView, click)
correctly use explicit return statements.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explain why gcc-arm-linux-gnueabihf is installed for musleabihf builds:
- Pure arm-linux-musleabihf-gcc not available in standard repos
- Use gnueabihf linker as tool with Rust target spec
- Static linking via -C link-arg=-static produces portable musl binary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add build time comparison (native vs cross-compile)
- Note that .cargo/config.toml is now included in repo
- Add static linking benefits table
- Include verification commands for static binaries
- Add cross-platform prerequisites (Linux, macOS, Windows)
- Add workflow diagram for cross-compilation process
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add armv6l-unknown-linux-musleabihf target to .cargo/config.toml
- Add target spec JSON for cross-compilation support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive step-by-step guide for compiling ZeroClaw on
Raspberry Pi Zero W (512MB RAM, ARMv6). Includes:
- Target ABI comparison (gnueabihf vs musleabihf)
- Native compilation instructions with swap setup
- Cross-compilation from more powerful hosts
- systemd service configuration
- Troubleshooting for constrained devices
musleabihf is recommended for smaller static binaries and better
portability across Raspberry Pi OS versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MIME strings like 'audio/webm; codecs=opus' were incorrectly matched by
the 'opus' branch (contains-check) before reaching the 'webm' branch,
returning 'voice.opus' instead of 'voice.webm'. This could cause the
Groq Whisper API to reject or misidentify the file format.
Fix: split on ';' to extract only the base MIME type, then match
exhaustively. Also add 'audio/x-wav' as a wav alias.
Adds a regression test: audio_mime_to_filename('audio/webm; codecs=opus')
must return 'voice.webm'.
Reported by CodeRabbit in PR review.
(cherry picked from commit 84861c727a)
Audio/voice messages on the WhatsApp Web channel were silently dropped
because `text_content()` returns an empty string for non-text messages
and no transcription path existed (unlike the Telegram channel which
already uses `transcription::transcribe_audio()`).
Changes:
- **Cargo.toml**: Move `qrcode` and all `wa-rs-*` crates out of the
`[target.'cfg(any(linux|macos|windows))'.dependencies]` section into
the unconditional `[dependencies]` section. All affected crates are
`optional = true`, so they add no compile cost unless
`--features whatsapp-web` is active. The previous placement caused
Cargo to exclude them when targeting `android` (target_os = "android"
does not match the cfg predicate), producing E0433 unresolved-crate
errors for every wa-rs import in `whatsapp_web.rs` and
`whatsapp_storage.rs` on Android cross-compilation.
- **whatsapp_web.rs**:
- Add `transcription: Option<TranscriptionConfig>` field.
- Add `with_transcription()` builder (mirrors `TelegramChannel`).
- Add `audio_mime_to_filename()` helper mapping WhatsApp MIME types
(e.g. `audio/ogg; codecs=opus`) to filenames the Groq Whisper API
accepts.
- Extend `Event::Message` handler: when text is empty, check
`msg.audio_message`; download and decrypt audio via
`client.download(audio_msg.as_ref())` (`.as_ref()` required because
prost boxes nested proto fields as `Box<AudioMessage>`, which does
not itself implement `Downloadable`); forward decrypted bytes to
`transcription::transcribe_audio()`.
- Add three unit tests: builder enable/disable guard and MIME mapping.
- **mod.rs**: Chain `.with_transcription(config.transcription.clone())`
onto `WhatsAppWebChannel::new(...)` in the `"web"` factory branch so
transcription is active whenever the global `[transcription]` section
is enabled.
Activation: set `[transcription] enabled = true` and export
`GROQ_API_KEY` in the environment.
(cherry picked from commit 325241aeb6)
Register /new, /model, and /models commands with Telegram's Bot API
on startup so they appear in the command menu for users. Registration
is non-fatal — if the API call fails, a warning is logged and the
bot continues listening normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split the catch-all `_` match arm on the deleteMessage result into
separate `Ok(r)` and `Err(e)` arms so that HTTP status codes and
network errors are logged individually. The response body is not
logged (security policy).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When editMessageText returns 'message is not modified', the draft
already contains the correct content from update_draft. Detect this
Telegram API response and treat it as success rather than falling
through to the delete+send fallback, which would create a visible
duplicate message.
Also guard the final fallback: only send a new message after
successfully deleting the draft. If deleteMessage fails, the draft
still shows the response text, so sending would create a duplicate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add support for Chrome, Firefox, and Edge browsers to the browser_open tool,
which previously only supported Brave. Users can now specify the browser
via the browser_open config option.
Changes:
- Add browser_open config field: "disable" | "brave" | "chrome" | "firefox" | "edge" | "default"
- Implement platform-specific launch commands for Chrome, Firefox, and Edge
- When set to "disable", only the browser automation tool is registered, not the browser_open tool
- Update tool descriptions and error messages to reflect browser selection
Co-Authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): retrigger PR checks after intake body update
* fix(ci): stabilize local quality gates on rebased main
---------
Co-authored-by: Chummy <chumyin0912@gmail.com>
Co-authored-by: xj <gh-xj@users.noreply.github.com>
Implement CostObserver that intercepts LlmResponse observer events and
records token usage to the CostTracker with proper cost calculations.
Changes:
- Add src/observability/cost.rs: CostObserver implementation
- Listens for LlmResponse events with token counts
- Looks up model pricing from CostConfig (with fallback defaults)
- Records usage via CostTracker.record_usage()
- Includes model family matching for pricing lookups
- Update src/observability/mod.rs:
- Export CostObserver
- Add create_observer_with_cost_tracking() helper that wraps base
observer with CostObserver when cost tracking is enabled
- Update src/gateway/mod.rs:
- Use create_observer_with_cost_tracking() to wire cost observer
into the gateway observer stack when config.cost.enabled is true
The /api/cost endpoint already exists and will now return accurate
session/daily/monthly cost data populated by the CostObserver.
Resolves#2111
Phase 2 of ClawWork integration. Implements:
- TaskClassifier with 44 BLS occupations and wage data
- OccupationCategory enum (Tech/Business/Healthcare/Legal)
- Keyword-based classification with confidence scoring
- Hours estimation based on instruction complexity
- Fuzzy matching for occupation lookup
Reference: ClawWork/clawmode_integration/task_classifier.py
- Fix MD036: convert bold text to proper heading at line 197
- Add Vietnamese localization (cron-scheduling.md)
- Update Vietnamese SUMMARY.md with cron-scheduling link
Add SiliconFlow provider factory support and alias/env handling.
Normalize onboarding UX to volcengine while preserving doubao/ark runtime aliases.
Add integration registry entries and provider resolution coverage tests.
Expand provider and command docs with setup and validation examples.
Remove detailed installation, configuration, and development sections from root README. These comprehensive sections are better maintained in the dedicated documentation system (docs/README.md, docs/SUMMARY.md, docs/commands-reference.md, etc.). This change makes the README more focused and accessible as a project overview while keeping detailed information in the proper documentation locations.
## Changes
- Removed: Prerequisites, Quick Start (Homebrew, bootstrap, pre-built binaries)
- Removed: Subscription Auth section
- Removed: Architecture, Runtime support, Memory System details
- Removed: Security checklist and detailed channel allowlists
- Removed: Configuration examples (Ollama, llama.cpp, vLLM, Osaurus, etc.)
- Removed: Gateway API, Commands, Service Management sections
- Removed: Development, Open-Skills Opt-In sections
- Removed: Collaboration & Docs sections, Support ZeroClaw
Kept essential overview elements: features, benchmark, license, contributing links, and key navigation pointers to the docs hub.
- Remove trailing whitespace in build.gradle.kts, SIZE.md, README.md
- Add rt-multi-thread feature to tokio dependency
- Add tracing-subscriber for logging support
Part of Android Phase 3 integration work.
Reduce PR CI critical path from ~80min to ~30min and total runner
pressure by ~40% through job parallelization, deduplication, and
cache unification.
Changes:
- ci-run.yml: run lint, test, and build in parallel (remove lint→test
serial dependency); merge flake-probe retry logic into test job;
unify rust-cache prefix-key for lint/test; simplify ci-required gate
- ci-build-fast.yml: delete redundant workflow (cargo build --release
with lto=fat, codegen-units=1 duplicated ci-run.build's release-fast)
- feature-matrix.yml: skip 4 compilation lanes on PRs by default (only
trigger on ci:full or ci:feature-matrix label); remove unnecessary
fetch-depth: 0 from checkout
- docs/ci-map.md: update documentation to reflect new job topology,
parallel execution model, and label-gated feature matrix behavior
Addresses 4 findings from CodeRabbit's fourth review that were not
covered by the maintainer's commit 7ef075e:
1. [Major] http_client() per-call allocation: cache reqwest::Client in
FeishuDocTool struct field, return &reqwest::Client. Enables
connection pooling across all API calls.
2. [Major] SSRF bypass via HTTP redirects: download_media now uses a
no-redirect reqwest client (Policy::none()) to prevent attackers
from using a public URL that 301/302-redirects to internal IPs.
3. [Minor] Missing empty-conversion guard in action_upload_image:
added converted.is_empty() check consistent with all other
convert_markdown_blocks callers.
4. [Minor] Schema description for link_share stale: updated from
'default: true' to 'default: false' to match actual behavior.
Validation:
- cargo check --features channel-lark ✅
- cargo clippy -p zeroclaw --lib --features channel-lark -- -D warnings ✅
- cargo test --features channel-lark -- feishu_doc ✅ (7/7 tests pass)
(cherry picked from commit e846604a13)
Addresses all 5 findings from CodeRabbit's second review on PR #1853:
1. [Major] list_all_blocks: add MAX_PAGES (200) hard cap to prevent
unbounded pagination loops on misbehaving APIs or huge documents.
2. [Major] Empty conversion guard: action_write, action_update_block,
and write_single_cell now bail with explicit error when
convert_markdown_blocks returns empty results, preventing silent
data loss (delete-then-write-nothing scenario).
3. [Minor] action_create: grant_owner_permission failure is now a soft
warning instead of hard error. Document is already created and
verified; permission failure is reported in the response JSON
'warning' field instead of propagating as an error.
4. [Nitpick] extract_ttl_seconds: remove unreachable as_i64 fallback
branch (as_u64 already covers all non-negative integers).
5. [Nitpick] Add unit tests: test_extract_ttl_seconds_defaults_and_clamps
and test_write_rejects_empty_conversion.
Validation:
- cargo check --features channel-lark ✅
- cargo clippy -p zeroclaw --lib --features channel-lark -- -D warnings ✅
- cargo test --features channel-lark -- feishu_doc ✅ (7/7 tests pass)
(cherry picked from commit 762e6082ec)
- Reorder convert-before-delete in action_write, action_update_block,
and write_single_cell to prevent data loss if markdown conversion fails
- Separate create POST from verification retry loop in action_create
to prevent duplicate document creation on retry
- Add resolve_doc_token to upload_image and upload_file so wiki
node_token resolution works for upload actions
- Add SSRF protection to download_media: validate URL scheme (http/https
only), block local/private hosts via existing url_validation module
- Guard empty credentials in mod.rs: skip FeishuDocTool registration
when app_id or app_secret are empty/whitespace-only
(cherry picked from commit feb1d46f41)
Summary
- Problem: Agent cannot read DOCX files — file_read returns garbled binary/XML, making Word documents inaccessible to the
agent
- Why it matters: DOCX is the most common business document format; without native extraction, users must manually convert
files, breaking autonomous workflows
- What changed: Added docx_read tool using zip (existing) + quick-xml (new) to extract plain text from DOCX Office Open XML
- What did not change: No changes to file_read, agent loop, security policy, config schema, or any existing tool behavior
Label Snapshot (required)
- Risk label: risk: low
- Size label: size: S
- Scope labels: tool
- Module labels: tool: docx_read
- If any auto-label is incorrect: N/A
Change Metadata
- Change type: feature
- Primary scope: tool
Linked Issue
- Closes #(issue number)
Validation Evidence (required)
cargo fmt --all -- --check # pass
cargo clippy --all-targets -- -D warnings # pass (zero new warnings)
cargo test docx_read # 14/14 passed
- Evidence provided: test results, manual verification with zeroclaw agent -m against real DOCX file
Security Impact (required)
- New permissions/capabilities? No (mirrors existing pdf_read security model exactly)
- New external network calls? No
- Secrets/tokens handling changed? No
- File system access scope changed? No
Privacy and Data Hygiene (required)
- Data-hygiene status: pass
- Redaction/anonymization notes: Test fixtures use neutral content ("Hello DOCX", "First", "Second")
- Neutral wording confirmation: Yes
Compatibility / Migration
- Backward compatible? Yes
- Config/env changes? No
- Migration needed? No
i18n Follow-Through
- i18n follow-through triggered? No (no docs or user-facing wording changes)
Human Verification (required)
- Verified scenarios: zeroclaw agent -m "read the file test-test.docx and output the content" — model selected docx_read,
extracted text correctly
- Edge cases checked: invalid ZIP, missing word/document.xml, symlink escape, path traversal, rate limiting, truncation
- What was not verified: encrypted DOCX (out of scope), extremely large files (>50MB)
Side Effects / Blast Radius (required)
- Affected subsystems/workflows: Tool registry only — one new tool added
- Potential unintended effects: None — additive only, no existing behavior changed
- Guardrails/monitoring: Tool follows identical security chain as pdf_read
Rollback Plan (required)
- Fast rollback command/path: git revert <commit>
- Feature flags or config toggles: None needed (always-on, like pdf_read)
- Observable failure symptoms: docx_read tool missing from tool list
Risks and Mitigations
- Risk: quick-xml new dependency adds to compile time
- Mitigation: quick-xml is lightweight pure Rust (~15K LOC), widely used (100M+ downloads), and will be shared when
XLSX/PPTX tools are added later
Removed:
- scripts/android/termux-install.sh
- scripts/android/adb-install.sh
- site/android-install.html
The native APK is the install experience. No need for CLI complexity.
Phase 4 polish features:
Widget:
- ZeroClawWidget for home screen
- Shows agent status (running/stopped)
- Toggle button to start/stop
- Tap to open app
- Material 3 styling with rounded corners
Accessibility:
- AccessibilityUtils for TalkBack support
- Content descriptions for all UI elements
- Screen reader detection
- Live region announcements
- ContentDescriptions constants
Install Scripts:
- termux-install.sh - One-liner for Termux users
- adb-install.sh - Install from computer via USB
- android-install.html - Web installer page with:
- Platform detection (Android vs desktop)
- Direct APK download
- QR code for desktop users
- Step-by-step instructions
- Copy-to-clipboard for commands
Files:
- widget/ZeroClawWidget.kt (128 lines)
- accessibility/AccessibilityUtils.kt (123 lines)
- res/layout/widget_zeroclaw.xml
- res/xml/widget_info.xml
- res/drawable/widget_*.xml
- scripts/android/*.sh
- site/android-install.html
Total: +799 lines across 12 files
- Add non-loopback auth guard to /v1/chat/completions (matching /api/chat)
- Fix migration guide references to non-existent files (api_chat.rs,
openai_compat_shim.rs, mod_patch.rs) — endpoints live in openclaw_compat.rs
- Remove phantom `provider` field from /api/chat response docs
- Add TOML string escaping to config converter to handle special chars
- Add proper JSON parse error handling in config converter
- Update deployment checklist and troubleshooting to match actual file layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a complete OpenClaw → ZeroClaw migration toolkit:
- POST /api/chat: ZeroClaw-native endpoint with full agent loop (tools, memory,
context enrichment). Supports session_id scoping and context[] injection for
conversation history. Same code path as Linq/WhatsApp/Nextcloud handlers.
- POST /v1/chat/completions: OpenAI-compatible shim that routes through
run_gateway_chat_with_tools instead of the simple provider.chat_with_history
path. Extracts last user message + up to 10 messages of conversation context
from the messages[] array. Supports streaming (simulated SSE). Drop-in
replacement for OpenClaw callers with zero code changes.
Both endpoints include full observability instrumentation (AgentStart, LlmRequest,
LlmResponse, RequestLatency, AgentEnd), auth (pairing + webhook secret), rate
limiting, auto-save to memory, and response sanitization.
Also adds:
- scripts/convert-openclaw-config.py: Converts openclaw.json → config.toml with
provider mapping, channel detection, and migration notes
- docs/migration/openclaw-migration-guide.md: Full migration walkthrough with
endpoint reference, config mapping, callsite examples, and deployment checklist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- report api_key_configured via provider credential resolution (env + overrides)\n- set agent.compact_context default to true for new configs\n- align docs and tests with the new default\n\nRefs: #1983\nRefs: #1984\nContext: #1358\n\nCo-authored-by: Argenis <144828210+theonlyhennygod@users.noreply.github.com>
Implements a plugin system for ZeroClaw modeled after OpenClaw's architecture.
Key components:
- Plugin trait and PluginApi for registering tools/hooks
- Plugin manifest (zeroclaw.plugin.toml) for metadata
- Plugin discovery from bundled, global, and workspace directories
- PluginRegistry managing loaded plugins, tools, and hooks
- Error isolation via panic catching in register()
- Config integration via [plugins] section
Example plugin included in extensions/hello-world/.
Closes#1414
# Conflicts:
# src/config/mod.rs
# src/config/schema.rs
The memory_config value is moved into Config at line 512, but was
borrowed at line 547. Use config.memory.backend instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compute api_key_configured through provider credential resolution so env-variable credentials are reported correctly for scenarios and delegate agents.
Closes#1983
Set AgentConfig compact_context default to true and align config defaults/tests/docs so daemon conversations recover from context pressure out of the box.
Closes#1984
- restore Lark constructor wiring and platform-specific builders used by channel-lark builds\n- re-export syscall anomaly detector types from security module\n- suppress unauthorized Telegram prompts for unmentioned group messages when mention_only=true (still allow /bind)\n\nRefs: #1959\nRefs: #1960
Two build errors on release/v0.1.8:
1. `src/config/schema.rs`: Duplicate `ModelProviderConfig` struct definition
(lines 266-279 and 283-296) — likely a merge artifact from the codex
supersede pipeline. Removed the second identical copy.
2. `web/src/App.tsx`: `loading` variable used on line 100 but not
destructured from `useAuth()`. Added `loading` to the destructure
on line 83.
Both prevent `cargo build` and `npm run build` respectively.
Signed-off-by: Crossing-2d23 <crossing-2d23@smoothcurves.nexus>
On-behalf-of: Lupo <lupo@smoothcurves.nexus>
Fixes#1794
Adds native-tls feature to reqwest dependency, allowing ZeroClaw to use
certificates trusted by the system's native TLS implementation. This
enables proper certificate validation for users with custom CA roots
or corporate PKI infrastructure.
Co-authored-by: Preventnetworkhacking <preventnetworkhacking@users.noreply.github.com>
Implements self-update functionality that downloads the latest release
from GitHub and replaces the current binary.
Features:
- `zeroclaw update` - downloads and installs latest version
- `zeroclaw update --check` - checks for updates without installing
- `zeroclaw update --force` - forces update even if already latest
- Cross-platform support (Linux, macOS, Windows)
- Atomic binary replacement on Unix, rename+copy on Windows
- Platform-specific archive handling (.tar.gz on Unix, .zip on Windows)
Closes#1352
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit c4ba69b6bf)
When mention_only=true is set, the bot should not respond to non-text
messages (photos, documents, videos, stickers, voice) in group chats
unless the caption contains a bot mention.
Changes:
- Add mention_only check in try_parse_attachment_message() for group messages
- Check if caption contains bot mention before processing
- Skip attachment if no caption or no mention
- Add mention_only check in try_parse_voice_message() for group messages
- Voice messages cannot contain mentions, so always skip in groups
- Add unit tests for the new behavior
Fixes#1662
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 419376b1f1)
Addresses 4 findings from CodeRabbit's fourth review that were not
covered by the maintainer's commit 7ef075e:
1. [Major] http_client() per-call allocation: cache reqwest::Client in
FeishuDocTool struct field, return &reqwest::Client. Enables
connection pooling across all API calls.
2. [Major] SSRF bypass via HTTP redirects: download_media now uses a
no-redirect reqwest client (Policy::none()) to prevent attackers
from using a public URL that 301/302-redirects to internal IPs.
3. [Minor] Missing empty-conversion guard in action_upload_image:
added converted.is_empty() check consistent with all other
convert_markdown_blocks callers.
4. [Minor] Schema description for link_share stale: updated from
'default: true' to 'default: false' to match actual behavior.
Validation:
- cargo check --features channel-lark ✅
- cargo clippy -p zeroclaw --lib --features channel-lark -- -D warnings ✅
- cargo test --features channel-lark -- feishu_doc ✅ (7/7 tests pass)
Addresses all 5 findings from CodeRabbit's second review on PR #1853:
1. [Major] list_all_blocks: add MAX_PAGES (200) hard cap to prevent
unbounded pagination loops on misbehaving APIs or huge documents.
2. [Major] Empty conversion guard: action_write, action_update_block,
and write_single_cell now bail with explicit error when
convert_markdown_blocks returns empty results, preventing silent
data loss (delete-then-write-nothing scenario).
3. [Minor] action_create: grant_owner_permission failure is now a soft
warning instead of hard error. Document is already created and
verified; permission failure is reported in the response JSON
'warning' field instead of propagating as an error.
4. [Nitpick] extract_ttl_seconds: remove unreachable as_i64 fallback
branch (as_u64 already covers all non-negative integers).
5. [Nitpick] Add unit tests: test_extract_ttl_seconds_defaults_and_clamps
and test_write_rejects_empty_conversion.
Validation:
- cargo check --features channel-lark ✅
- cargo clippy -p zeroclaw --lib --features channel-lark -- -D warnings ✅
- cargo test --features channel-lark -- feishu_doc ✅ (7/7 tests pass)
- Reorder convert-before-delete in action_write, action_update_block,
and write_single_cell to prevent data loss if markdown conversion fails
- Separate create POST from verification retry loop in action_create
to prevent duplicate document creation on retry
- Add resolve_doc_token to upload_image and upload_file so wiki
node_token resolution works for upload actions
- Add SSRF protection to download_media: validate URL scheme (http/https
only), block local/private hosts via existing url_validation module
- Guard empty credentials in mod.rs: skip FeishuDocTool registration
when app_id or app_secret are empty/whitespace-only
Release v0.1.7 includes:
- Fixed supports_native_tools() for MiniMax and similar providers
- Fixed prompt_guard regex patterns for better injection detection
- Various stability and security improvements
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a Telegram message originates from a forum topic, the thread_id was
extracted and used for reply routing but never stored in ChannelMessage.thread_ts.
This caused all messages from the same sender to share conversation history
regardless of which topic they were posted in.
Changes:
- Set thread_ts to the extracted thread_id in parse_update_message,
try_parse_voice_message, and try_parse_attachment_message
- Use 'ref' in if-let patterns to avoid moving thread_id before it's assigned
- Update conversation_history_key() to include thread_ts when present,
producing keys like 'telegram_<thread_id>_<sender>' for forum topics
- Update conversation_memory_key() to also include thread_ts for memory isolation
This enables proper per-topic session isolation in Telegram forum groups while
preserving existing behavior for regular groups and DMs (where thread_ts is None).
Closes#1532
Replace line-based TOML masking with structured config masking so secret fields keep their original types (including reliability.api_keys arrays).\nHydrate dashboard PUT payloads with runtime config_path/workspace_dir and restore masked secret placeholders from current config before validation/save.\nAlso allow GET on /api/doctor for dashboard/client compatibility to avoid 405 responses.
- security: honor explicit command paths in allowed_commands list
- security: respect workspace_only=false in resolved path checks
- config: enforce 0600 permissions on every config save (unix)
- config: reject temp-directory paths in active workspace marker
- provider: preserve reasoning_content in tool-call conversation history
- provider: add allow_user_image_parts parameter for minimax compatibility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The supports_native_tools() method was hardcoded to return true,
but it should return the value of self.native_tool_calling to
properly disable native tool calling for providers like MiniMax.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(channels,providers): remap Docker /workspace paths and enable vision for custom provider
Two fixes:
1. Telegram channel: when a Docker-containerised runtime writes a file to
/workspace/<path>, the host-side sender couldn't find it because the
container mount point differs from the host workspace dir. Remap
/workspace/<rel> → <host_workspace_dir>/<rel> in send_attachment before
the path-exists check so generated media is delivered correctly.
2. Provider factory: custom: provider was created with vision disabled,
causing all image messages to be rejected with a capability error even
though the underlying OpenAI-compatible endpoint supports vision. Switch
to new_with_vision(..., true) so image inputs are forwarded correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(memory): restore Qdrant vector database backend
Re-adds the Qdrant memory backend that was removed from main in a
recent upstream merge. Restores:
- src/memory/qdrant.rs — full QdrantMemory implementation with lazy
init, HTTP REST client, embeddings, and Memory trait
- src/memory/backend.rs — Qdrant variant in MemoryBackendKind, profile,
classify and profile dispatch
- src/memory/mod.rs — module export, factory routing with build_qdrant_memory
- src/config/schema.rs — QdrantConfig struct and qdrant field on MemoryConfig
- src/config/mod.rs — re-export QdrantConfig
- src/onboard/wizard.rs — qdrant field in MemoryConfig initializer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
copilot is the only provider that performs a device-code flow automatically on
first run. openai-codex and gemini (when OAuth-backed) require an explicit
`zeroclaw auth login --provider <name>` step. Split the device-flow next-steps
block to reflect this distinction.
Addresses Copilot review comment on PR #1509.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hardcoded OPENROUTER_API_KEY hint with provider-aware logic:
- keyless local providers (ollama, llamacpp, etc.) show chat/gateway/status hints
- device-flow providers (copilot, gemini, openai-codex) show OAuth/first-run hint
- all other providers show the correct provider-specific env var via provider_env_var()
Also adds canonical alias "github-copilot" -> "copilot" in canonical_provider_name(),
and a new provider_supports_device_flow() helper with accompanying test.
Additionally fixes pre-existing compile blockers that prevented CI from running:
- fix(security): correct raw string literals in leak_detector.rs that terminated
early due to unescaped " inside r"..." (use r#"..."# instead)
- fix(gateway): add missing wati: None in two test AppState initializations
- fix(gateway): use serde::Deserialize path on WatiVerifyQuery struct
- fix(security): add #[allow(unused_imports)] on new pub use re-exports in mod.rs
- fix(security): remove unused serde::{Deserialize, Serialize} import
- chore: apply cargo fmt to files that had pending formatting diffs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scheduled jobs created via channel conversations (Discord, Telegram, etc.)
never delivered output back to the channel because:
1. The agent had no channel context (channel name + reply_target) in its
system prompt, so it could not populate the delivery config.
2. The schedule tool only creates shell jobs with no delivery support,
and the cron_add tool's delivery schema was opaque.
3. OpenAiCompatibleProvider was missing the native_tool_calling field,
causing a compile error.
Changes:
- Inject channel context (channel name + reply_target) into the system
prompt so the agent knows how to address delivery when scheduling.
- Improve cron_add tool description and delivery parameter schema to
guide the agent toward correct delivery config.
- Update schedule tool description to warn that output is only logged
and redirect to cron_add for channel delivery.
- Fix missing native_tool_calling field in OpenAiCompatibleProvider.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci(homebrew): prefer HOMEBREW_UPSTREAM_PR_TOKEN with fallback
* ci(homebrew): handle existing upstream remote and main base
* fix: always emit toolResult blocks for tool_use responses
The Bedrock Converse API requires that every toolUse block in an
assistant message has a corresponding toolResult block in the
subsequent user message. Two bugs caused violations of this contract:
1. When parse_tool_result_message failed (e.g. malformed JSON or
missing tool_call_id), the fallback emitted a plain text user
message instead of a toolResult block, causing Bedrock to reject
the request with "Expected toolResult blocks at messages.N.content
for the following Ids: ..."
2. When the assistant made multiple tool calls in a single turn, each
tool result was pushed as a separate ConverseMessage with role
"user". Bedrock expects all toolResult blocks for a turn to appear
in a single user message.
Fix (1) by making the fallback construct a toolResult with status
"error" containing the raw content, and attempting to extract the
tool_use_id from the previous assistant message if JSON parsing fails.
Fix (2) by merging consecutive tool-result user messages into a single
ConverseMessage during convert_messages.
Also accept alternate field names (tool_use_id, toolUseId) in addition
to tool_call_id when parsing tool result messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
MiniMax API does not support OpenAI-style native tool definitions
(`tools` parameter in chat completions). Sending them causes a 500
Internal Server Error with "unknown error (1000)" on every request.
Add a `native_tool_calling` field to `OpenAiCompatibleProvider` so each
constructor can declare its tool-calling capability independently.
MiniMax (via `new_merge_system_into_user`) now sets this to `false`,
causing the agent loop to inject tool instructions into the system
prompt as text instead of sending native JSON tool definitions.
Closes#1387
(cherry picked from commit 2b92a774fb)
(cherry picked from commit 1816e8a829)
Co-authored-by: keiten arch <tang.zhengliang@ivis-sh.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace 🙌 and 💪 with 🔥 and 👍 in the TELEGRAM_ACK_REACTIONS pool.
The removed emojis are not in Telegram's allowed reaction set, causing
~40% of ACK reactions to fail with REACTION_INVALID (400 Bad Request).
All replacements verified against the Telegram Bot API setMessageReaction
endpoint in a live private chat.
Closes#1475
* feat(composio): fix v3 compatibility with parameter discovery, NLP text execution, and error enrichment
Three-layer fix for the Composio v3 API compatibility issue where the LLM
agent cannot discover parameter schemas, leading to repeated guessing and
execution failures.
Layer 1 – Surface parameter hints in list output:
- Add input_parameters field to ComposioV3Tool and ComposioAction structs
- Pass through input_parameters from v3 list response via map_v3_tools_to_actions
- Add format_input_params_hint() to show required/optional param names in list output
Layer 2 – Support natural-language text execution:
- Add text parameter to tool schema (mutually exclusive with params)
- Thread text through execute handler → execute_action → execute_action_v3
- Update build_execute_action_v3_request to send text instead of arguments
- Skip v2 fallback when text-mode is used (v2 has no NLP support)
Layer 3 – Enrich execute errors with parameter schema:
- Add get_tool_schema() to fetch full tool metadata from GET /api/v3/tools/{slug}
- Add format_schema_hint() to render parameter names, types, and descriptions
- On execute failure, auto-fetch schema and append to error message
Root cause: The v3 API returns input_parameters in list responses but
ComposioV3Tool was silently discarding them. The LLM had no way to discover
parameter schemas before calling execute, and error messages provided no
remediation guidance — creating an infinite guessing loop.
Co-Authored-By: unknown <>
(cherry picked from commit fd92cc5eb0)
* fix(composio): use floor_char_boundary for safe UTF-8 truncation in format_schema_hint
Co-Authored-By: unknown <>
(cherry picked from commit 18e72b6344)
* fix(composio): restore coherent v3 execute flow after replay
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Problem: The existing http_request tool returns raw HTML/JSON, which is nearly unusable for LLMs to extract
meaningful content from web pages.
- Why it matters: All mainstream AI agents (Claude Code, Gemini CLI, Aider) have dedicated web content extraction
tools. ZeroClaw lacks this capability, limiting its ability to research and gather information from the web.
- What changed: Added a new web_fetch tool that fetches web pages and converts HTML to clean plain text using
nanohtml2text. Includes domain allowlist/blocklist, SSRF protection, redirect following, and content-type aware
processing.
- What did not change (scope boundary): http_request tool is untouched. No shared code extracted between http_request
and web_fetch (DRY rule-of-three: only 2 callers). No changes to existing tool behavior or defaults.
Label Snapshot (required)
- Risk label: risk: medium
- Size label: size: M
- Scope labels: tool, config
- Module labels: tool: web_fetch
- If any auto-label is incorrect, note requested correction: N/A
Change Metadata
- Change type: feature
- Primary scope: tool
Linked Issue
- Closes #
- Related #
- Depends on #
- Supersedes #
Supersede Attribution (required when Supersedes # is used)
N/A
Validation Evidence (required)
cargo fmt --all -- --check # pass
cargo clippy --all-targets -- -D warnings # no new warnings (pre-existing warnings only)
cargo test --lib -- web_fetch # 26/26 passed
cargo test --lib -- tools::tests # 12/12 passed
cargo test --lib -- config::schema::tests # 134/134 passed
- Evidence provided: unit test results (26 new tests), manual end-to-end test with Ollama + qwen2.5:72b
- If any command is intentionally skipped, explain why: Full cargo clippy --all-targets has 43 pre-existing errors
unrelated to this PR (e.g. await_holding_lock, format! appended to String). Zero errors from web_fetch code.
Security Impact (required)
- New permissions/capabilities? Yes — new web_fetch tool can make outbound HTTP GET requests
- New external network calls? Yes — fetches web pages from allowed domains
- Secrets/tokens handling changed? No
- File system access scope changed? No
- If any Yes, describe risk and mitigation:
- Deny-by-default: enabled = false by default; tool is not registered unless explicitly enabled
- Domain filtering: allowed_domains (default ["*"] = all public hosts) + blocked_domains (takes priority).
Blocklist always wins over allowlist.
- SSRF protection: Blocks localhost, private IPs (RFC 1918), link-local, multicast, reserved ranges, IPv4-mapped
IPv6, .local TLD — identical coverage to http_request
- Rate limiting: can_act() + record_action() enforce autonomy level and rate limits
- Read-only mode: Blocked when autonomy is ReadOnly
- Response size cap: 500KB default truncation prevents context window exhaustion
- Proxy support: Honors [proxy] config via tool.web_fetch service key
Privacy and Data Hygiene (required)
- Data-hygiene status: pass
- Redaction/anonymization notes: No personal data in code, tests, or fixtures
- Neutral wording confirmation: All test identifiers use neutral project-scoped labels
Compatibility / Migration
- Backward compatible? Yes — new tool, no existing behavior changed
- Config/env changes? Yes — new [web_fetch] section in config.toml (all fields have defaults)
- Migration needed? No — #[serde(default)] on all fields; existing configs without [web_fetch] section work unchanged
i18n Follow-Through (required when docs or user-facing wording changes)
- i18n follow-through triggered? No — no docs or user-facing wording changes
Human Verification (required)
- Verified scenarios:
- End-to-end test: zeroclaw agent with Ollama qwen2.5:72b successfully called web_fetch to fetch
https://github.com/zeroclaw-labs/zeroclaw, returned clean plain text with project description, features, star count
- Tool registration: tool_count increased from 22 to 23 when enabled = true
- Config: enabled = false (default) → tool not registered; enabled = true → tool available
- Edge cases checked:
- Missing [web_fetch] section in existing config.toml → works (serde defaults)
- Blocklist priority over allowlist
- SSRF with localhost, private IPs, IPv6
- What was not verified:
- Proxy routing (no proxy configured in test environment)
- Very large page truncation with real-world content
Side Effects / Blast Radius (required)
- Affected subsystems/workflows: all_tools_with_runtime() signature gained one parameter (web_fetch_config); all 5
call sites updated
- Potential unintended effects: None — new tool only, existing tools unchanged
- Guardrails/monitoring for early detection: enabled = false default; tool_count in debug logs
Agent Collaboration Notes (recommended)
- Agent tools used: Claude Code (Opus 4.6)
- Workflow/plan summary: Plan mode → approval → implementation → validation
- Verification focus: Security (SSRF, domain filtering, rate limiting), config compatibility, tool registration
- Confirmation: naming + architecture boundaries followed (CLAUDE.md + CONTRIBUTING.md): Yes — trait implementation +
factory registration pattern, independent security helpers (DRY rule-of-three), deny-by-default config
Rollback Plan (required)
- Fast rollback command/path: git revert <commit>
- Feature flags or config toggles: [web_fetch] enabled = false (default) disables completely
- Observable failure symptoms: tool_count in debug logs drops by 1; LLM cannot call web_fetch
Risks and Mitigations
- Risk: SSRF bypass via DNS rebinding (attacker-controlled domain resolving to private IP)
- Mitigation: Pre-request host validation blocks known private/local patterns. Same defense level as existing
http_request tool. Full DNS-level protection would require async DNS resolution before connect, which is out of scope
for this PR.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 04597352cc)
Addresses the unbounded-map gap left by #951: entries below the lockout
threshold (count < MAX_PAIR_ATTEMPTS, lockout = None) were never evicted,
allowing distributed brute-force (>1024 unique IPs, <5 attempts each) to
permanently fill the tracking map and disable accounting for new attackers.
Hardening delta on top of #951:
- Replace raw tuple with typed FailedAttemptState (count, lockout_until,
last_attempt) for clarity and to enable retention-based sweep.
- Bump MAX_TRACKED_CLIENTS from 1024 to 10_000.
- Add 15-min retention sweep (prune_failed_attempts) on 5-min interval.
- Switch lockout from relative (locked_at + elapsed) to absolute
(lockout_until) for simpler and monotonic comparison.
- Add LRU eviction fallback when map is at capacity after pruning.
- Add normalize_client_key() to sanitize whitespace/empty client IDs.
- Add 3 focused tests: per-client reset isolation, bounded map capacity,
and sweep pruning of stale entries.
Supersedes:
- #670 by @fettpl (original hardening branch, rebased as delta)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Prepends [YYYY-MM-DD HH:MM:SS TZ] to each user message before it
reaches the model. This gives the agent accurate temporal context
on every turn, not just session start.
Previously DateTimeSection only injected the time once when the
system prompt was built. Long conversations or cron jobs had
stale timestamps. Now every message carries the real time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci(homebrew): prefer HOMEBREW_UPSTREAM_PR_TOKEN with fallback
* ci(homebrew): handle existing upstream remote and main base
* feat(tools): Use system default browser instead of hard-coded Brave Browser
---------
Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com>
Adds a `/new` runtime chat command for Telegram and Discord that clears
the sender's conversation history without changing provider or model.
Useful for starting a fresh session when stale context causes issues.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Return output string from 'execute_and_persist_job' alongside job id and success flag.
- Include failure reason in 'tracing::warn' when a scheduler job fails.
- Makes failed cron job errors visible in logs without inspecting the database.
Gemini CLI oauth_creds.json can omit client_id/client_secret, causing refresh requests to fail with HTTP 400 invalid_request (could not determine client ID).
Parse id_token claims (aud/azp) as a client_id fallback, preserve env/file overrides, and keep refresh form logic explicit. Also add camelCase deserialization aliases and regression tests for refresh-form and id_token parsing edge cases.
Refs #1424
The previous emoji set included unsupported reactions (🦀, 👣) that Telegram API
rejects with REACTION_INVALID error in some chat contexts. Remove these while
keeping the working emojis.
Before: ["⚡️", "🦀", "🙌", "💪", "👌", "👀", "👣"]
After: ["⚡️", "🙌", "💪", "👌", "👀"]
Fixes warning: REACTION_INVALID 400 Bad Request
When max_response_size is set to 0, the condition `text.len() > 0` is
true for any non-empty response, causing all responses to be truncated
to empty strings. The conventional meaning of 0 for size limits is
"no limit" (matching ulimit, nginx client_max_body_size, curl, etc.).
Add an early return when max_response_size == 0 and update the doc
comment to document this behavior.
Fix OpenAI Codex vision support by converting file paths to data URIs
before sending requests to the API.
## Problem
OpenAI Codex API was rejecting vision requests with 400 error:
"Invalid 'input[0].content[1].image_url'. Expected a valid URL,
but got a value with an invalid format."
Root cause: provider was sending raw file paths (e.g. `/tmp/test.png`)
instead of data URIs (e.g. `data:image/png;base64,...`).
## Solution
Add image normalization in both `chat_with_system` and `chat_with_history`:
- Call `multimodal::prepare_messages_for_provider()` before building request
- Converts file paths to base64 data URIs
- Validates image size and MIME type
- Works with both local files and remote URLs
## Changes
- `src/providers/openai_codex.rs`:
- Normalize images in `chat_with_system()`
- Normalize images in `chat_with_history()`
- Simplify `ResponsesInputContent.image_url` from nested object to String
- Fix unit test assertion for flat image_url structure
- `tests/openai_codex_vision_e2e.rs`:
- Add E2E test for second profile vision support
- Validates capabilities, request success, and response content
## Verification
✅ Unit tests pass: `cargo test --lib openai_codex`
✅ E2E test passes: `cargo test openai_codex_second_vision -- --ignored`
✅ Second profile accepts vision requests (200 OK)
✅ Returns correct image descriptions
## Impact
- Enables vision support for all OpenAI Codex profiles
- Second profile works without rate limits
- Fallback chain: default → second → gemini
- No breaking changes to existing non-vision flows
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add vision capability declaration (vision: true)
- Extend ResponsesInputContent to support image_url field
- Update build_responses_input() to parse [IMAGE:...] markers
- Add ImageUrlContent structure for data URI images
- Maintain backward compatibility with text-only messages
- Add comprehensive unit tests for image handling
Enables multimodal input for gpt-5.3-codex and similar models.
Image markers are parsed and sent as separate input_image content items.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Добавлен автоматический refresh протухших OAuth токенов Gemini при вызове warmup().
## Проблема
При использовании Gemini как fallback провайдера, OAuth токены могут протухнуть пока daemon работает. Это приводит к ошибкам при попытке переключения с OpenAI Codex на Gemini.
Сценарий:
1. Daemon работает, но не делает запросов к Gemini
2. OAuth токены Gemini истекают (TTL = 1 час)
3. Происходит ошибка на OpenAI Codex → fallback на Gemini
4. Gemini провайдер использует протухшие токены → запрос падает
## Решение
### Изменения в `GeminiProvider::warmup()`
Добавлена проверка и обновление токенов для `ManagedOAuth`:
- Вызывается `AuthService::get_valid_gemini_access_token()` который автоматически обновляет токены если нужно
- Для `OAuthToken` (CLI): пропускается (существующее поведение)
- Для API key: проверяется через публичный API (существующее поведение)
### Тесты
**Unit тесты** (`src/providers/gemini.rs`):
- `warmup_managed_oauth_requires_auth_service()` — проверка что ManagedOAuth требует auth_service
- `warmup_cli_oauth_skips_validation()` — проверка что CLI OAuth пропускает валидацию
**E2E тест** (`tests/gemini_fallback_oauth_refresh.rs`):
- `gemini_warmup_refreshes_expired_oauth_token()` — live тест с expired токеном и реальным refresh
- `gemini_warmup_with_valid_credentials()` — простой тест что warmup работает с валидными credentials
### Зависимости
Добавлена dev-зависимость `scopeguard = "1.2"` для безопасного восстановления файлов в тестах.
## Верификация
Проверено на live daemon с Telegram ботом:
- OpenAI Codex упал с 429 rate limit
- Fallback на Gemini сработал успешно
- Бот ответил через Gemini без ошибок
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When using streaming mode with Telegram, the finalize_draft function
would only edit the message text and never send actual image attachments
marked with [IMAGE:path] syntax.
This fix:
- Parses attachment markers in finalize_draft
- Deletes the draft message when attachments are present
- Sends text and attachments as separate messages
- Maintains backward compatibility for text-only messages
Fixes: Telegram finalize_draft edit failed; falling back to sendMessage
Issue: #1420
Some LLM providers (e.g., xAI grok) output tool calls in the format:
```tool file_write
{"path": "...", "content": "..."}
```
Previously, ZeroClaw only matched:
- ```tool_call
- ```tool-call
- ```toolcall
- ```invoke
This caused silent failures where:
1. Tool calls were not parsed
2. Agent reported success but no tools executed
3. LLM hallucinated tool execution results
Fix:
1. Added new regex `MD_TOOL_NAME_RE` to match ` ```tool <name>` format
2. Parse the tool name from the code block header
3. Parse JSON arguments from the block content
4. Updated `detect_tool_call_parse_issue()` to include this format
Added 3 tests:
- parse_tool_calls_handles_tool_name_fence_format
- parse_tool_calls_handles_tool_name_fence_shell
- parse_tool_calls_handles_multiple_tool_name_fences
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ci(homebrew): prefer HOMEBREW_UPSTREAM_PR_TOKEN with fallback
* ci(homebrew): handle existing upstream remote and main base
* fix(skills): allow cross-skill references in open-skills audit
Issue: #1391
The skill audit was too strict when validating markdown links in
open-skills, causing many skills to fail loading with errors like:
- "absolute markdown link paths are not allowed (../other-skill/SKILL.md)"
- "markdown link points to a missing file (skill-name.md)"
Root cause:
1. `looks_like_absolute_path()` rejected paths starting with ".."
before canonicalization could validate they stay within root
2. Missing file errors were raised for cross-skill references that
are valid but point to skills not installed locally
Fix:
1. Allow ".." paths to pass through to canonicalization check which
properly validates they resolve within the skill root
2. Treat cross-skill references (parent dir traversal or bare .md
filenames) as non-fatal when pointing to missing files
Cross-skill references are identified by:
- Parent directory traversal: `../other-skill/SKILL.md`
- Bare skill filename: `other-skill.md`
- Explicit relative: `./other-skill.md`
Added 6 new tests to cover edge cases for cross-skill references.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(config): warn on unknown config keys to prevent silent misconfig
Issue: #1304
When users configure `[providers.ollama]` with `api_url`, the setting is
silently ignored because `[providers.*]` sections don't exist in the
config schema. This causes Ollama to always use localhost:11434 regardless
of the configured URL.
Fix: Use serde_ignored to detect and warn about unknown config keys at
load time. This helps users identify misconfigurations like:
- `[providers.ollama]` (should be top-level `api_url`)
- Typos in section names
- Deprecated/removed options
The warning is non-blocking - config still loads, but users see:
```
WARN Unknown config key ignored: "providers". Check config.toml...
```
This follows the fail-fast/explicit errors principle (CLAUDE.md §3.5).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a debug-level log line confirming when the startup probe succeeds
and the main long-poll loop is entered. Aids diagnostics when
troubleshooting persistent 409s (e.g. from an external competing poller).
Note: persistent 409 despite the startup probe and 35s backoff indicates
an external process is actively polling the same bot token from another
host. In that case, rotating the bot token via @BotFather is the fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every daemon restart produced a flood of 409 Telegram polling conflicts for
up to several minutes. Two changes fix this:
1. **Startup probe (retry loop):** Before entering the long-poll loop,
repeatedly issue `getUpdates?timeout=0` until a 200 OK is received.
This claims the Telegram getUpdates slot before the 30-second long-poll
starts, preventing the first long-poll from racing a stale server-side
session left by the previous daemon. The probe retries every 5 seconds
until the slot is confirmed free.
2. **Extended 409 backoff:** Increased from 2 s → 35 s (> the 30-second
poll timeout). If a 409 still occurs despite the probe (e.g. in a genuine
dual-instance scenario), the retry now waits long enough for the competing
session to expire naturally before the next attempt, instead of hammering
Telegram with ~15 retries per minute.
Fixes#1281.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue: #1391
The skill audit was too strict when validating markdown links in
open-skills, causing many skills to fail loading with errors like:
- "absolute markdown link paths are not allowed (../other-skill/SKILL.md)"
- "markdown link points to a missing file (skill-name.md)"
Root cause:
1. `looks_like_absolute_path()` rejected paths starting with ".."
before canonicalization could validate they stay within root
2. Missing file errors were raised for cross-skill references that
are valid but point to skills not installed locally
Fix:
1. Allow ".." paths to pass through to canonicalization check which
properly validates they resolve within the skill root
2. Treat cross-skill references (parent dir traversal or bare .md
filenames) as non-fatal when pointing to missing files
Cross-skill references are identified by:
- Parent directory traversal: `../other-skill/SKILL.md`
- Bare skill filename: `other-skill.md`
- Explicit relative: `./other-skill.md`
Added 6 new tests to cover edge cases for cross-skill references.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NVIDIA's NIM API (integrate.api.nvidia.com) does not support the
OpenAI Responses API endpoint. When chat completions returns a
non-success status, the fallback to /v1/responses also fails with
404, producing a confusing double-failure error.
Use `new_no_responses_fallback()` for the NVIDIA provider, matching
the approach already used for GLM and other chat-completions-only
providers.
Fixes#1282
echo "Required CI jobs did not pass: lint=${lint_result} workspace-check=${workspace_check_result} package-check=${package_check_result} test=${test_result} build=${build_result}"
exit 1
fi
if [ "$event_name" = "pull_request" ] && [ "$license_owner_result" != "success" ]; then
- those events retrigger only label-driven `pull_request_target` automation (`pr-auto-response.yml`); `pr-labeler.yml` now runs only on PR lifecycle events (`opened`/`reopened`/`synchronize`/`ready_for_review`) to reduce churn.
6. When contributor pushes new commits to fork branch (`synchronize`):
1. Triggered on `pull_request` to `dev` or `main` when Docker build-input paths change.
2. Runs `PR Docker Smoke` job:
- Builds local smoke image with Blacksmith builder.
- Builds local smoke image with Buildx builder.
- Verifies container with `docker run ... --version`.
3. Typical runtime in recent sample: ~240.4s.
4. No registry push happens on PR events.
@@ -204,9 +199,8 @@ Canary policy lane:
## Merge/Policy Notes
1. Workflow-file changes (`.github/workflows/**`) activate owner-approval gate in `ci-run.yml`.
1. Workflow-file changes (`.github/workflows/**`) are validated through `pr-intake-checks.yml`, `ci-change-audit.yml`, and `CI Required Gate` without a dedicated owner-approval gate.
2. PR lint/test strictness is intentionally controlled by `ci:full` label.
3.`pr-intake-checks.yml` now blocks PRs missing a Linear issue key (`RMN-*`, `CDV-*`, `COM-*`) to keep execution mapped to Linear.
4.`sec-audit.yml` runs on PR/push/merge queue (`merge_group`), plus scheduled weekly.
5.`ci-change-audit.yml` enforces pinned `uses:` references for CI/security workflow changes.
6.`sec-audit.yml` includes deny policy hygiene checks (`deny_policy_guard.py`) before cargo-deny.
@@ -216,6 +210,7 @@ Canary policy lane:
10. Workflow-specific JavaScript helpers are organized under `.github/workflows/scripts/`.
11.`ci-run.yml` includes cache partitioning (`prefix-key`) across lint/test/build/flake-probe lanes to reduce cache contention.
12.`ci-rollback.yml` provides a guarded rollback planning lane (scheduled dry-run + manual execute controls) with audit artifacts.
13.`ci-queue-hygiene.yml` periodically deduplicates superseded queued runs for lightweight PR automation workflows to reduce queue pressure.
## Mermaid Diagrams
@@ -240,29 +235,29 @@ flowchart TD
G --> H["push event on dev"]
```
### Promotion and Release
### Main Delivery and Release
```mermaid
flowchart TD
D0["Commit reaches dev"] --> B0["ci-run.yml"]
D0 --> C0["sec-audit.yml"]
P["Promotion PR dev -> main"] --> PG["main-promotion-gate.yml"]
PG --> M["Merge to main"]
PRM["PR to main"] --> QM["ci-run.yml + sec-audit.yml (+ path-scoped)"]
This file defines the default working protocol for coding agents in this repository.
Scope: entire repository.
## 0) Session Default Target (Mandatory)
- When operator intent does not explicitly specify another repository/path, treat the active coding target as this repository (`/home/ubuntu/zeroclaw`).
- Do not switch to or implement in other repositories unless the operator explicitly requests that scope in the current conversation.
- Ambiguous wording (for example "这个仓库", "当前项目", "the repo") is resolved to `/home/ubuntu/zeroclaw` by default.
- Context mentioning external repositories does not authorize cross-repo edits; explicit current-turn override is required.
- Before any repo-affecting action, verify target lock (`pwd` + git root) to prevent accidental execution in sibling repositories.
## 0.1) Clean Worktree First Gate (Mandatory)
- Before handling any repository content (analysis, debugging, coding, tests, docs, CI), create a **new clean dedicated git worktree** for the active task.
- Do not perform substantive task work in a dirty workspace.
- Do not reuse a previously dirty worktree for a new task track.
- If the current location is dirty, stop and bootstrap a clean worktree/branch first.
- If worktree bootstrap fails, stop and report the blocker; do not continue in-place.
## 1) Project Snapshot (Read First)
ZeroClaw is a Rust-first autonomous agent runtime optimized for:
@@ -240,8 +256,8 @@ All contributors (human or agent) must follow the same collaboration flow:
- Create and work from a non-`main` branch.
- Commit changes to that branch with clear, scoped commit messages.
- Open a PR to `dev`; do not push directly to `dev` or `main`.
-`main`is reserved for release promotion PRs from `dev`.
- Open a PR to `main` by default (`dev` is optional for integration batching); do not push directly to `dev` or `main`.
-`main`accepts direct PR merges after required checks and review policy pass.
- Wait for required checks and review outcomes before merging.
- Merge via PR controls (squash/rebase/merge as repository policy allows).
- After merge/close, clean up task branches/worktrees that are no longer needed.
@@ -251,7 +267,7 @@ All contributors (human or agent) must follow the same collaboration flow:
- Decide merge/close outcomes from repository-local authority in this order: `.github/workflows/**`, GitHub branch protection/rulesets, `docs/pr-workflow.md`, then this `AGENTS.md`.
- External agent skills/templates are execution aids only; they must not override repository-local policy.
- A normal contributor PR targeting `main` is a routing defect, not by itself a closure reason; if intent and content are legitimate, retarget to `dev`.
- A normal contributor PR targeting `main` is valid under the main-first flow when required checks and review policy are satisfied; use `dev` only for explicit integration batching.
- Direct-close the PR (do not supersede/replay) when high-confidence integrity-risk signals exist:
- unapproved or unrelated repository rebranding attempts (for example replacing project logo/identity assets)
- unauthorized platform-surface expansion (for example introducing `web` apps, dashboards, frontend stacks, or UI surfaces not requested by maintainers)
@@ -350,7 +366,6 @@ Use these rules to keep the trait/factory architecture stable under growth.
- Apply `docs/i18n-guide.md` completion checklist before merge and include i18n status in PR notes.
- For docs snapshots, add new date-stamped files for new sprints rather than rewriting historical context.
@@ -240,8 +240,8 @@ All contributors (human or agent) must follow the same collaboration flow:
- Create and work from a non-`main` branch.
- Commit changes to that branch with clear, scoped commit messages.
- Open a PR to `dev`; do not push directly to `dev` or `main`.
-`main`is reserved for release promotion PRs from `dev`.
- Open a PR to `main` by default (`dev` is optional for integration batching); do not push directly to `dev` or `main`.
-`main`accepts direct PR merges after required checks and review policy pass.
- Wait for required checks and review outcomes before merging.
- Merge via PR controls (squash/rebase/merge as repository policy allows).
- After merge/close, clean up task branches/worktrees that are no longer needed.
@@ -251,7 +251,7 @@ All contributors (human or agent) must follow the same collaboration flow:
- Decide merge/close outcomes from repository-local authority in this order: `.github/workflows/**`, GitHub branch protection/rulesets, `docs/pr-workflow.md`, then this `CLAUDE.md`.
- External agent skills/templates are execution aids only; they must not override repository-local policy.
- A normal contributor PR targeting `main` is a routing defect, not by itself a closure reason; if intent and content are legitimate, retarget to `dev`.
- A normal contributor PR targeting `main` is valid under the main-first flow when required checks and review policy are satisfied; use `dev` only for explicit integration batching.
- Direct-close the PR (do not supersede/replay) when high-confidence integrity-risk signals exist:
- unapproved or unrelated repository rebranding attempts (for example replacing project logo/identity assets)
- unauthorized platform-surface expansion (for example introducing `web` apps, dashboards, frontend stacks, or UI surfaces not requested by maintainers)
@@ -350,7 +350,6 @@ Use these rules to keep the trait/factory architecture stable under growth.
- Apply `docs/i18n-guide.md` completion checklist before merge and include i18n status in PR notes.
- For docs snapshots, add new date-stamped files for new sprints rather than rewriting historical context.
This PR implements the Android client for ZeroClaw with full agent integration, including foreground service, Quick Settings tile, boot receiver, and background heartbeat support.
### Changes
-`ZeroClawApp.kt` - Application setup with notification channels and WorkManager
-`SettingsRepository.kt` - DataStore + EncryptedSharedPreferences for secure settings
-`SettingsScreen.kt` - Compose UI for configuring the agent
-`BootReceiver.kt` - Auto-start on boot when enabled
-`HeartbeatWorker.kt` - Background periodic tasks via WorkManager
-`ZeroClawTileService.kt` - Quick Settings tile for agent control
-`ShareHandler.kt` - Handle content shared from other apps
-`ci-android.yml` - GitHub Actions workflow for Android builds
-`proguard-rules.pro` - R8 optimization rules
---
## Validation Evidence
- [x] All HIGH and MEDIUM CodeRabbit issues addressed
- [x] DataStore IOException handling added to prevent crashes on corrupted preferences
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.