* feat(tools): add Firecrawl fallback for JS-heavy and bot-blocked sites
Adds optional Firecrawl API integration as a fallback when standard web
fetching fails due to JavaScript-heavy pages or bot protection. Includes
configurable API key, timeout, and domain allowlist/blocklist.
Original PR #4244 by rareba.
* ci: retrigger CI
* feat(tools): add LLM task tool for structured JSON-only sub-calls
Add LlmTaskTool — a lightweight tool that runs a single prompt through
an LLM provider with no tool access and optionally validates the
response against a caller-supplied JSON Schema. Ideal for structured
data extraction, classification, and transformation in workflows.
- Parameters: prompt (required), schema (optional JSON Schema),
model (optional override), temperature (optional override)
- Uses configured default provider/model from root config
- Validates response JSON against schema (required fields, type checks)
- Strips markdown code fences from LLM responses before validation
- Gated by ToolOperation::Act security policy
- Registered in all_tools_with_runtime (always available)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use non-constant value instead of approximate PI in tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: rareba <rareba@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skill tools defined in [[tools]] sections are now registered as first-class
callable tool specs via the Tool trait, rather than only appearing as XML
in the system prompt. This enables the LLM to invoke skill tools through
native function calling.
- Add SkillShellTool for shell/script kind skill tools
- Add SkillHttpTool for http kind skill tools
- Add skills_to_tools() conversion and register_skill_tools() wiring
- Wire registration into both CLI and process_message agent paths
- Update prompt rendering to mark registered tools as callable
- Update affected tests across skills, agent/prompt, and channels
* feat: add discord history logging and search tool with persistent channel cache
* fix: remove unused channel_names field from DiscordHistoryChannel
The channel_names HashMap was declared and initialized but never used.
Channel name caching is handled via discord_memory.get()/store() with
the cache:channel_name: prefix. Remove the dead field.
* style: run cargo fmt on discord_history.rs
---------
Co-authored-by: ninenox <nisit15@hotmail.com>
* feat(gateway): add Live Canvas (A2UI) tool and real-time web viewer
Add a Live Canvas system that enables the agent to push rendered content
(HTML, SVG, Markdown, text) to a web-visible canvas in real time.
Backend:
- src/tools/canvas.rs: CanvasTool with render/snapshot/clear/eval actions,
backed by a shared CanvasStore (Arc<RwLock<HashMap>>) with per-canvas
broadcast channels for real-time updates
- src/gateway/canvas.rs: REST endpoints (GET/POST/DELETE /api/canvas/:id,
GET /api/canvas/:id/history, GET /api/canvas) and WebSocket endpoint
(WS /ws/canvas/:id) for real-time frame delivery
Frontend:
- web/src/pages/Canvas.tsx: Canvas viewer page with WebSocket connection,
iframe sandbox rendering, canvas switcher, frame history panel
Registration:
- CanvasTool registered in all_tools_with_runtime (always available)
- Canvas routes wired into gateway router
- CanvasStore added to AppState
- Canvas page added to App.tsx router and Sidebar navigation
- i18n keys added for en/zh/tr locales
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(config): fix pre-existing test compilation errors in schema.rs
- Remove #[cfg(unix)] gate on `use tempfile::TempDir` import since
TempDir is used unconditionally in bootstrap file tests
- Add explicit type annotations on tokio::fs::* calls to resolve
type inference failures (create_dir_all, write, read_to_string)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(gateway): share CanvasStore between tool and REST API
The CanvasTool and gateway AppState each created their own CanvasStore,
so content rendered via the tool never appeared in the REST API.
Create the CanvasStore once in the gateway, pass it to
all_tools_with_runtime via a new optional parameter, and reuse the
same instance in AppState.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(gateway): address critical security and reliability bugs in Live Canvas
- Validate content_type in REST POST endpoint against allowed set,
preventing injection of "eval" frames via the REST API
- Enforce MAX_CONTENT_SIZE (256KB) limit on REST POST endpoint,
matching tool-side validation to prevent memory exhaustion
- Add MAX_CANVAS_COUNT (100) limit to prevent unbounded canvas creation
and memory exhaustion from CanvasStore
- Handle broadcast RecvError::Lagged in WebSocket handler gracefully
instead of disconnecting the client
- Make MAX_CONTENT_SIZE and ALLOWED_CONTENT_TYPES pub for gateway reuse
- Update CanvasStore::render and subscribe to return Option for
canvas count enforcement
---------
Co-authored-by: Giulio V <vannini.gv@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: rareba <rareba@users.noreply.github.com>
- Replace manual can_act()/record_action() with enforce_tool_operation()
to match the codebase convention used by all other tools (notion,
memory_forget, claude_code, delegate, etc.), producing consistent
error messages and avoiding logic duplication.
- Add model parameter validation to prevent URL path traversal attacks
via crafted model identifiers (e.g. "../../evil-endpoint").
- Add tests for model traversal rejection and filename sanitization.
SessionsSendTool was missing security gate enforcement entirely - any agent
could send messages to any session without security policy checks. Similarly,
SessionsHistoryTool had no security enforcement for reading session data.
Changes:
- Add SecurityPolicy field to SessionsHistoryTool (enforces ToolOperation::Read)
- Add SecurityPolicy field to SessionsSendTool (enforces ToolOperation::Act)
- Add session_id validation to reject empty or non-alphanumeric-only IDs
- Pass security policy from all_tools_with_runtime registration
- Add tests for empty session_id, non-alphanumeric session_id validation
The reaction tool was passing the channel adapter name (e.g. "discord",
"slack") as the first argument to Channel::add_reaction() and
Channel::remove_reaction(), but the trait signature expects a
platform-specific channel_id (e.g. Discord channel snowflake, Slack
channel ID like "C0123ABCD"). This would cause all reaction API calls
to fail at the platform level.
Fixes:
- Add required "channel_id" parameter to the tool schema
- Extract and pass channel_id (not channel_name) to trait methods
- Update tool description to mention the new parameter
- Add MockChannel channel_id capture for test verification
- Add test asserting channel_id (not name) reaches the trait
- Update all existing tests to supply channel_id
The output format used "{action}ed" which produced "removeed" for the
remove action. Use explicit past-tense mapping instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ImageGenTool that exposes fal.ai Flux model image generation as a
standalone tool, decoupled from the LinkedIn client. The tool accepts a
text prompt, optional filename/size/model parameters, calls the fal.ai
synchronous API, downloads the result, and saves to workspace/images/.
- New src/tools/image_gen.rs with full Tool trait implementation
- New ImageGenConfig in schema.rs (enabled, default_model, api_key_env)
- Config-gated registration in all_tools_with_runtime
- Security: checks can_act() and record_action() before execution
- Comprehensive unit tests (prompt validation, API key, size enum,
autonomy blocking, tool spec)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ReactionTool that exposes Channel::add_reaction and
Channel::remove_reaction as an agent-callable tool. Uses a
late-binding ChannelMapHandle (Arc<RwLock<HashMap>>) pattern
so the tool can be constructed during tool registry init and
populated once channels are available in start_channels.
Parameters: channel, message_id, emoji, action (add/remove).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three new tools in src/tools/sessions.rs:
- sessions_list: lists active sessions with channel, message count, last activity
- sessions_history: reads last N messages from a session by ID
- sessions_send: appends a message to a session for inter-agent communication
All tools operate on the SessionBackend trait, using the JSONL SessionStore
by default. Registered unconditionally in all_tools_with_runtime when the
sessions directory is accessible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(config): add configurable pacing controls for slow/local LLM workloads (#2963)
Add a new `[pacing]` config section with four opt-in parameters that
let users tune timeout and loop-detection behavior for local LLMs
(Ollama, llama.cpp, vLLM) without disabling safety features entirely:
- `step_timeout_secs`: per-step LLM inference timeout independent of
the overall message budget, catching hung model responses early.
- `loop_detection_min_elapsed_secs`: time-gated loop detection that
only activates after a configurable grace period, avoiding false
positives on long-running browser/research workflows.
- `loop_ignore_tools`: per-tool loop-detection exclusions so tools
like `browser_screenshot` that structurally resemble loops are not
counted toward identical-output detection.
- `message_timeout_scale_max`: overrides the hardcoded 4x ceiling in
the channel message timeout scaling formula.
All parameters are strictly optional with no effect when absent,
preserving full backwards compatibility.
Closes#2963
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(config): add missing pacing fields in tests and call sites
* fix(config): add pacing arg to remaining cost-tracking test call sites
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: argenis de la rosa <theonlyhennygod@gmail.com>
* feat(tool): enrich delegate sub-agent system prompt and add skills_directory config key (#3046)
Sub-agents configured under [agents.<name>] previously received only the
bare system_prompt string. They now receive a structured system prompt
containing: tools section (allowed tools with parameters and invocation
protocol), skills section (from scoped or default directory), workspace
path, current date/time, safety constraints, and shell policy when shell
is in the effective tool list.
Add optional skills_directory field to DelegateAgentConfig for per-agent
scoped skill loading. When unset, falls back to default workspace
skills/ directory.
Closes#3046
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tools): add missing fields after rebase
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: argenis de la rosa <theonlyhennygod@gmail.com>
Adopted from #3705 by @fangxueshun with fixes:
- Added input validation for date strings (RFC 3339)
- Used chrono DateTime comparison instead of string comparison
- Added since < until validation
- Updated mem0 backend
Supersedes #3705
Fixes E0382 borrow-after-move error: wait_with_output() consumed the
child handle, making child.kill() in the timeout branch invalid.
Use kill_on_drop(true) with cmd.output() instead.
Implements the Weather integration as a native Rust Tool trait
implementation, consistent with the existing first-party tool
architecture (no WASM/plugin layer required).
- Add src/tools/weather_tool.rs with full WeatherTool impl
- Fetches from wttr.in ?format=j1 (no API key, global coverage)
- Supports city names (any language/script), IATA airport codes,
GPS coordinates, postal/zip codes, domain-based geolocation
- Metric (°C, km/h, mm) and imperial (°F, mph, in) units
- Current conditions + 0-3 day forecast with hourly breakdown
- Graceful error messages for unknown/invalid locations
- Respects runtime proxy config via apply_runtime_proxy_to_builder
- 36 unit tests: schema, URL building, param validation, formatting
- Register WeatherTool unconditionally in all_tools_with_runtime
(no API key needed, no config gate — same pattern as CalculatorTool)
- Flip integrations registry Weather entry from ComingSoon to Available
Closes #<issue>
Resolves merge conflicts from PR #4064. Uses typed DeliveryConfig in
CronAddBody and passes delivery directly to add_shell_job_with_approval
and add_agent_job instead of post-creation patching. Preserves master's
richer API fields (session_target, model, allowed_tools, delete_after_run).
* Sync jira tool description between .rs and en.toml
Replace multi-line operational guide in en.toml with the same one-liner
already in jira_tool.rs description(), matching the pattern used by all
other tools where both sources are in sync.
* Add myself action to jira tool for credential verification
* Add tests for myself action in jira tool
* Review and fix list_projects action added to jira tool
- Fix doc comment: update action count from four to five and add missing
myself entry
- Remove redundant statuses_url variable (was identical to url)
The list_projects action fetches all projects with their issue types,
statuses, and assignable users by combining /rest/api/3/project,
per-project /statuses, and /user/assignable/multiProjectSearch endpoints.
* Remove hardcoded project-specific statuses from shape_projects
Replace fixed known_order list (which included project-specific statuses
like 'Collecting Intel', 'Design', 'Verification') with a simple
alphabetical sort. Any Jira project can use arbitrary status names so
hardcoding an order is not applicable universally.
* Fix list_projects: bounded concurrency, error surfacing, and output shape
- Use tokio::task::JoinSet with STATUS_CONCURRENCY=5 to fetch per-project
statuses concurrently instead of sequentially, bounding API blast radius
- Surface user fetch errors: non-2xx and JSON parse failures now bail
instead of silently falling back to empty vec
- Surface per-project status JSON parse errors instead of swallowing them
with unwrap_or_else
- Move users to top-level output {projects, users} so they are not
duplicated across every project entry
* fix(tool): apply rustfmt formatting to jira_tool.rs
Add full rich media send/receive support using unified [TYPE:target] markers
(aligned with Telegram). Register QQ as a cron announcement delivery channel.
- Media upload with SHA256-based caching and TTL
- Attachment download to workspace with all types supported
- Voice: prefer voice_wav_url (WAV), inject QQ ASR transcription
- File uploads include file_name for proper display in QQ client
- msg_seq generation and reply rate-limit tracking
- QQ delivery instructions in system prompt
- Register QQ in cron scheduler and tool description
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(config): add google workspace operation allowlists
* docs(superpowers): link google workspace operation inventory sources
* docs(superpowers): verify starter operation examples
* fix(google_workspace): remove duplicate credential/audit blocks, fix trim in allowlist check, add duplicate-methods test
- Remove the duplicated credentials_path, default_account, and audit_log
blocks that were copy-pasted into execute() — they were idempotent but
misleading and would double-append --account args on every call.
- Trim stored service/resource/method values in is_operation_allowed() to
match the trim applied during Config::validate(), preventing a mismatch
where a config entry with surrounding whitespace would pass validation but
never match at runtime.
- Add google_workspace_allowed_operations_reject_duplicate_methods_within_entry
test to cover the duplicate-method validation path that was implemented but
untested.
* fix(google_workspace): close sub_resource bypass, trim allowed_services at runtime, mark spec implemented
- HIGH: extract and validate sub_resource before the allowlist check;
is_operation_allowed() now accepts Option<&str> for sub_resource and
returns false (fail-closed) when allowed_operations is non-empty and
a sub_resource is present — prevents nested gws calls such as
`drive/files/permissions/list` from slipping past a 3-segment policy
- MEDIUM: runtime allowed_services check now uses s.trim() == service,
matching the trim() applied during config validation
- LOW: spec status updated to Implemented; stale "does not currently
support method-level allowlists" line removed
- Added test: operation_allowlist_rejects_sub_resource_when_operations_configured
* docs(google_workspace): document sub_resource limitation and add config-reference entries
Spec updates (superpowers/specs):
- Semantics section: note that sub_resource calls are denied fail-closed when
allowed_operations is configured
- Mental model: show both 3-segment and 4-segment gws command shapes; explain
that 4-segment commands are unsupported with allowed_operations in this version
- Runtime enforcement: correct the validation order to match the implementation
(sub_resource extracted before allowlist check, budget charged last)
- New section: Sub-Resource Limitation — documents impact, operator workaround,
and confirms the deny is intentional for this slice
- Follow-On Work: add sub_resource config model extension as item 1
Config reference updates (all three locales):
- Add [google_workspace] section with top-level keys, [[allowed_operations]]
sub-table, sub-resource limitation note, and TOML example
* fix(docs): add classroom and events to allowed_services list in all config-reference locales
* feat(google_workspace): extend allowed_operations to support sub_resource for 4-segment gws commands
All Gmail operations use gws gmail users <sub_resource> <method>, not the flat
3-segment shape. Without sub_resource support in allowed_operations, Gmail could
not be scoped at all, making the email-assistant use case impossible.
Config model:
- Add optional sub_resource field to GoogleWorkspaceAllowedOperation
- An entry without sub_resource matches 3-segment calls (Drive, Calendar, etc.)
- An entry with sub_resource matches only calls with that exact sub_resource value
- Duplicate detection updated to (service, resource, sub_resource) key
Runtime:
- Remove blanket sub_resource deny; is_operation_allowed now matches on all four
dimensions including the optional sub_resource
Tests:
- Add operation_allowlist_matches_gmail_sub_resource_shape
- Add operation_allowlist_matches_drive_3_segment_shape
- Add rejects_operation_with_unlisted_sub_resource
- Add google_workspace_allowed_operations_allow_same_resource_different_sub_resource
- Add google_workspace_allowed_operations_reject_invalid_sub_resource_characters
- Add google_workspace_allowed_operations_deserialize_without_sub_resource
- Update all existing tests to use correct gws command shapes
Docs:
- Spec: correct Gmail examples throughout; remove Sub-Resource Limitation section;
update data model, validation rules, example use case, and follow-on work
- Config-reference (en, vi, zh-CN): add sub_resource field to allowed_operations
table; update Gmail examples to correct 4-segment shapes
Platform:
- email-assistant SKILL.md: update allowed_operations paths to gmail/users/* shape
* fix(google_workspace): add classroom and events to service parameter schema description
* fix(google_workspace): cross-validate allowed_operations service against allowed_services
When allowed_services is explicitly configured, each allowed_operations entry's
service must appear in that list. An entry that can never match at runtime is a
misconfigured policy: it looks valid but silently produces a narrower scope than
the operator intended. Validation now rejects it with a clear error message.
Scope: only applies when allowed_services is non-empty. When it is empty, the tool
uses a built-in default list defined in the tool layer; the validator cannot
enumerate that list without duplicating the constant, so the cross-check is skipped.
Also:
- Update allowed_operations field doc-comment from 3-part (service, resource, method)
to 4-part (service, resource, sub_resource, method) model
- Soften Gmail sub_resource "required" language in config-reference (en, vi, zh-CN)
from a validation requirement to a runtime matching requirement — the validator
does not and should not hardcode API shape knowledge for individual services
- Add tests: rejects operation service not in allowed_services; skips cross-check
when allowed_services is empty
* fix(google_workspace): cross-validate allowed_operations.service against effective service set
When allowed_services is empty the validator was silently skipping the
service cross-check, allowing impossible configs like an unlisted service
in allowed_operations to pass validation and only fail at runtime.
Move DEFAULT_GWS_SERVICES from the tool layer (google_workspace.rs) into
schema.rs so the validator can use it unconditionally. When allowed_services
is explicitly set, validate against that set; when empty, fall back to
DEFAULT_GWS_SERVICES. Remove the now-incorrect "skips cross-check when empty"
test and add two replacement tests: one confirming a valid default service
passes, one confirming an unknown service is rejected even with empty
allowed_services.
* fix(google_workspace): update test assertion for new error message wording
* docs(google_workspace): fix stale 3-segment gmail example in TDD plan
* fix(google_workspace): address adversarial review round 4 findings
- Error message for denied operations now includes sub_resource when
present, so gmail/users/messages/send and gmail/users/drafts/send
produce distinct, debuggable errors.
- Audit log now records sub_resource, completing the trail for 4-segment
Gmail operations.
- Normalize (trim) allowed_services and allowed_operations fields at
construction time in new(). Runtime comparisons now use plain equality
instead of .trim() on every call, removing the latent defect where a
future code path could forget to trim and silently fail to match.
- Unify runtime character validation with schema validation: sub_resource
and service/resource/method checks now both require lowercase alphanumeric
plus underscore and hyphen, matching the validator's character set.
- Add positional_cmd_args() test helper and tests verifying 3-segment
(Drive) and 4-segment (Gmail) argument ordering.
- Add test confirming page_limit without page_all passes validation.
- Add test confirming whitespace in config values is normalized at
construction, not deferred to comparison time.
- Fix spec Runtime Enforcement section to reflect actual code order.
* fix(google_workspace): wire production helpers to close test coverage gaps
- Remove #[cfg(test)] from positional_cmd_args; execute() now calls the
same function the arg-ordering tests exercise, so a drift in the real
command-building path is caught by the existing tests.
- Extract build_pagination_args(page_all, page_limit) as a production
method used by execute(). Replace the brittle page_limit_without_page_all
test (which relied on environment-specific execution failure wording)
with four direct assertions on build_pagination_args covering all
page_all/page_limit combinations.
---------
Co-authored-by: argenis de la rosa <theonlyhennygod@gmail.com>
* feat(tools): add text browser tool for headless environments (#3879)
* fix(tools): remove redundant match arm in text_browser clippy lint
* ci: trigger fresh workflow run
Add tool descriptions alongside names in the deferred tools section so
the LLM can better identify which tool to activate via tool_search.
Original author: @mark-linyb
* feat(delegate): make delegate timeout configurable via config.toml
Add configurable timeout options for delegate tool:
- timeout_secs: for non-agentic sub-agent calls (default: 120s)
- agentic_timeout_secs: for agentic sub-agent runs (default: 300s)
Previously these were hardcoded constants (DELEGATE_TIMEOUT_SECS
and DELEGATE_AGENTIC_TIMEOUT_SECS). Users can now customize them
in config.toml under [[delegate.agents]] section.
Fixes#3898
* feat(config): make delegate tool timeouts configurable via config.toml
This change makes the hardcoded 120s/300s delegate tool timeouts
configurable through the config file:
- Add [delegate] section to Config with timeout_secs and agentic_timeout_secs
- Add DelegateToolConfig struct for global default timeout values
- Add DEFAULT_DELEGATE_TIMEOUT_SECS (120) and DEFAULT_DELEGATE_AGENTIC_TIMEOUT_SECS (300) constants
- Remove hardcoded constants from delegate.rs
- Update tests to use constant values instead of magic numbers
- Update examples/config.example.toml with documentation
Closes#3898
* fix: keep delegate timeout fields as Option<u64> with global fallback
- Change DelegateAgentConfig.timeout_secs and agentic_timeout_secs from
u64 to Option<u64> so per-agent overrides are truly optional
- Implement manual Default for DelegateToolConfig with correct values
(120s and 300s) instead of derive(Default)
- Add DelegateToolConfig to DelegateTool struct and wire through
constructors so agent timeouts fall back to global [delegate] config
- Add validation for delegate timeout values in Config::validate()
- Fix example config to use [agents.name] table syntax matching the
HashMap<String, DelegateAgentConfig> schema
- Add missing timeout fields to all DelegateAgentConfig struct literals
across codebase (doctor, swarm, model_routing_config, tools/mod)
* chore: trigger CI
* chore: retrigger CI
* fix: cargo fmt line wrapping in config/mod.rs
* fix: import timeout constants in delegate tests
* style: cargo fmt
---------
Co-authored-by: vincent067 <vincent067@outlook.com>
* feat: add Jira tool with get_ticket, search_tickets, and comment_ticket
Implements a new `jira` tool following the existing zeroclaw tool
conventions (Tool trait, SecurityPolicy, config-gated registration).
- get_ticket: configurable detail level (basic/basic_search/full/changelog)
with response shaping; always in the default allowed_actions list
- search_tickets: JQL-based search with cursor pagination (nextPageToken);
always returns basic_search shape; gated by allowed_actions
- comment_ticket: posts ADF comments with inline markdown-like syntax —
@email mentions resolved to Jira accountId, **bold**, bullet lists,
newlines; gated by allowed_actions and SecurityPolicy Act operation
Config: [jira] section with base_url, email, api_token (encrypted at
rest, falls back to JIRA_API_TOKEN env var), allowed_actions (default:
["get_ticket"]), and timeout_secs. Validated on load.
Tool description in tool_descriptions/en.toml documents all three
actions and the full comment syntax for the AI system prompt.
* fix: address jira tool code review findings
High priority:
- Validate issue_key against ^[A-Z][A-Z0-9]+-\d+$ before URL interpolation
to prevent path traversal in get_ticket and comment_ticket
Medium priority:
- Add email guard in tool registration (mod.rs) to skip with a warning
instead of registering a broken tool when jira.email is empty
- Shape comment_ticket response to return only id, author, created —
avoids exposing internal Jira metadata to the AI
- Replace O(n²) comment matching in shape_basic with a HashMap lookup
keyed by comment ID for O(1) access
- Add api_token validation in Config::validate() checking both the
config field and JIRA_API_TOKEN env var when jira.enabled = true
Low priority:
- Make shape_basic_search private (was accidentally pub)
- Extend clean_email to strip leading punctuation (( and [) so that
@(john@co.com) resolves correctly; fix suffix computation via pointer
arithmetic to handle the shifted offset
- Clarify tool_descriptions/en.toml: @prefix is required for mentions,
bare emails without @ are treated as plain text
- Handle unmatched ** in parse_inline: emit as literal text instead of
silently producing a bold node with no closing marker
* fix(jira): allow lowercase project keys in issue_key validation
Relax validate_issue_key to accept both PROJ-123 and proj-123, since
some Jira instances use lowercase custom project keys. Path traversal
protection is preserved via alphanumeric + digit-number requirement.
* feat(tools): add tool honesty instructions to system prompt
Prevent AI from fabricating tool results by injecting a CRITICAL:
Tool Honesty section into both channel and CLI/agent system prompts.
Rules: never fabricate or guess tool results, report errors as-is,
and ask the user when unsure if a tool call succeeded.
* style: sort JiraConfig import alphabetically in config/mod.rs
* style(jira): fix strict clippy lints in jira_tool
- Derive Default for LevelOfDetails instead of manual impl
- Use char arrays in trim_start_matches/trim_end_matches
- Allow cast_possible_truncation on search_tickets (usize->u32 bounded by max_results)
- Remove needless borrow on &email
* fix(ci): adapt to upstream autonomy_level additions in channels/mod.rs
- Add missing autonomy_level argument to build_system_prompt_with_mode call in test
- Add missing autonomy_level field in ChannelRuntimeContext test initializer
- Allow large_futures in load_or_init test (Config struct growth from JiraConfig)
* fix(ci): resolve duplicate and missing autonomy_level in test initializers
* fix(ci): use TelegramRecordingChannel in telegram-specific test
The test process_channel_message_executes_tool_calls_instead_of_sending_raw_json
sent messages on channel "telegram" but registered RecordingChannel (name:
"test-channel"), causing the channel lookup to return None and no messages to
be sent.
* fix(jira): prevent panics on short dates, fix dedup bug, normalize base_url
- Add date_prefix() helper to safely slice date strings instead of
panicking on empty or short strings from the Jira API.
- Replace Vec::dedup() with HashSet-based retain in extract_emails()
to correctly deduplicate non-adjacent duplicates.
- Strip trailing slashes from base_url during construction to prevent
double-slash URLs.
- Add tests for date_prefix and non-adjacent email dedup.
---------
Co-authored-by: Anatolii <anatolii@Anatoliis-MacBook.local>
Co-authored-by: Anatolii <anatolii.fesiuk@gmail.com>
Persist allowed_tools in cron_jobs table, threading it through CLI add/update and cron_add/cron_update tool APIs. Add regression coverage for store, tool, and CLI roundtrip paths.
Fixups over original PR #3929: add allowed_tools to all_overdue_jobs SELECT (merge gap), resolve merge conflicts.
Closes#3920
Supersedes #3929
* fix: add sandbox field to ShellTool struct
Add `sandbox: Arc<dyn Sandbox>` field to `ShellTool` and a
`new_with_sandbox()` constructor so callers can inject the configured
sandbox backend. The existing `new()` constructor defaults to
`NoopSandbox` for backward compatibility.
Ref: #3983
* fix: apply sandbox wrapping in ShellTool::execute()
Call `self.sandbox.wrap_command()` on the underlying std::process::Command
(via `as_std_mut()`) after building the shell command and before clearing
the environment. This ensures every shell command passes through the
configured sandbox backend before execution.
Ref: #3983
* fix: wire up sandbox creation at ShellTool callsites
In `all_tools_with_runtime()`, create a sandbox from
`root_config.security` via `create_sandbox()` and pass it to
`ShellTool::new_with_sandbox()`. The `default_tools_with_runtime()`
path retains `ShellTool::new()` which defaults to `NoopSandbox`.
Ref: #3983
* test: add sandbox integration tests for ShellTool
Verify that ShellTool can be constructed with a sandbox via
`new_with_sandbox()`, that NoopSandbox leaves commands unmodified,
and that command execution works end-to-end with a sandbox attached.
Ref: #3983
Replace workspace_dir.join(path) with resolve_tool_path(path) in
file_write, file_edit, and pdf_read tools to correctly handle absolute
paths within the workspace directory, preventing path doubling.
Closes#3774
Add `timeout_secs` and `agentic_timeout_secs` fields to
`DelegateAgentConfig` so users can tune per-agent timeouts instead
of relying on the hardcoded 120s / 300s defaults.
Validation rejects values of 0 or above 3600s, matching the pattern
used by MCP timeout validation.
Closes#3898
When LLMs pass the schedule parameter as a JSON string instead of a JSON
object, serde fails with "invalid type: string, expected internally
tagged enum Schedule". Add a deserialize_maybe_stringified helper that
detects stringified JSON values and parses the inner string before
deserializing, providing backward compatibility for both object and
string representations.
Fixes#3860
The deferred MCP tools section in the system prompt only listed tool
names inside <available-deferred-tools> tags without any instruction
telling the LLM to call tool_search to activate them. In daemon and
Telegram mode, where conversations are shorter and less guided, the
LLM never discovered it should call tool_search, so deferred tools
were effectively unavailable.
Add a "## Deferred Tools" heading with explicit instructions that
the LLM MUST call tool_search before using any listed tool. This
ensures the LLM knows to activate deferred tools in all modes
(CLI, daemon, Telegram) consistently.
Also add tests covering:
- Instruction presence in the deferred section
- Multiple-server deferred tool search
- Cross-server keyword search ranking
- Activation persistence across multiple tool_search calls
- Idempotent re-activation
Add support for switching AI models at runtime during a conversation.
The model_switch tool allows users to:
- Get current model state
- List available providers
- List models for a provider
- Switch to a different model
The switch takes effect immediately for the current conversation by
recreating the provider with the new model after tool execution.
Risk: Medium - internal state changes and provider recreation