mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 23:10:11 +00:00
b425213c538243d6ebc2be613d3f598388f2544b
333
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b425213c53 |
feat(e2e): extensions tab tests, CI parallelization, and 3 production bug fixes (#584)
* feat(e2e): extensions tab tests, CI parallelization, and 3 bug fixes ## E2E test coverage - Add tests/e2e/scenarios/test_extensions.py with 57 tests covering all extensions tab flows: installed WASM tool/MCP/channel cards, configure modal (open, fields, cancel, save, OAuth, error), auth card (token, OAuth, submit, cancel, error, multi-extension coexistence), activate flow, install/remove flows, WASM channel stepper states, and tab reload behaviour. All network calls intercepted via page.route() — no real binaries or external registries needed. - Expand tests/e2e/helpers.py with 50+ new CSS selectors for the extensions tab UI. - Add tests/e2e/README.md documentation on the page.route() mocking pattern, LIFO handler ordering, and page.evaluate() injection. ## CI parallelization - Split .github/workflows/e2e.yml into a build job (compile once, upload artifact) and a 3-way parallel test matrix (core / features / extensions), matching the pattern in test.yml. Reduces wall-clock time from ~15–20 min serial to ~10–12 min. Adds an e2e roll-up job for branch protection. ## Bug fixes in app.js (found via test-driven code review) - Fix null crash: renderExtensionCard() called ext.tools.length without a null guard; add ext.tools && check (regression: test_ext_tools_null). - Fix modal UX: submitConfigureModal() closed the overlay before checking success, making failures unrecoverable without reopening; close only on success, re-enable buttons and keep modal open on failure (regression: test_configure_modal_stays_open_on_save_failure). - Fix URL injection: all window.open() calls for server-supplied auth_url now go through openOAuthUrl() which rejects non-HTTPS schemes (regression: test_oauth_url_injection_blocked). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * refactor(e2e): prune extensions tests 57→46 by merging redundant setups Merge 11 tests that shared identical fixture+navigation overhead: - Group A: 3 empty-state tests → test_extensions_empty_tab_layout - Group B: card_renders absorbs ext_tools_list_shown (same _WASM_TOOL fixture) - Group B: auth_dot_unauthed + unauthed_shows_configure_btn → test_installed_wasm_tool_unauthed_state - Group D: installed + configured states → test_wasm_channel_setup_states (identical UI) - Group D: failed_state + stepper_failed_circle → test_wasm_channel_failed_renders - Group G: 5 field badge tests → test_configure_modal_field_variants (4 fields, one pass) - Group H: submit_success + enter_key_submits → test_auth_card_submit_success Coverage preserved: all assertions kept, no unique behaviors removed. Extensions CI job estimated to drop from ~7 min to ~5 min. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): fix configure_input selector scoping in merged field variants test modal.locator(".configure-modal input[type='password']") scoped the absolute selector inside .configure-modal, effectively searching for a nested .configure-modal which never exists → count() == 0. Use page.locator() instead, consistent with all other tests in the file. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address PR review comments — replace fixed sleeps with deterministic waits - Remove unnecessary wait_for_timeout(1000) from test_remove_cancelled_keeps_card (window.confirm = () => false is synchronous; DOM is unchanged when click() returns) - Replace wait_for_timeout(800) with wait_for_function() for window._lastOpenedUrl checks in configure_modal_save_oauth and activate_with_auth_url_opens_popup - Replace wait_for_timeout(300) with nth(1).wait_for(visible) in test_auth_card_multiple_extensions_coexist - Remove wait_for_timeout(800/300) in test_auth_card_submit_empty_noop and test_auth_completed_sse_dismisses_card (both check synchronous JS side-effects) - Add comment in test_oauth_url_injection_blocked explaining why timeout is kept (negative assertion — cannot use wait_for_function for absence of event) Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address remaining PR review comments - Remove unused `import pytest` from test_extensions.py - Fix unawaited coroutine bug: convert lambda route handlers to async def in test_extensions_tab_reloads_on_revisit and test_auth_completed_sse_triggers_extensions_reload (lambda r: r.fulfill(...) returns an unawaited coroutine; requests silently fell through to real server) - Fix README.md example to use async def handler (same bug in docs) - Harden openOAuthUrl() in app.js: use URL constructor instead of .startsWith() so non-string server-supplied values (objects, null, etc.) are safely rejected rather than throwing TypeError Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address second round of PR review comments - Add timeout-minutes to CI build job to prevent hung workflows - Use parsed.href instead of raw url in openOAuthUrl for safety - Remove unused MessageEvent variable in auth_completed test - Replace wait_for_timeout(800) with expect_response in activate test - Replace wait_for_timeout(300) with tab panel wait_for in reload test [skip-regression-check] Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> |
||
|
|
37bba72397 |
test: add 29 E2E trace tests for issues #571-575 (#593)
* test: add 29 E2E trace tests for worker, threading, tools, workspace, and routines (#571-575) Add comprehensive E2E test coverage across five test files: - e2e_worker_coverage (7 tests): parallel tool calls, error feedback, unknown tools, invalid params, rate limiting, iteration limits, planning mode - e2e_thread_scheduling (3 tests + 2 deferred): multi-turn state, undo/redo, concurrent dispatch - e2e_builtin_tool_coverage (8 tests): time parse/diff/invalid, routine CRUD/history, job create/status/list/cancel, HTTP replay - e2e_workspace_coverage (6 tests): chunked search, multi-doc search, hybrid search, directory tree, document lifecycle, identity in system prompt - e2e_routine_heartbeat (5 tests): cron triggers, event matching, cooldown enforcement, heartbeat findings, empty checklist skip Infrastructure: extend TestRig with database/workspace/trace_llm accessors, register job and routine tools by default, add with_extra_tools() for custom stub tools. Includes 24 JSON trace fixtures across worker/, threading/, tools/, and workspace/. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use 6-field cron format in routine_create_list fixture The cron 0.13 crate accepts both 6 and 7 fields, but the routine_create tool documents 6-field format. Align the fixture to match. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: eliminate vacuous passes and silently-skipped assertions in E2E tests - job_create_status: replace job_status (needs dynamic UUID) with list_jobs, assert both succeed via completed() not just started() - job_list_cancel: keep cancel_job but explicitly assert it fails with invalid canned job_id "latest", verify create_job + list_jobs succeed - unknown_tool_name: add !is_empty() guard before .all() to prevent vacuous pass on empty iterator - workspace tests: change `if let Some(ws)` to `.expect()` so assertions are never silently skipped when workspace/trace_llm is available [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add template substitution to TraceLlm for dynamic tool result forwarding Add {{call_id.json_path}} template syntax to trace fixtures, enabling tool results from one step to flow into subsequent steps' arguments. TraceLlm extracts variables from Role::Tool messages (stripping the safety layer's <tool_output> XML wrapper and unescaping entities) and substitutes them in canned tool_call arguments before returning. This fixes job_create_status and job_list_cancel tests to properly test job_status and cancel_job with real dynamic UUIDs from create_job, instead of using invalid canned IDs that silently failed. Also adds tool result content assertions to job_create_status to verify the actual tool output contains expected data (job_id, title). [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review feedback on E2E tests - undo_redo_cycle: assert exactly 3 turns instead of >= 2 - tool_error_feedback: use tempfile::tempdir() instead of hardcoded /tmp path, patch fixture path at runtime for CI portability - worker_timeout → iteration_limit: rename to accurately describe what's tested - post_plan_work_remaining → simple_echo_flow: rename, test doesn't exercise planning - identity_in_system_prompt: seed IDENTITY.md before test, assert system prompt contains the seeded content instead of just checking Role::System exists [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: strengthen workspace E2E test assertions per PR review - write_chunk_search: assert memory_search was called and returned payment/architecture-related results - multi_document_search: assert memory_search was called for cross-document search - hybrid_search_with_embeddings: assert both memory_write and memory_search were called to confirm write-then-search pipeline - directory_tree: assert tree output contains expected alpha/beta project paths [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
2df9602d56 |
fix(ci): fix three coverage workflow failures (#597)
* fix(ci): fix three coverage workflow failures 1. Migration ordering: glob `V*.sql` sorted V10 before V1 (ASCII '0' < '_'). Use `sort -V` for correct numeric ordering. 2. Missing WASM channels: telegram_auth_integration tests need the Telegram WASM binary. Add wasm32-wasip2 target, cargo-component, and build-wasm-extensions.sh to both coverage and e2e-coverage jobs (matching test.yml). 3. E2E shell quoting: `cargo llvm-cov show-env` outputs shell-quoted values (KEY='value') but GITHUB_ENV expects unquoted KEY=value. Strip single quotes with sed before appending. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(ci): address PR review feedback on coverage workflow - Migration loop: use readarray + printf | sort -V instead of $(ls) to avoid word-splitting on filenames - cargo-component install: check if already installed first, don't mask failures with || true - show-env quote stripping: use targeted regex to strip only wrapping quotes (KEY='value' -> KEY=value) instead of removing all quotes [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: skip telegram_auth_integration tests when WASM module not built Replace panicking assert! with a require_telegram_wasm!() macro that gracefully skips tests when the Telegram WASM binary hasn't been compiled. This ensures the test suite passes across all configurations (with and without wasm32-wasip2 target), while still running the tests in CI where the WASM channels are built. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: panic in CI when telegram WASM module missing, skip locally - require_telegram_wasm!() now checks the CI env var: panics in CI (so a broken WASM build step fails loudly) but skips locally - fs::read error now includes the file path for better diagnostics [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
06c84a5c77 |
test: add 26 tests for multi-thread safety, db CRUD, concurrency, errors (#442)
* fix: use std::sync::RwLock in MessageTool to avoid runtime panic The `requires_approval` method is synchronous but was using `tokio::sync::RwLock` with `.await` which requires blocking the runtime. This caused a panic: "Cannot block the current thread from within a runtime" Changes: - Replace `tokio::sync::RwLock` with `std::sync::RwLock` for `default_channel` and `default_target` fields - Use `unwrap_or_else(|e| e.into_inner())` to gracefully handle poisoned locks (recovers instead of panicking) - Update all usages from `.read().await` to `.read().unwrap_or_else()` The locks are short-held (just cloning strings), making std::sync::RwLock appropriate for sync methods called from async contexts. Fixes: "Cannot block the current thread from within a runtime" panic when the LLM tries to send a message via the message tool. Co-Authored-By: Claude Opus 4.6 <[email protected]> * test: comprehensive testing improvements and fix MessageTool blocking_read panic Fix tokio::sync::RwLock::blocking_read() panic in MessageTool::requires_approval() under multi-threaded tokio runtimes by switching to std::sync::RwLock with poison recovery. Add 26 new tests across 4 tiers: Tier 1 - Multi-thread runtime safety: - Fix MessageTool to use std::sync::RwLock instead of tokio::sync::RwLock - 4 multi-thread tests for MessageTool::requires_approval() scenarios - 1 multi-thread test for HttpTool credential-dependent approval - 1 structural test exercising all core tool sync trait methods under multi-thread runtime Tier 2 - Database CRUD coverage: - Settings lifecycle (CRUD, bulk ops) - Tool failure tracking (record, broken list, repair) - Routine lifecycle (create, get, list, update, delete, runs) - LLM call recording - Sandbox job lifecycle (create, get, update, list, mode) - Job events (save, list, limit) - Estimation snapshot round-trip Tier 3 - Concurrency: - ToolRegistry concurrent register + read under 4-worker runtime Tier 4 - Error coverage: - Display tests for all 8 error variants - From conversion tests for top-level Error enum Supersedes the fix in PR #411 with the same bug fix plus comprehensive test coverage. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: remove trailing whitespace in registry.rs Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Jerome Revillard <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Illia Polosukhin <[email protected]> |
||
|
|
04c5c3fe9f |
feat: WASM extension versioning with WIT compat checks (#592)
* feat: add WASM extension versioning with WIT compat checks and CI enforcement Phase 1 — WIT Versioning & Compatibility Checks: - Version WIT packages as `package near:[email protected];` - Add `semver` crate for version parsing and comparison - Add `WIT_TOOL_VERSION` / `WIT_CHANNEL_VERSION` host constants - Add `version` and `wit_version` fields to capabilities schemas - Add `wit_version` column to `wasm_tools` DB table (both backends) - Add load-time `check_wit_version_compat()` with semver rules - Add `IncompatibleWitVersion` error variants for tools and channels - Enhance instantiation errors with WIT version mismatch hints - Update all 14 capabilities JSON and 14 registry JSON files Phase 2 — Upgrade-in-Place & Channel DB Storage: - Change tool store to DELETE-before-INSERT (one version per extension) - Create `wasm_channels` table (PostgreSQL migration + libSQL schema) - Add `WasmChannelStore` trait with PostgreSQL and libSQL backends - Add `extension_info` tool showing version, WIT version, and status - Wire `ExtensionInfoTool` into tool registry (7 extension tools) Phase 3 — CI Version-Bump Enforcement: - Add `scripts/check-version-bumps.sh` checking WIT/tool/channel versions - Add `version-check` CI job (PR-only) to `.github/workflows/test.yml` - Support `[skip-version-check]` label/commit message bypass Includes 7 regression tests for WIT version compatibility checking and 2 integration tests for WIT version annotation verification. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review feedback for WASM extension versioning - Wrap PostgreSQL DELETE+INSERT in transactions for both tool and channel store() methods to prevent data loss on partial failure (Gemini, Copilot) - Rename StoredWasmChannelWithBinary.tool → .channel (copy-paste fix) - Remove unused WasmError::IncompatibleWitVersion variant (dead code) - Map channel loader WIT mismatch to IncompatibleWitVersion instead of generic Config error, simplify variant to single String message - Fix extension_info description to match actual returned fields - Add schema test for ExtensionInfoTool matching existing test pattern - Fix CI script to fail fast on git errors instead of silent bypass [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
a516e92156 |
fix: Telegram channel accepts group messages from all users if owner_… (#590)
* fix: Telegram channel accepts group messages from all users if owner_id is null * fix linter * fix tests * fix tests * fix tests in ci |
||
|
|
de7f503df9 |
fix(ci): anchor coverage/ gitignore rule to repo root (#591)
coverage/ matched tests/fixtures/llm_traces/coverage/, causing release-plz to detect committed+ignored files and abort on every push to main. PR #561 has been stuck with only 1 changelog entry since v0.15.0. Anchor the rule to the repo root with /coverage/ so it only ignores the top-level coverage report directory generated by cargo llvm-cov, not nested fixture directories. [skip-regression-check] |
||
|
|
fe4c3c5fe6 |
chore: update WASM artifact SHA256 checksums [skip ci] (#560)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Henry Park <[email protected]> |
||
|
|
14de4c1b57 |
feat: Add HMAC-SHA256 webhook signature validation for Slack (#588)
* feat: Add HMAC-SHA256 webhook signature validation for Slack * review fixes |
||
|
|
2d332f12f0 |
feat(tools): add Google Discovery API URLs to WASM tool descriptions (#585)
Add Google Discovery Service URLs to all 6 Google WASM tool descriptions so the LLM can fetch full API documentation on demand using its built-in HTTP tool. Discovery API is public and requires no authentication. URLs added: - Gmail: googleapis.com/discovery/v1/apis/gmail/v1/rest - Calendar: calendar-json.googleapis.com/$discovery/rest?version=v3 - Drive: googleapis.com/discovery/v1/apis/drive/v3/rest - Docs: googleapis.com/discovery/v1/apis/docs/v1/rest - Sheets: googleapis.com/discovery/v1/apis/sheets/v4/rest - Slides: googleapis.com/discovery/v1/apis/slides/v1/rest [skip-regression-check] Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
46218ec794 |
test: add WIT compatibility tests for WASM extensions (#586)
* test: add WIT compatibility tests for all WASM tools and channels Adds CI and integration tests to catch WIT interface breakage across all 14 WASM extensions (10 tools + 4 channels). Previously, changing wit/tool.wit or wit/channel.wit could silently break guest-side tools that weren't rebuilt until release time. Three new pieces: 1. scripts/build-wasm-extensions.sh — builds all WASM extensions from source by reading registry manifests. Used by CI and locally. 2. tests/wit_compat.rs — integration tests that compile and instantiate each .wasm binary against the current wasmtime host linker with stubbed host functions. Catches added/removed/renamed WIT functions, signature mismatches, and missing exports. Skips gracefully when artifacts aren't built so `cargo test` still passes standalone. 3. .github/workflows/test.yml — new wasm-wit-compat CI job that builds all extensions then runs instantiation tests on every PR. Added to the branch protection roll-up. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix rustfmt formatting in wit_compat tests Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review feedback on WIT compat tests - Switch build script from python3 to jq for JSON parsing, consistent with release.yml and avoids python3 dependency (#1, #7) - Use dirs::home_dir() instead of HOME env var for portability (#2) - Filter extensions by manifest "kind" field instead of path (#3) - Replace .flatten() with explicit error handling in dir iteration (#4, #5) - Split stub_tool_host_functions into stub_shared_host_functions + tool-only tool-invoke stub, since tool-invoke is not in channel WIT (#6) Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
6a2a6cd050 |
fix(security): use OsRng for all security-critical key and token generation (#519)
* fix(security): use OsRng for all security-critical key and token generation Replace rand::thread_rng() with rand::rngs::OsRng in all security-critical code paths that generate cryptographic key material, bearer tokens, PKCE verifiers, CSRF state parameters, and webhook secrets. thread_rng() uses a userspace CSPRNG (ChaCha) seeded from OS entropy, which is fine for non-security contexts but adds an unnecessary intermediate layer for key material where direct OS entropy (OsRng) is the correct choice. Files changed: - src/secrets/keychain.rs: master encryption key generation - src/secrets/crypto.rs: per-secret HKDF salt generation - src/orchestrator/auth.rs: per-job bearer token generation - src/channels/web/mod.rs: gateway auth token fallback - src/cli/oauth_defaults.rs: OAuth PKCE verifier and CSRF state - src/tools/mcp/auth.rs: MCP OAuth PKCE verifier - src/extensions/manager.rs: auto-generated extension secrets - src/setup/channels.rs: webhook secret generation Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(security): address PR review feedback for OsRng migration - Remove shadowing inner `use rand::rngs::OsRng` in `generate_salt()`; use module-level `aes_gcm::aead::OsRng` import instead (same type, avoids divergence risk if rand_core versions drift) - Fix missed callsites in `pairing/store.rs`: `random_code()` and `generate_unique_code()` now use `OsRng` for pairing auth codes - Add regression tests for `generate_salt()`: correct length, non-zero output, uniqueness across calls Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> |
||
|
|
df49b17d0f |
fix: prevent concurrent memory hygiene passes and Windows file lock errors (#535)
* fix: prevent concurrent memory hygiene passes and Windows file lock errors (#495) The heartbeat system spawns hygiene passes via tokio::spawn on every tick, creating a TOCTOU race where multiple tasks read the state file before any saves, causing all to execute concurrently. On Windows this also triggers OS error 1224 (file locked by memory-mapped section) when multiple tasks call std::fs::write on the same file. Three fixes: - AtomicBool guard (RUNNING + RunningGuard RAII) ensures only one hygiene pass runs at a time - State file is saved before cleanup (not after) to claim the cadence window early and close the TOCTOU race - Atomic file write (write to .tmp then rename) avoids Windows file-locking errors from concurrent writers Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add Mutex to serialize tests touching global RUNNING AtomicBool Address PR review feedback: the running_guard_prevents_reentry test manipulates a global static AtomicBool, which could cause flaky failures if future tests also touch it and run in parallel. A test-only Mutex ensures serialization. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
c87525d81f |
fix: sort tool_definitions() for deterministic LLM tool ordering (#582)
* fix: sort tool_definitions() for deterministic LLM tool ordering HashMap iteration order is non-deterministic, causing the LLM to receive tools in different orders across calls. Sort alphabetically by name to eliminate position bias in tool selection. Closes #566 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: use sort_unstable_by for tool definitions ordering Stable sort is unnecessary since tool names are unique. Unstable sort avoids the overhead of preserving equal-element order. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: repair bad merge in registry.rs (missing closing brace and test attribute) The merge of main into fix/sort-tool-definitions dropped the closing `}` of test_tool_definitions_sorted_alphabetically and the `#[tokio::test]` attribute on test_retain_only_filters_tools, causing an unclosed delimiter parse error that failed all CI jobs. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
9ae04f14e3 |
feat: restart (#531)
* feat: restart * review fixes * add IRONCLAW_IN_DOCKER env variable * review fixes * fix tests * set default value as false |
||
|
|
470de5bd2d |
feat: merge http/web_fetch tools, add tool output stash for large responses (#578)
* feat: merge http/web_fetch tools, add tool output stash for large responses Merge `web_fetch` into `http` tool with smart approval: plain GETs (no headers, no body) run without approval and follow redirects with SSRF re-validation per hop; all other requests require approval as before. Add `tool_output_stash` on JobContext so full tool outputs are preserved before safety-layer truncation. The `json` tool gains a `source_tool_call_id` parameter to reference stashed outputs, enabling reliable parsing of large API responses that exceed the 100KB context limit. Other improvements: - Descriptive User-Agent header using CARGO_PKG_VERSION - Truncation now keeps partial data + hint about source_tool_call_id - System prompt reinforces tool_calls over narration - json tool query/stringify handle pre-parsed (non-string) data [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: delete dead web_fetch.rs (merged into http tool) Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix rustfmt formatting Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: rename shadowed data binding for clarity in json tool Address PR review: rename owned `data` to `data_value` before re-binding as `let data = &data_value` to make ownership explicit. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(ci): mark network-dependent trace tests as #[ignore] The weather_sf and baseball_stats tests hit live external APIs (wttr.in, ESPN) which are unreliable in CI. Mark them #[ignore] so they don't block the pipeline. Run locally with `--ignored` to include them. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: replay recorded HTTP exchanges in trace tests instead of hitting live APIs Wire ReplayingHttpInterceptor into TestRig when the trace fixture contains http_exchanges. This replays recorded responses instead of making live network calls, making tests deterministic and CI-stable. Add captured HTTP responses to weather_sf.json (wttr.in) and baseball_stats.json (ESPN API) fixtures. Revert #[ignore] on both tests — they now run offline. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: recover inline bracket-format tool calls from LLM text responses When flatten_tool_messages converts tool calls to text like `[Called tool `http` with arguments: {...}]` for NEAR AI compatibility, the LLM sometimes echoes this format back in its text responses instead of using proper tool_calls. Add recovery for this bracket format in recover_tool_calls_from_content and strip it in clean_response so users don't see raw tool call syntax. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
69cddb10fd |
feat: integrate 13-dimension complexity scorer into smart routing (#529)
* feat(llm): add smart model routing based on request complexity Automatically selects optimal model tier (flash/standard/pro/frontier) for each request based on 13-dimension complexity scoring: - Reasoning words, multi-step signals, code indicators - Domain-specific terms, creativity, precision - Safety sensitivity, tool likelihood, question complexity - Token estimate, context dependency, sentence complexity Features: - Pattern overrides for fast-path routing (greetings → flash, security audits → frontier) - Configurable tier-to-model mappings (defaults to -latest aliases) - Thinking mode per tier (pro: low, frontier: medium) - User-configurable pattern overrides - Zero-config for default benefits, full control for power users Expected cost savings: 50-70% vs always-using-frontier baseline. Refs: smart-routing-spec.md * fix(routing): address Gemini Code Assist review feedback - Add tracing warnings for invalid tier/regex in user overrides (router.rs) - Use unreachable!() for tier hint match since regex enforces valid tiers (scorer.rs) - Refactor weighted total to array iteration for maintainability (scorer.rs) - Add TODO for making domain keywords configurable (scorer.rs) Refs: PR #208 * feat(routing): make domain keywords configurable - Add ScorerConfig with optional domain_keywords field - Add DEFAULT_DOMAIN_KEYWORDS constant (exported for reference) - Add domain_keywords to RouterConfig for top-level configuration - Build domain regex at runtime from config, fallback to defaults - Add score_complexity_with_config() function - Add test for custom domain keywords Users can now provide project-specific keywords: RouterConfig { domain_keywords: Some(vec!["mycompany".into(), "myproduct".into()]), ..Default::default() } Addresses Gemini Code Assist review feedback on PR #208. Tests: 20/20 passing * docs: add domain_keywords to routing config example * feat: integrate 13-dimension complexity scorer into smart routing (takeover #208) Folds the 13-dimension complexity scorer and pattern overrides from PR #208 into the existing SmartRoutingProvider, replacing the simpler keyword-based classifier. Adds 4-tier system (Flash/Standard/Pro/Frontier), configurable scorer weights, domain keywords, regex pattern overrides, tier hints, and multi-dimensional boost. Removes separate routing/ directory and lazy_static dependency in favor of std::sync::LazyLock. Includes 44 tests covering all scoring dimensions, tier boundaries, pattern overrides, and provider routing. Co-Authored-By: onlyamicrowave <[email protected]> Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address review feedback on smart routing PR (#529) - Cache compiled domain regex in SmartRoutingProvider (built once at construction, not per-request) and add score_complexity_with_regex() API - Check explicit tier hints before pattern overrides so user intent wins (e.g. "[tier:flash] security audit" routes as Flash, not Frontier) - Trim input before matching/scoring so trailing whitespace doesn't break anchored override regexes or skew token-length scoring - Fix token estimate comment (>=520 chars = 100, not >500) - Update spec: check implementation plan boxes, fix file paths, add note that llm.routing YAML schema is target design (current config uses env vars) - Add regression tests for tier hint precedence and trimmed greeting matching Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: restore Cargo.lock from main to fix html_to_markdown test The lockfile was fully regenerated during the PR #208 merge conflict resolution, which bumped html-to-markdown-rs from 2.25.1 to 2.27.2. The new version produces different output that breaks the golden-file snapshot test. Restore the original lockfile from main — lazy_static was never in main's lockfile, so no further changes needed. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address second round of review feedback (#529) - Tighten quick-lookup override regex with end anchor to prevent matching complex questions like "What time complexity is merge sort?" - Handle empty domain keywords list by falling back to defaults instead of producing a broken regex that matches empty strings everywhere - Clarify spec architecture diagram: current impl uses 2-provider split (cheap/primary), per-tier model mapping is target design - Add regression tests for both fixes Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Microwave <[email protected]> Co-authored-by: Joe <[email protected]> Co-authored-by: onlyamicrowave <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
b4b19738a8 |
Trajectory benchmarks and e2e trace test rig (#553)
* refactor: extract shared assertion helpers to support/assertions.rs Move 5 assertion helpers from e2e_spot_checks.rs to a shared module. Add assert_all_tools_succeeded and assert_tool_succeeded for eliminating false positives in E2E tests. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add tool output capture via tool_results() accessor Extract (name, preview) from ToolResult status events in TestChannel and TestRig, enabling content assertions on tool outputs. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: correct tool parameters in 3 broken trace fixtures - tool_time.json: add missing "operation": "now" for time tool - robust_correct_tool.json: same fix - memory_full_cycle.json: change "path" to "target" for memory_write Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add tool success and output assertions to eliminate false positives Every E2E test that exercises tools now calls assert_all_tools_succeeded. Added tool output content assertions where tool results are predictable (time year, read_file content, memory_read content). Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: capture per-tool timing from ToolStarted/ToolCompleted events Record Instant on ToolStarted and compute elapsed duration on ToolCompleted, wiring real timing data into collect_metrics() instead of hardcoded zeros. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: add RAII CleanupGuard for temp file/dir cleanup in tests Replace manual cleanup_test_dir() calls and inline remove_file() with Drop-based CleanupGuard that ensures cleanup even if a test panics. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add Drop impl and graceful shutdown for TestRig Wrap agent_handle in Option so Drop can abort leaked tasks. Signal the channel shutdown before aborting for future cooperative shutdown. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: replace agent startup sleep with oneshot ready signal Use a oneshot channel fired in Channel::start() instead of a fixed 100ms sleep, eliminating the race condition on slow systems. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: replace fragile string-matching iteration limit with count-based detection Use tool completion count vs max_tool_iterations instead of scanning status messages for "iteration"/"limit" substrings. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use assert_all_tools_succeeded for memory_full_cycle test Remove incorrect comment about memory_tree failing with empty path (it actually succeeds). Omit empty path from fixture and use the standard assert_all_tools_succeeded instead of per-tool assertions. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: promote benchmark metrics types to library code Move TraceMetrics, ScenarioResult, RunResult, MetricDelta, and compare_runs() from tests/support/metrics.rs to src/benchmark/metrics.rs. Existing tests use re-export for backward compatibility. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add Scenario and Criterion types for agent benchmarking Scenario defines a task with input, success criteria, and resource limits. Criterion is an enum of programmatic checks (tool_used, response_contains, etc.) evaluated without LLM judgment. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add initial benchmark scenario suite (12 scenarios across 5 categories) Scenarios cover tool_selection, tool_chaining, error_recovery, efficiency, and memory_operations. All loaded from JSON with deserialization validation test. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add benchmark runner with BenchChannel and InstrumentedLlm BenchChannel is a minimal Channel implementation for benchmarks. InstrumentedLlm wraps any LlmProvider to capture per-call metrics. Runner creates a fresh agent per scenario, evaluates success criteria, and produces RunResult with timing, token, and cost metrics. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add baseline management, reports, and benchmark entry point - baseline.rs: load/save/promote benchmark results - report.rs: format comparison reports with regression detection - benchmark_runner.rs: integration test with real LLM (feature-gated) - Add benchmark feature flag to Cargo.toml Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: apply cargo fmt to benchmark module Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add multi-turn scenario types with setup, judge, ResponseNotContains Add BenchScenario, Turn, TurnAssertions, JudgeConfig, ScenarioSetup, WorkspaceSetup, SeedDocument types for multi-turn benchmark scenarios. Add ResponseNotContains criterion variant. Add TurnAssertions::to_criteria() converter for backward compat with existing evaluation engine. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add JSON scenario loader with recursive discovery and tag filter Add load_bench_scenarios() for the new BenchScenario format with recursive directory traversal and tag-based filtering. Create 4 initial trajectory scenarios across tool-selection, multi-turn, and efficiency categories. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): multi-turn runner with workspace seeding and per-turn metrics Add run_bench_scenario() that loops over BenchScenario turns, seeds workspace documents, collects per-turn metrics (tokens, tool calls, wall time), and evaluates per-turn assertions. Add TurnMetrics to metrics.rs and clear_for_next_turn() to BenchChannel. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add LLM-as-judge scoring with prompt formatting and score parsing Create judge.rs with format_judge_prompt, parse_judge_score, and judge_turn. Wire into run_bench_scenario for turns with judge config -- scores below min_score fail the turn. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add CLI subcommand (ironclaw benchmark) Add BenchmarkCommand with --tags, --scenario, --no-judge, --timeout, --update-baseline flags. Wire into Command enum and main.rs dispatch. Feature-gated behind benchmark flag. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): per-scenario JSON output with full trajectory Add save_scenario_results() that writes per-scenario JSON files alongside the run summary. Each scenario gets its own file with turn_metrics trajectory. Update CLI to use new output format. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add ToolRegistry::retain_only and wire tool filtering in scenarios Add a retain_only() method to ToolRegistry that filters tools down to a given allowlist. Wire this into run_bench_scenario() so that when a scenario specifies a tools list in its setup, only those tools are available during the benchmark run. Includes two tests for the new method: one verifying filtering works and one verifying empty input is a no-op. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): wire identity overrides into workspace before agent start Add seed_identity() helper that writes identity files (IDENTITY.md, USER.md, etc.) into the workspace before the agent starts, so that workspace.system_prompt() picks them up. Wire it into run_bench_scenario() after workspace seeding. Include a test that verifies identity files are written and readable. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add --parallel and --max-cost CLI flags Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(benchmark): use feature-conditional snapshot names for CLI help tests Prevents snapshot conflicts between default (no benchmark) and all-features (with benchmark) builds by using separate snapshot names per feature set. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): parallel execution with JoinSet and budget cap enforcement Replace sequential loop in run_all_bench() with parallel execution using JoinSet + semaphore when config.parallel > 1. Add budget cap enforcement that skips remaining scenarios when max_total_cost_usd is exceeded. Track skipped count in RunResult.skipped_scenarios and display it in format_report(). Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add tool restriction and identity override test scenarios Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: fix formatting for Phase 3 Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add SkillRegistry::retain_only and wire skill filtering in scenarios Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(benchmark): add --json flag for machine-readable output Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: add GitHub Actions benchmark workflow (manual trigger) Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor(benchmark): remove in-tree benchmark harness, keep retain_only utilities Move benchmark-specific code out of ironclaw in preparation for the nearai/benchmarks trajectory adapter. This removes: - src/benchmark/ (runner, scenarios, metrics, judge, report, etc.) - src/cli/benchmark.rs and the Benchmark CLI subcommand - benchmarks/ data directory (scenarios + trajectories) - .github/workflows/benchmark.yml - The "benchmark" Cargo feature flag What remains: - ToolRegistry::retain_only() and SkillRegistry::retain_only() - Test support types (TraceMetrics, InstrumentedLlm) inlined into tests/support/ instead of re-exporting from the deleted module Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: add README for LLM trace fixture format Documents the trajectory JSON format, response types, request hints, directory structure, and how to write new traces. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(test): unify trace format around turns, add multi-turn support Introduce TraceTurn type that groups user_input with LLM response steps, making traces self-contained conversation trajectories. Add run_trace() to TestRig for automatic multi-turn replay. Backward-compatible: flat "steps" JSON is deserialized as a single turn transparently. Includes all trace fixtures (spot, coverage, advanced), plan docs, and new e2e tests for steering, error recovery, long chains, memory, and prompt injection resilience. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(test): fix CI failures after merging main - Fix tool_json fixture: use "data" parameter (not "input") to match JsonTool schema - Fix status_events test: remove assertion for "time" tool that isn't in the fixture (only "echo" calls are used) - Allow dead_code in test support metrics/instrumented_llm modules (utilities for future benchmark tests) [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * Working on recording traces and testing them * feat(test): add declarative expects to trace fixtures, split infra tests Add TraceExpects struct with 9 optional assertion fields (response_contains, tools_used, all_tools_succeeded, etc.) that can be declared in fixture JSON instead of hand-written Rust. Add verify_expects() and run_recorded_trace() so recorded trace tests become one-liners. Split trace infra tests (deserialization, backward compat) into tests/trace_format.rs which doesn't require the libsql feature gate. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor(test): add expects to all trace fixtures, simplify e2e tests Add declarative expects blocks to all 19 trace fixture JSONs across spot/, coverage/, advanced/, and root directories. Update all 8 e2e test files to use verify_trace_expects() / run_and_verify_trace(), replacing ~270 lines of hand-written assertions with fixture-driven verification. Tests that check things beyond expects (file content on disk, metrics, event ordering) keep those extra assertions alongside the declarative ones. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(test): adapt tests to AppBuilder refactor, fix formatting Update test files to work with refactored TestRigBuilder that uses AppBuilder::build_all() (removing with_tools/with_workspace methods). Update telegram_check fixture to use tool_list instead of echo. Fix cargo fmt issues in src/llm/mod.rs and src/llm/recording.rs. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor(test): deduplicate support unit tests into single binary Support modules (assertions, cleanup, test_channel, test_rig, trace_llm) had #[cfg(test)] mod tests blocks that were compiled and run 12 times — once per e2e test binary that declares `mod support;`. Extracted all 29 support unit tests into a dedicated `tests/support_unit_tests.rs` so they run exactly once. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix trailing newlines in support files Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor(test): unify trace types and fix recorded multi-turn replay Import shared types (TraceStep, TraceResponse, TraceToolCall, RequestHint, ExpectedToolResult, MemorySnapshotEntry, HttpExchange*) from ironclaw::llm::recording instead of redefining them in trace_llm.rs. Fix the flat-steps deserializer to split at UserInput boundaries into multiple turns, instead of filtering them out and wrapping everything into a single turn. This enables recorded multi-turn traces to be replayed as proper multi-turn conversations via run_trace(). [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(test): fix CI failures - unused imports and missing struct fields - Add #[allow(unused_imports)] on pub use re-exports in trace_llm.rs (types are re-exported for downstream test files, not used locally) - Add `..` to ToolCompleted pattern in test_channel.rs to match new `error` and `parameters` fields Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(test): fix CI failures after merging main - Add missing `error` and `parameters` fields to ToolCompleted constructors in support_unit_tests.rs - Add `..` to ToolCompleted pattern match in support_unit_tests.rs - Add #[allow(dead_code)] to CleanupGuard, LlmTrace impl, and TraceLlm impl (only used behind #[cfg(feature = "libsql")]) Co-Authored-By: Claude Opus 4.6 <[email protected]> * Adding coverage running script * fix(test): address review feedback on E2E test infrastructure - Increase wait_for_responses polling to exponential backoff (50ms-500ms) and raise default timeout from 15s to 30s to reduce CI flakiness (#1) - Strengthen prompt_injection_resilience test with positive safety layer assertion via has_safety_warnings(), enable injection_check (#2) - Add assert_tool_order() helper and tools_order field in TraceExpects for verifying tool execution ordering in multi-step traces (#3) - Document TraceLlm sequential-call assumption for concurrency (#6) - Clean up CleanupGuard with PathKind enum instead of shotgun remove_file + remove_dir_all on every path (#8) - Fix coverage.sh: default to --lib only, fix multi-filter syntax, add COV_ALL_TARGETS option - Add coverage/ to .gitignore - Remove planning docs from PR [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review - use HashSet in retain_only, improve skill test - Use HashSet for O(N+M) lookup in SkillRegistry::retain_only and ToolRegistry::retain_only instead of linear scan - Strengthen test_retain_only_empty_is_noop in SkillRegistry to pre-populate with a skill before asserting the no-op behavior [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(test): revert incorrect safety layer assertion in injection test The safety layer sanitizes tool output, not user input. The injection test sends a malicious user message with no tools called, so the safety layer never fires. Reverted to the original test which correctly validates the LLM refuses via trace expects. Also fixed case-sensitive request hint ("ignore" -> "Ignore") to suppress noisy warning. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: clean stale profdata before coverage run Adds `cargo llvm-cov clean` before each run to prevent "mismatched data" warnings from stale instrumentation profiles. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix formatting in retain_only test [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Illia Polosukhin <[email protected]> |
||
|
|
a1f0208956 |
fix(ci): persist all cargo-llvm-cov env vars for E2E coverage (#559)
* fix(ci): persist all cargo-llvm-cov env vars for E2E coverage Newer cargo-llvm-cov versions output CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS from show-env. The workflow was cherry-picking specific vars (RUSTFLAGS, LLVM_PROFILE_FILE, etc.) to persist to $GITHUB_ENV, so CARGO_ENCODED_RUSTFLAGS was never set during the build step, producing a non-instrumented binary and zero .profraw files. Replace the manual echo lines with `cargo llvm-cov show-env >> $GITHUB_ENV` to forward all vars (including CARGO_ENCODED_RUSTFLAGS, CARGO_INCREMENTAL, etc.) regardless of cargo-llvm-cov version. Also forward CARGO_ENCODED_RUSTFLAGS in the E2E conftest subprocess env. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(ci): address PR review — prefix-based env forwarding, split clean step - conftest.py: replace explicit env var list with prefix-based matching (CARGO_LLVM_COV*, LLVM_*) plus specific vars (CARGO_ENCODED_RUSTFLAGS, CARGO_INCREMENTAL) to stay resilient to cargo-llvm-cov changes. - coverage.yml: move `cargo llvm-cov clean` to its own step so the env vars from show-env (persisted via $GITHUB_ENV) are active when clean runs. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
3615967f92 |
chore: release v0.15.0 (#526)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.15.0 |
||
|
|
704d63f16a |
feat(oauth): route callbacks through web gateway for hosted instances (#555)
* feat: route OAuth callbacks through web gateway for hosted instances On hosted instances (e.g., NEAR AI), OAuth callbacks can't reach the local TCP listener on port 9876. This adds a gateway-routed OAuth flow that works behind reverse proxies and load balancers. Backend changes: - Add /oauth/callback as a public route on the web gateway - PendingOAuthFlow registry shared between ExtensionManager and handler - Gateway mode auto-detected via IRONCLAW_OAUTH_CALLBACK_URL env var - Platform state format (instance:nonce) for nginx routing - Token exchange proxy support via IRONCLAW_OAUTH_EXCHANGE_URL - Local TCP listener mode preserved as backward-compatible fallback UX improvements: - Hide Configure button for tools with auto-resolved OAuth credentials (builtin defaults or platform-injected env vars) - Skip client_id/client_secret fields in setup schema when auto-resolved - Show Reconfigure only after successful authentication Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(oauth): harden gateway callback and refactor AuthResult - Add 60s timeout to exchange_via_proxy HTTP client (matching exchange_oauth_code) - Read GATEWAY_AUTH_TOKEN once at ExtensionManager construction instead of per-flow from env (prevents coupling and clarifies token provenance) - Extract oauth_error_page() helper to deduplicate error landing pages - Remove IRONCLAW_FORCE_GATEWAY_CALLBACK env var (auto-detection suffices) - Refactor AuthResult into typed AuthStatus enum with constructors, eliminating stringly-typed status and Option fields that were always None - Adapt all handlers (chat, extensions, ws) to new AuthResult/AuthStatus API - Use setup_url (not validation_endpoint) for awaiting_token responses [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(oauth): address review feedback — empty token guard, test flakiness, doc typos - Fail early in exchange_via_proxy() when gateway_token is empty instead of sending an unauthenticated request to the exchange proxy - Fix test_oauth_callback_strips_instance_prefix to use an expired flow so it never attempts a real HTTP token exchange (prevents CI flakiness) - Fix doc comments: /auth/callback → /oauth/callback in PendingOAuthFlow and ExtensionManager pending_oauth_flows docs [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix: clarify strip_instance_prefix safety, wrapper credential fix, test assertion - Add comment to strip_instance_prefix noting nonces are base64url (no colons) - Expand wrapper.rs comment explaining the credential_user_id bug fix - Fix test_oauth_callback_strips_instance_prefix assertion: landing_html does not include provider_name on error pages [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
902492bcdb |
feat(web): show error details for failed tool calls (#490)
* feat(web): show error details and input params for failed tool calls Failed tool calls in the gateway UI previously showed only a red X icon with an empty expandable body. This change: - Adds optional `error` and `parameters` fields to `ToolCompleted` SSE events so the browser receives failure details in real-time - Auto-expands failed tool cards to make errors immediately visible - Adds `StatusUpdate::tool_completed()` constructor that centralizes the 5 duplicated construction sites and applies `redact_params()` to prevent sensitive values (e.g. secret_save's "value" param) from leaking through SSE broadcasts - Adds `sensitive_params()` trait method to `Tool` for declaring which parameters must be redacted before logging, hooks, and UI display - Adds `redact_params()` utility and wires it through hooks, approvals, ActionRecord storage, and debug logs in dispatcher/worker - Adds `SecretListTool` and `SecretDeleteTool` for LLM-driven secret management (values never returned, only names/metadata) - Fixes auth flow: setup-only extensions show configure modal instead of OAuth card; auth_completed SSE dismisses both UI paths - CI: release workflow creates PR instead of pushing directly to main - Registry: MissingChecksum error enables source fallback for bootstrapping when checksums haven't been populated yet Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: apply cargo fmt Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: keep original params in PendingApproval for execution, redact only for display Address two PR review comments: 1. execute_chat_tool_standalone now redacts sensitive params before logging, matching the pattern already used in worker.rs. 2. PendingApproval previously stored redacted parameters, which meant approved tool calls received "[REDACTED]" instead of the actual values. Add a display_parameters field for UI/logs and keep parameters as the original values used for execution. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address PR review comments - worker.rs: redact sensitive params before BeforeToolCall hook, matching dispatcher.rs — hooks in the autonomous job path now receive redacted params instead of raw values - registry.rs: fix docstring for register_secrets_tools (list, delete, not save/list/delete — no SecretSaveTool is registered) - app.js: fix double toast/loadExtensions in submitConfigureModal — for non-OAuth success the auth_completed SSE already handles both, so skip them in the HTTP response handler to avoid duplicates [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
13697976db |
feat(extensions): improve auth UX and add load-time validation (#536)
* feat(extensions): add load-time validation for auth capabilities Catch common misconfigurations (missing auth section, missing setup_url, short prompts) at startup via tracing::warn instead of silently failing at auth time. * feat(extensions): improve auth prompts, setup_url, and showAuthCard Add setup_url and descriptive prompts to channel and tool capabilities files. Fix showAuthCard in web gateway and improve extension manager auth flow messaging. * refactor(extensions): extract MIN_PROMPT_LENGTH constant in validate() Address review feedback: replace magic number 30 with a named constant for readability and maintainability. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
cbcd5adcc0 |
fix(security): restrict query-token auth to SSE endpoints only (#528)
* fix(security): restrict query-token auth to SSE endpoints only Query-string `?token=xxx` auth was accepted on all endpoints, exposing the main auth token in server logs, Referer headers, and browser history for state-changing routes. Now only GET /api/chat/events and GET /api/logs/events accept query tokens; all other endpoints require the Authorization header. Supersedes #364. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add WebSocket endpoint to query-token allowlist, add URL-encoding tests The WS upgrade at /api/chat/ws also can't set custom headers, so it needs query-token auth like the SSE endpoints. Also adds tests for URL-encoded token values to cover the form_urlencoded parser. Addresses review feedback from Gemini (partially, /api/jobs/{id}/events is a JSON endpoint not SSE, so it correctly stays excluded) and Copilot (URL-encoded token test). Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
e24c33ff90 |
fix(ci): flush profraw coverage data in E2E teardown (#550)
The ironclaw binary only handles SIGINT (via tokio::signal::ctrl_c), not SIGTERM. When conftest.py sent SIGTERM during teardown, the OS killed the process immediately without running atexit handlers, so LLVM never flushed .profraw files. cargo llvm-cov report then found zero profraw files and failed. - Send SIGINT instead of SIGTERM so the existing ctrl_c handler triggers graceful shutdown → main() returns → atexit runs → profraw flushed - Increase shutdown wait from 5s to 10s for graceful cleanup - Add a diagnostic step to verify profraw files exist before the report step, making future issues visible in CI logs Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
f99991d27b |
fix(wasm): coerce string parameters to schema-declared types (#498)
* fix(wasm): coerce string parameters to schema-declared types
LLMs frequently pass numeric values as JSON strings ("5" instead of 5)
or booleans as strings ("true" instead of true). The WASM module's
serde deserializer rejects these type mismatches. This adds a
coerce_params_to_schema() helper that walks the params JSON object
and converts string values to their schema-declared types (number,
integer, boolean) before passing to the WASM module.
Adds 5 unit tests covering number, integer, boolean coercion,
already-correct types, and unparseable strings.
Closes #486
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* refactor: use in-place mutation and case-insensitive boolean coercion
Address review feedback:
- Use get_mut instead of clone+insert to avoid allocations
- Make boolean coercion case-insensitive (handles "True", "FALSE", etc.)
- Expand boolean test to cover false and mixed-case values
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: collapse nested if-let to satisfy clippy collapsible_if lint
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
|
||
|
|
89600e2b5c |
fix(agent): strip leaked [Called tool ...] text from responses (#497)
* fix(agent): strip leaked [Called tool ...] text from agent responses When the NEAR AI provider flattens tool_call messages to plain text, markers like [Called tool ...] and [Tool ... returned: ...] can leak into the user-visible response if the LLM echoes them back. This adds a sanitization step in the agentic loop's text response path that strips these internal markers before returning. If stripping leaves the response empty, a generic fallback message is returned instead. Closes #487 Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: use fold instead of collect+join to avoid heap allocation Address review feedback: replace Vec collect + join with fold to build the filtered string directly, avoiding an intermediate heap allocation. Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: cargo fmt Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Pierre LE GUEN <[email protected]> |
||
|
|
e4e78d8a87 |
fix(web): reset job list UI on restart failure (#499)
* fix(web): reset job list UI on restart failure The restartJob() catch handler was missing a loadJobs() call, so the job row stayed in a stale highlighted state after a failed restart attempt. Add loadJobs() to match the success path behavior. Closes #485 Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: use .finally() for loadJobs() instead of duplicating Move loadJobs() to a .finally() block so it runs on both success and failure without duplication. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
b9446712e9 |
fix(telegram): add missing webhook section to capabilities.json (#381)
The Telegram channel capabilities file was missing the `webhook` block inside `capabilities.channel`, causing the router to fall back to the default `X-Webhook-Secret` header instead of the Telegram- specific `X-Telegram-Bot-Api-Secret-Token`. When a webhook secret is configured (via `telegram_webhook_secret`), incoming updates are rejected with 401 because Telegram sends the token in `X-Telegram-Bot-Api-Secret-Token` but the router looks for `X-Webhook-Secret`. The existing test in `schema.rs` already expects the correct header name, confirming this is an oversight in the shipped capabilities file. Co-authored-by: SMKRV <[email protected]> Co-authored-by: firat.sertgoz <[email protected]> |
||
|
|
31a4330f24 | Fix UTF-8 unsafe truncation in sandbox log capture (#359) | ||
|
|
9b47dbbaed |
fix(security): replace .unwrap() panics in pairing store with proper error handling (#515)
The pairing store called .unwrap() on path.parent() in three locations (upsert_request, record_failed_approve, add_allow_from). If a path has no parent (root path or empty), this panics — a potential denial-of-service vector if an attacker can influence the path. Added InvalidPath variant to PairingStoreError and replaced all three .unwrap() calls with ok_or_else error propagation. This follows the project's no-panics-in-production policy. Locations fixed: - upsert_request (line ~227) - record_failed_approve (line ~322) - add_allow_from (line ~465) Co-authored-by: Claude Sonnet 4.6 <[email protected]> |
||
|
|
ac3c928853 |
ci: enhance coverage with feature matrix, postgres, and E2E (#523)
* ci: enhance coverage workflow with feature matrix, postgres, and E2E Replace single-config coverage job with a multi-job pipeline: - Mirror test.yml's 3-config feature matrix (all-features, default, libsql-only) - Add PostgreSQL service (pgvector/pgvector:pg16) with migrations for postgres configs so integration tests actually run instead of skipping - Add E2E coverage job using cargo-llvm-cov instrumented binary with Playwright browser tests - Add coverage-gate roll-up job for branch protection - Upload per-config flags to Codecov (all-features, default, libsql-only, e2e) - Forward LLVM coverage env vars in E2E conftest.py so profraw data lands where cargo-llvm-cov report expects it [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review feedback on coverage workflow - Avoid setting DATABASE_URL to empty string for libsql-only config; use $GITHUB_ENV conditional step so the var is unset entirely - Add set -euo pipefail and psql -v ON_ERROR_STOP=1 to migrations so SQL errors fail the job immediately [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
bf2a08be94 |
feat: add local-test skill and Dockerfile.test for web gateway testing (#524)
Add Dockerfile.test as reusable infrastructure for spinning up local test instances with libsql (no PostgreSQL dependency). Defaults to port 3003 to avoid conflict with dev server. Add local-test workspace skill that teaches the agent how to build, run, and test against local Docker containers using Chrome MCP browser automation tools. Covers LLM backend configuration, multi-instance testing, cleanup, and troubleshooting. |
||
|
|
308758c27c |
chore: release v0.14.0 (#480)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.14.0 |
||
|
|
f60c91e9a7 |
ci: enforce regression tests for fix commits (#517)
* ci: enforce regression tests for fix commits Add a commit-msg hook and CI workflow that require test changes alongside bug fix commits, ensuring every fix includes a regression test that would have caught the bug. - scripts/commit-msg-regression.sh: local git hook (blocks fix commits without test changes; exempts static/docs-only; bypass via [skip-regression-check] marker) - .github/workflows/regression-test-check.yml: CI mirror on PRs (checks title + commit messages; skip via label) - scripts/dev-setup.sh: install hook in step 6 - .github/scripts/create-labels.sh: add skip-regression-check label - CLAUDE.md: document regression test policy Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review feedback on regression test enforcement - Use here-strings instead of echo|grep to avoid misinterpreting special characters in variables - Use git diff -W (whole-function context) to detect edits inside existing test functions, not just new #[test] attributes - Honor [skip-regression-check] in commit messages in CI (not just the PR label) - Use git rev-parse --git-path hooks for worktree-safe hook install [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * Update .github/workflows/regression-test-check.yml Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Copilot <[email protected]> |
||
|
|
a22d44f2b2 |
ci: add code coverage with cargo-llvm-cov and Codecov (#511)
* ci: add code coverage with cargo-llvm-cov and Codecov Add a Coverage workflow that runs on PRs and pushes to main using cargo-llvm-cov with --all-features, uploading LCOV results to Codecov. Include codecov.yml config with project/patch targets and ignore rules for stub files. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: switch Codecov upload to OIDC (tokenless) Use GitHub OIDC tokens instead of CODECOV_TOKEN secret so coverage uploads work for fork PRs where secrets are not available. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: fail coverage upload strictly on push, leniently on PRs Use a conditional so pushes to main fail if Codecov upload breaks (preventing silent reporting gaps) while PRs stay lenient to avoid blocking fork PRs where OIDC may not be available. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: disable Codecov auto-detection to suppress warnings We provide lcov.info explicitly, so disable auto-search for gcov, coverage.py, and Xcode formats that produce noisy warnings. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: include channels-src and tools-src in coverage reporting These WASM source directories should be tracked for test coverage rather than ignored. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: remove stale ignore entries from codecov.yml The marketplace, ecommerce, taskrabbit, and restaurant stub files no longer exist in the codebase. Co-Authored-By: Claude Opus 4.6 <[email protected]> * ci: run coverage on push to main only Avoids running tests twice on PRs (once in test.yml, once for coverage). Coverage runs on merge to main instead. Simplify fail_ci_if_error to always true since it only runs on push now. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
a181c8b384 |
fix(web): mobile browser bar obscures chat input (#508)
* fix(web): use dvh units to prevent mobile browser bar from obscuring chat input On mobile browsers (Brave/Android, Safari/iOS), the bottom navigation bar covers the chat input because 100vh includes space behind browser chrome. Switch to 100dvh (dynamic viewport height) with vh fallback for older browsers, and add safe-area-inset padding for notched devices. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Fix padding declaration in chat input style --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Illia Polosukhin <[email protected]> |
||
|
|
35a79caf87 |
fix(web): assign unique thread_id to manual routine triggers (#500)
* fix(web): assign unique thread_id to manual routine triggers
Manual routine triggers via the web API created an IncomingMessage
without a thread_id, causing session_manager.resolve_thread() to
route the output to whatever thread was last associated with the
(user, "gateway", None) key. This sets a unique thread_id of the
form "routine-{id}-{timestamp}" so each manual trigger gets its own
dedicated thread.
Closes #484
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: add ownership check to routine trigger handler (IDOR)
Address review feedback: verify routine.user_id matches the
authenticated user before allowing the trigger, preventing
unauthorized cross-user routine execution.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
|
||
|
|
85999b25a8 |
fix(web): refresh routine UI after Run Now trigger (#501)
* fix(web): refresh routine UI after "Run Now" trigger triggerRoutine() only showed a toast but did not refresh the routine data after triggering. This adds openRoutineDetail() / loadRoutines() calls after the toast, matching the pattern used by toggleRoutine(). Closes #483 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: only refresh detail view if triggered routine matches current view Check currentRoutineId === id before refreshing the detail panel to avoid refreshing the wrong routine's view. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
b60e5e907a |
fix(skills): use slug for skill download URL from ClawHub (#502)
* fix(web): use slug for skill download URL from ClawHub The skill install handler was using req.name (display name like "Markdown Converter") instead of the slug (like "owner/markdown-converter") when constructing the download URL. The registry endpoint expects a slug, so display names caused 502 errors. - Add optional `slug` field to SkillInstallRequest - Prefer slug over name when building the download URL - JS installSkill() now sends slug from search results Closes #482 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: guard against empty slug string in skill download URL Filter out empty slug strings so we fall back to name instead of constructing an invalid download URL. Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: cargo fmt Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
d562dc8d90 |
fix(workspace): thread document path through search results (#503)
* fix(workspace): thread document path through search results Memory search results were showing chunk UUIDs instead of source file paths. Thread document_path through RankedResult, SearchResult, and the RRF fusion pipeline so handlers can display the actual file path. Fixes #481 Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: use into_iter to move values instead of cloning Address review feedback: consume results with into_iter() to move String fields directly instead of cloning them. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
c239a4fc2a | feat: remove the okta tool (#506) | ||
|
|
944968bf76 |
fix(workspace): import custom templates before seeding defaults (#505)
Swap the order of import_from_directory() and seed_if_empty() so that custom workspace templates from WORKSPACE_IMPORT_DIR take priority over generic seeds. Previously, seed_if_empty() ran first and created all default files, causing import_from_directory() to skip everything since the files already existed in the DB. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
18b59ae9a7 |
feat: add OAuth support for WASM tools in web gateway (#489)
* feat: add OAuth support for WASM tools in web gateway Extract reusable OAuth functions (build_oauth_url, exchange_oauth_code, store_oauth_tokens, validate_oauth_token) from CLI into shared oauth_defaults module, then wire them into the web gateway's ExtensionManager. Key changes: - Install auto-activates WASM tools (no separate Activate button) - Configure button triggers OAuth flow via save_setup_secrets - Scope merging: installing a second Google tool triggers re-auth with merged scopes from all tools sharing the same secret_name - Cancel-and-retry: aborting stale OAuth listeners prevents port conflicts - Post-auth validation: wrong account detected via validation_endpoint - Reconfigure always re-auths (deletes old token before starting fresh) - UI shows error toast on OAuth failure, refreshes extension list Flow: Install → Active → Configure (enter client_id/secret) → Save → OAuth popup → authorize → done. Second Google tool install auto-triggers scope expansion OAuth. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address PR review comments - Add custom headers support to ValidationEndpointSchema (fixes missing Notion-Version header regression) - Guard activate handler auth check with status == "awaiting_authorization" to prevent unexpected OAuth popups - Add window dimensions to OAuth popup in activateExtension() - Simplify UTF-8 truncation boundary check Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address Copilot PR review comments (security, UX, bugs) - Add CSRF state parameter to OAuth flow (random state in auth URL, validated in callback) - Restore MCP server Activate button in web UI (was hidden for all non-channel extensions) - Abort JoinHandle in cleanup_expired_auths to prevent port 9876 conflicts - Fix Google-specific error message for non-Google OAuth providers - Add has_auth field to ExtensionInfo API response (fixes Configure button visibility) - Use oauth_defaults::callback_url() instead of hardcoded redirect_uri (both CLI and manager) - Update auth check comment to match actual behavior (scope expansion + first-time auth) - Add unit tests for build_oauth_url (basic, PKCE, extra params, state uniqueness) - Check all required setup secrets (client_id + client_secret) before starting OAuth Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
f4855962fc |
fix: use std::sync::RwLock in MessageTool to avoid runtime panic (#411)
* fix: use std::sync::RwLock in MessageTool to avoid runtime panic The `requires_approval` method is synchronous but was using `tokio::sync::RwLock` with `.await` which requires blocking the runtime. This caused a panic: "Cannot block the current thread from within a runtime" Changes: - Replace `tokio::sync::RwLock` with `std::sync::RwLock` for `default_channel` and `default_target` fields - Use `unwrap_or_else(|e| e.into_inner())` to gracefully handle poisoned locks (recovers instead of panicking) - Update all usages from `.read().await` to `.read().unwrap_or_else()` The locks are short-held (just cloning strings), making std::sync::RwLock appropriate for sync methods called from async contexts. Fixes: "Cannot block the current thread from within a runtime" panic when the LLM tries to send a message via the message tool. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address code review feedback for MessageTool RwLock fix - Fix formatting (long lines broken up per rustfmt) - Add regression test that demonstrates the panic with tokio::sync::RwLock and passes with std::sync::RwLock when calling requires_approval() (sync method) from async context Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
f18fb5173b |
feat(web): fix jobs UI parity for non-sandbox mode (#491)
* feat(web): fix jobs UI parity for non-sandbox mode The web gateway Jobs UI was built primarily for sandbox (Docker) jobs. When running without sandbox (common for NEAR AI hosted envs), multiple features were broken. This change fixes all of them: - Agent jobs now broadcast live SSE events to the web UI (Activity tab) - Agent job restart via scheduler.dispatch_job (not chat message) - Follow-up prompts for agent jobs via WorkerMessage injection - Capability flags (can_restart, can_prompt, job_kind) in job detail API - Rate-limit retry with cap (10 consecutive) and Retry-After header parsing - Plan interruption on user message (breaks out of plan, re-evaluates) - Correct SSE status field in mark_completed/mark_failed/mark_stuck - SseManager preserved across rebuild_state calls Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: fix formatting in db/mod.rs and nearai_chat.rs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
78878ad7ef |
Remove restart infrastructure, generalize WASM channel setup (#493)
* refactor: remove restart infrastructure and generalize Telegram-specific code Remove the gateway restart mechanism (hot-activation works, restart won't fix activation failures) and generalize Telegram-specific hardcoded checks so all WASM channels get equal treatment. Part 1 - Remove restart infrastructure: - Remove needs_restart from ActionResponse, restart_requested from GatewayState - Remove gateway_restart_handler, /api/gateway/restart route, exit code 75 - Remove restart overlay JS/CSS (dead code - restartGateway() never called) - Surface actual activation errors instead of suggesting restart Part 2 - Generalize Telegram-specific code: - Replace telegram_owner_id: Option<i64> with generic wasm_channel_owner_ids: HashMap<String, i64> (backwards-compatible via TELEGRAM_OWNER_ID env var) - Pairing status check now applies to all active WASM channels - All channels get 3-step stepper in web UI, remove "coming soon" note - Remove dead setup_telegram() code (~700 lines) - Telegram's capabilities.json declares required_secrets, so the generic setup_wasm_channel() path handles it Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * test: add Settings::set() test for wasm_channel_owner_ids Addresses review feedback: verify that setting per-channel owner IDs via the dotted-path Settings::set() API works correctly with the new HashMap<String, i64> type. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(web): refresh extension stepper after pairing approval loadPairingRequests only refreshed the pairing section, not the stepper status. Call loadExtensions() instead so the stepper updates from "Awaiting Pairing" to "Active" immediately after approval. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
5f841554d5 |
feat(workspace): add TOOLS.md, BOOTSTRAP.md, and disk-to-DB import (#477)
* feat(workspace): add TOOLS.md, BOOTSTRAP.md, and disk-to-DB import Add two new OpenClaw-compatible workspace markdown files: - TOOLS.md: Environment-specific tool notes (SSH hosts, device names, etc.) injected into the system prompt under "## Tool Notes". Seeded as comment-only (like HEARTBEAT.md) so it's effectively empty until the user adds real content. Not write-protected — the agent can update it as it learns the environment. - BOOTSTRAP.md: First-run onboarding ritual. Injected FIRST in the system prompt when present. Guides the agent through introducing itself, learning about the user, and updating workspace files. Only seeded on truly fresh workspaces (no existing identity files) to avoid triggering the ritual on existing deployments. Agent clears it via `memory_write(target="bootstrap")` when done. Add `Workspace::import_from_directory()` for disk-to-DB import: - Scans a directory for *.md files and imports any that don't already exist in the database (never overwrites user edits) - Controlled by WORKSPACE_IMPORT_DIR env var, runs after seed_if_empty() - Enables Docker images / deployment scripts to ship customized workspace templates that override generic seeds - Backwards compatible: no-op when env var is unset Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address PR review comments - Use stable `path.extension() != Some(OsStr::new("md"))` instead of unstable `is_none_or` (nightly-only) - Use `tokio::join!` for concurrent DB reads in fresh-workspace check - Skip unreadable directory entries instead of failing the entire import - Skip unreadable files instead of failing the entire import Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
6adf95b6d1 |
fix: wire secrets store into all WASM runtime activation paths (#479)
WASM tools and channels activated at runtime (via web UI or CLI) were missing secrets store wiring, causing credential injection to silently fail. Tools like web-search would get 401s from APIs even though the user had configured their API key. Four bugs fixed: - activate_wasm_tool(): WasmToolLoader created without .with_secrets_store() - register_wasm_from_storage(): hardcoded secrets_store: None - WasmChannelLoader: no secrets_store field at all (added field + builder) - activate_wasm_channel() and startup path: both missed wiring secrets The startup path in app.rs was correct; all runtime paths now match it. Co-authored-by: Claude Sonnet 4.6 <[email protected]> |
||
|
|
8530f44630 |
chore: release v0.13.1 (#453)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v0.13.1 |