Trident stage 1 dropped the assistant message that carried a file tool
use while keeping the tool result that answered it, so the compacted
session ended up with orphaned `tool_result` blocks. Providers reject
such requests with HTTP 400 ("tool_result blocks must be a response to
a preceding tool_use"), which breaks the auto-compact retry path that
runs Trident before resending a request.
Stage 1 also counted a tool result as an independent file operation,
which pushed `last_write` past the write the result belonged to and made
the newest write look obsolete.
Only tool uses count as file operations now, and a tool result is
dropped together with the tool use that produced it.
* Fix issue #3364: web search returns error on non-2xx response and filters search engine self-links
- Add HTTP status code check to execute_web_search: return error if response is not 2xx
instead of proceeding with potentially junk results (e.g., DuckDuckGo challenge pages)
- Filter out links pointing back to the search engine host in extract_search_hits_from_generic_links
to avoid returning search engine self-links when no proper search results are found
- Add regression tests for both scenarios
Refs #3364
* fix: implement registrable domain filtering and error on 202/blocked responses
- Replace exact host matching with registrable domain comparison to properly
filter search engine self-links across subdomains (e.g., html.duckduckgo.com
vs duckduckgo.com)
- Add error handling for 2xx responses with no search results (e.g., 202
challenge pages from blocked search providers)
- Add regression tests for both 202 challenge page and mixed self-link scenarios
- Remove unrelated Cargo.lock version bumps (revert to 0.1.6)
Fixes#3364
* fix(tools): reject DuckDuckGo 202 challenge pages and drop search-engine links
The challenge page arrives as HTTP 202, which is_success() accepts, and its
links point at duckduckgo.com while the request goes to html.duckduckgo.com,
so the previous filter kept them. Only a 200 page is parsed now, and the
generic fallback drops any duckduckgo.com link or link to the configured
search host. Removes the registrable-domain heuristic and the 'no hits is an
error' path (a real query can legitimately return nothing), and replaces
tests that never exercised a self-link with ones that fail on the old code.
Refs #3364
---------
Co-authored-by: clawdbot <clawdbot@ultraworkers.dev>
The environment section rendered "Model family: Claude Opus 4.6" for
every Anthropic model, so claude-opus-5-5, claude-sonnet-4-6, and any
other Claude model told users it was Opus 4.6.
Render a neutral "Claude" family label followed by the resolved model
ID, and rebuild the system prompt on /model so the line follows a
mid-session switch instead of keeping the startup model.
Models from the OpenAI-compatible providers fell back to
default_sonnet_tier ($3/$15 per MTok), so e.g. gpt-5.4-nano was
estimated at 15x its input price and qwen-plus at about 26x.
Add a table of published rates keyed by model id, matching dated
snapshots and -latest aliases:
- OpenAI standard tier for current gpt-6 / gpt-5.x / gpt-4.1 / gpt-4o
and o-series models (Responses-only pro models are omitted).
- xAI grok-4.x and grok-build. Slugs retired on 2026-05-15 (grok-3,
grok-4-0709, grok-4-fast-*, grok-4-1-fast-*) are billed at grok-4.3
rates, and grok-code-fast-* is served as grok-build.
- DashScope qwen-max, qwen-plus, qwen-flash and kimi-k2.5 at China
(Beijing) rates, which is claw's default DashScope endpoint, with
non-thinking output and the 20% implicit-cache hit rate.
Long-context and higher input tiers use the base rate because usage is
aggregated across requests.
Merge PR #3360 from c01dsummer/claw-code (fix/roadmap-835-838)
Four fixes addressing issues #3361 and #3360:
1. Parallel tool-result pairing (835): The Anthropic Messages API requires all
tool results of a turn to be in a single message. When a turn issued two tool
calls at once, the results were sent in separate messages, causing a 400 error.
Fixed by merging adjacent tool-result-only messages into one.
2. Terminal-scope path limit (836): Correlation keys up to 512 bytes expanded to
1024+ hex characters, exceeding APFS's 1024-byte total path limit. Fixed by
using SHA256 digest (32 hex chars) for path components while storing the full
key in the record for exact matching.
3. Credential hint (838): When ANTHROPIC_BASE_URL points to a non-Anthropic
endpoint (DeepSeek, corporate gateways), the missing-credentials error now
explains that the endpoint expects its own credentials, not Anthropic's.
4. ROADMAP entries: Documents all three fixes with verification details.
All tests pass (370+ for rusty-claude-cli, 240+ for sdk, 122+ for tools, 209+ for
api). Formatting verified with cargo fmt. CI checks approved and passed.
Fixes#3361, #3360
std::env::vars() panics on any non-UTF-8 value. zsh exports a metafied
$_ when the command path contains non-ASCII characters (e.g. 下載), so
claw panicked on startup. Use vars_os() and skip entries that are not
valid UTF-8.
* fix(runtime): update model pricing table to current rates
Align pricing_for_model with the published Anthropic pricing table:
- default_sonnet_tier was $15/$75 (the old Opus rate); it is now the
Sonnet 4.x rate of $3/$15, which is what the `sonnet` alias resolves to.
- Add Sonnet 5 / 5.5, Haiku 5.5, Haiku 3.5, and Fable / Mythos rates.
- Opus 4.5+ is $5/$25 and Opus 5.5 is $4/$20; Opus 4/4.1 keep the $15/$75
tier they are still billed at on Bedrock and Google Cloud.
Versions are parsed from the model id so dated, provider-prefixed, and
Vertex-style ids resolve to the right tier. Haiku 5.5's >100K-token tier
is not modeled because usage is aggregated across requests.
* fix(runtime): add DeepSeek API pricing
DeepSeek models fell back to default_sonnet_tier ($3/$15 per MTok),
overestimating V4 Flash by about 10x on input and 12x on output.
Price the ids served by the DeepSeek API at peak rates: deepseek-v4-pro
at $1.32/$3.96, and deepseek-flash, deepseek-v4-flash and the retired
deepseek-chat / deepseek-reasoner aliases at $0.30/$1.20. Off-peak usage
is billed at half. Other DeepSeek models on third-party hosts keep the
default because their prices vary by host.
push_output_block recorded a thinking event at content_block_start (an
empty placeholder) and the stream loop recorded the full block again at
content_block_stop. The empty duplicate was replayed on the next request
and rejected with 400 "each thinking block must contain thinking".
Only emit the event from the non-streaming path.
The English README migrated the star-history chart/link from
star-history.com / api.star-history.com to star-history.dera.page,
but README.zh.md (last synced at d229a9b) still used the old domains.
Update the 4 URLs so both language versions stay consistent.
The /sandbox status can be 'ok' (with namespace isolation) or 'warn' (in
restricted environments like CI without unshare support). Update the test
to accept either value rather than hard-coding an expectation.
- Update client_integration.rs user-agent assertion to expect version 0.1.5
- Fix output_format_contract.rs /sandbox status expectation from 'warn' to 'ok'
The test explicit_model_provider_hint_controls_runtime_provider_dispatch
was failing when OPENAI_BASE_URL was set in the environment to a non-standard
URL (e.g., api.layofflabs.com). The test expects the OpenAI provider to use
the default base URL (api.openai.com) when no explicit base_url is provided
in the ProviderConfig.
This fix adds an EnvVarGuard struct to the tests module and uses it to
ensure OPENAI_BASE_URL is unset before running the test, preventing
environment pollution from affecting the test result.
Fixes: https://github.com/ultraworkers/claw-code/issues/3345
Co-authored-by: clawdbot <clawdbot@ultraworkers.dev>
- Add SkillOrigin::Bundled variant to support bundled skills registry
- Implement bundled_skill_root() function for discovering bundled skills
- Update skill discovery to include bundled skills from standard install paths
- Create example bundled skills (example-skill, chain-example)
- Add comprehensive tests for bundled skills discovery and metadata
- Bundled skills follow same directory structure as plugins
- Skills can now chain/invoke other skills using standard dispatch mechanisms
- Maintains backward compatibility with existing SkillsDir and LegacyCommandsDir
- Implement OperationRegistry with standard query, control, and event operations
- Add Schema struct for defining input/output schemas
- Add OperationMetadata for rich operation descriptions
- Add Frame methods to extract operation and input from broker requests
- Add comprehensive tests demonstrating the gap and the fix
- Operations can be discovered, validated, and customized
This addresses issue #3321 (Slice 1.2): SDK operation registry & schema validation.
The gap: The original SDK allowed any operation name with no validation.
The fix: A registry-based system that defines valid operations and their schemas.
- Mark SDK wire protocol (Slice 1.1) as implemented (PR #3330)
- Update tool catalog status with read selectors (PR #3332/#3334)
- Add slash commands progress: /tools, /info (PR #3339)
- Document as historical planning reference with current implementation status
- Add PR references and status summary
- All section status notes now reflect merged work as of 2026-10-13
- Resolve .gitignore conflict: include both nix entries and .claude/
- Update AnthropicRuntimeClient::new() test calls to match new signature
- Fix explicit_model_provider_hint test to unset OPENAI_BASE_URL env var
The PermissionMode enum intentionally does not implement PartialOrd
to prevent silent re-ranking of the privilege ladder. Use the explicit
covers() method for privilege comparisons instead of < and >=.
execute_batch already keyed results to original call slots; this makes
that airtight: panicking worker threads resolve to their own index via
a fallback tuple instead of colliding with slot 0, and results are
asserted to follow call order with a regression test that mixes a
workspace-escape read among valid reads at cap=2.
Adds the fan-out bound: parallel-safe calls run in waves of at most
maxParallelToolCalls (default 8, CLAWD_PARALLEL_TOOL_CALLS env, or the
new setting applied via with_max_parallel_tool_calls at runtime build).
Sequential behavior is preserved: allowed_tools checks still run
before dispatch, and ToolSearch stays off the parallel path.
TeamCreate resolves maxConcurrentAgents from the per-team input, then
the maxConcurrentAgents setting, then unlimited (pre-fix behavior).
The cap is persisted in the manifest and registered on a process-wide
gate; agent threads still spawn immediately but block on an RAII slot
before entering their run loop, so manifest/task-claim semantics are
unchanged. TeamDelete forgets the gate so queued agents drain and no
stale team entries accumulate. Includes a gate regression test.