Fallback providers in create_resilient_provider_with_options() were
created via create_provider_with_options() which passed the primary
provider's api_key as credential_override. This caused
resolve_provider_credential() to short-circuit on the override and
never check the fallback provider's own env var (e.g. DEEPSEEK_API_KEY
for a deepseek fallback), resulting in auth failures (401) when the
primary and fallback use different API services.
Switch to create_provider_with_url(fallback, None, None) so each
fallback resolves its own credential via provider-specific env vars.
This also enables custom: URL prefixes (e.g.
custom:http://host.docker.internal:1234/v1) to work as fallback
entries, which was previously impossible through the options path.
Add three focused tests covering independent credential resolution,
custom URL fallbacks, and mixed fallback chains.
Three related agent UX issues found during MiniMax channel testing:
1. DateTimeSection injected only timezone, not the actual date/time.
Models have no reliable way to know the current date from training
data alone, causing wrong or hallucinated dates in responses.
Fix: include full timestamp (YYYY-MM-DD HH:MM:SS TZ) in the prompt.
2. The `date` shell command was absent from the security policy
allowed_commands default list. When a model tried to call
shell("date") to get the current time, it received a policy
rejection and told the user it was "blocked by security policy".
Fix: add "date" to the default allowed_commands list. The command
is read-only, side-effect-free, and carries no security risk.
3. (Context) The datetime prompt fix makes the date command fallback
largely unnecessary, but the allowlist addition ensures the tool
works correctly if models choose to call it anyway.
Non-goals:
- Not changing the autonomy model or risk classification
- Not adding new config keys
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MiniMax API rejects role: system in the messages array with error
2013 (invalid message role: system). In channel mode, the history
builder prepends a system message and optionally appends a second
one for delivery instructions, causing 400 errors on every channel
turn.
Additionally, MiniMax reasoning models embed chain-of-thought in
the content field as <think>...</think> blocks rather than using
the separate reasoning_content field, causing raw thinking output
to leak into user-visible responses.
Changes:
- Add merge_system_into_user flag to OpenAiCompatibleProvider;
when set, all system messages are concatenated and prepended to
the first user message before sending to the API
- Add new_merge_system_into_user() constructor used by MiniMax
- Add strip_think_tags() helper that removes <think>...</think>
blocks from response content before returning to the caller
- Apply strip_think_tags in effective_content() and
effective_content_optional() so all non-streaming paths are covered
- Update MiniMax factory registration to use new_merge_system_into_user
- Fix pre-existing rustfmt violation on apply_auth_header call
All other providers continue to use the default path unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a `config schema` subcommand that dumps the full configuration
schema as JSON Schema (draft 2020-12) to stdout. This enables
downstream consumers (like PankoAgent) to programmatically validate
configs, generate forms, and stay in sync with zeroclaw's evolving
config surface without hand-maintaining copies of the schema.
- Add schemars 1.2 dependency and derive JsonSchema on all config
structs/enums (schema.rs, policy.rs, email_channel.rs)
- Add `Config` subcommand group with `Schema` sub-command
- Output is valid JSON Schema with $defs for all 56 config types
Remove early return in IdentitySection::build() that caused AGENTS.md,
SOUL.md, and other workspace files to be silently skipped when AIEOS
identity loaded successfully. Both AIEOS identity and workspace files
now coexist in the system prompt.
Closeszeroclaw-labs/zeroclaw#856
Co-Authored-By: Kristofer Mondlane <kmondlane@gmail.com>
Privacy-enabled Signal users have no sourceNumber, so sender()
falls back to their UUID from the source field. Previously
parse_recipient_target() treated non-E.164 strings without the
group: prefix as group IDs, causing signal-cli to reject the
UUID as an invalid base64 group ID.
Add is_uuid() helper using the already-imported uuid crate and
recognise valid UUIDs as Direct targets alongside E.164 numbers.
Add Update variant to CronCommands in both main.rs and lib.rs, with
handler in cron/mod.rs that constructs a CronJobPatch and calls
update_job(). Includes security policy check for command changes.
Fixes from review feedback:
- --tz alone now correctly updates timezone (fetches existing schedule)
- --expression alone preserves existing timezone instead of clearing it
- All-None patch (no flags) now returns an error
- Output uses consistent emoji prefix
Tests exercise handle_command directly to cover schedule construction.
Closes#809
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OpenAiProvider overrode chat() with native tool support but never
overrode chat_with_tools(), which is the method called by
run_tool_call_loop in channel mode (IRC/Discord/etc). The trait default
for chat_with_tools() silently drops the tools parameter, sending plain
ChatRequest with no tools — causing the model to never use native tool
calls in channel mode.
Add chat_with_tools() override that deserializes tool specs, uses
convert_messages() for proper tool_call_id handling, and sends
NativeChatRequest with tools and tool_choice.
Also add Deserialize derive to NativeToolSpec and NativeToolFunctionSpec
to support deserialization from OpenAI-format JSON.
The existing iMessage channel relies on AppleScript and only works on macOS.
Linq provides a REST API for iMessage, RCS, and SMS — this gives ZeroClaw
native iMessage support on any platform via webhooks.
Implements LinqChannel following the same patterns as WhatsAppChannel:
- Channel trait impl (send, listen, health_check, typing indicators)
- Webhook handler with HMAC-SHA256 signature verification
- Sender allowlist filtering
- Onboarding wizard step with connection testing
- 18 unit tests covering parsing, auth, and signature verification
Resolves#656 — the prior issue was closed without a merged PR, so this
is the actual implementation.
Add configurable timeout for processing channel messages (LLM + tools).
Default: 300s (optimized for on-device LLMs like Ollama).
Can be overridden in config.toml:
[channels_config]
message_timeout_secs = 600
- pr-auto-response.yml: restore permissions, steps, and checkout in
contributor-tier-issues job (broken by runner swap)
- pr-check-stale.yml: restore steps block and step name
- pr-intake-checks.yml: restore steps block, checkout, and timeout
- pr-check-status.yml: revert STALE_HOURS from 4 to 48 (not a cost
optimization; 4h is too aggressive), switch to ubuntu-latest per
PR description
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pushover tool priority parameter schema used integer enum values
[-2, -1, 0, 1, 2]. OpenAI-compatible APIs accept this, but the Gemini
API (and Gemini-relay proxies) strictly require all enum values to be
strings, rejecting the request with 400 Bad Request.
This causes every agent turn to fail with a non_retryable error when
using Gemini models, regardless of user message content, because tool
schemas are included in every request.
Fix: remove the enum constraint, keeping integer type and description
documenting the valid range. This is valid for both OpenAI and Gemini
providers and requires no changes to execute() which already uses
as_i64() with range validation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hard-coded string literals used as cryptographic keys/secrets in
gateway webhook and WhatsApp signature verification tests with runtime-
generated random values. This resolves CodeQL rust/hard-coded-cryptographic-value
alerts while maintaining identical test coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add URL scheme validation before HTTP requests that transmit sensitive
data (account IDs, phone numbers, user IDs). All endpoints already use
HTTPS URLs, but this explicit check satisfies CodeQL rust/cleartext-
transmission analysis and prevents future regressions if URLs are
changed.
Affected files: composio.rs, whatsapp.rs, qq.rs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>