yoagent 0.25.0 is a crates.io release (published 2026-10-08T01:59Z) of the Rust agent library used by yoyo. It contains two breaking changes — an opt-in pricing model and an AgentMessage::Extension → AgentMessage::Custom rename — plus a tool-call cancellation correctness fix, a new sub-agent cancellation error, a unified extension plug-in contract, and compaction-spend accounting. One further behavioral change (Anthropic 529 handling) was inherited from 0.24.3. Because yoyo pins yoagent = "0.24" with lockfile 0.24.2, adopting 0.25 requires a Cargo.toml edit; 0.24.3 arrives through an ordinary cargo update.
Key Points
Breaking — pricing is now opt-in. Every ModelConfig constructor/preset sets cost = None unless the process calls yoagent::provider::prices::enable_bundled() (or enable_live) once at startup. An unguarded upgrade silently changes /cost for every yoagent-priced Claude preset, because yoyo's format/cost.rs reads anthropic_preset(model).and_then(|p| p.cost).
Breaking — enum rename.AgentMessage::Extension → AgentMessage::Custom, and ExtensionMessage → CustomMessage. yoyo had five call sites. Serialized data is unchanged (untagged enum with a role of extension).
Fix #243 — cancelled runs no longer execute unstarted tool calls. Previously a Ctrl-C arriving as a response landed could still run a command or write a file. Every TurnStart now gets a matching TurnEnd, and a final failure/cancel emits MessageStart/MessageEnd — so event counts change.
Sub-agent cancellation is now an error. A sub-agent cancelled before it answers fails with ToolError::Cancelled instead of succeeding with the placeholder text "(sub-agent produced no text output)".
Extensions unify plug-in contracts.yoagent::extension provides one contract (tools, , , , hooks); applies host policy to delegated runs at any depth — a parent never reached sub-agents. This is a candidate replacement for yoyo's hand-wired guards inside child s (#709/#977 class).
before_tool
on_input
before_model
finish
with_tree_extension
ToolMiddleware
BashTool
Compaction spend is now visible.LlmCompaction usage is counted in SessionStats::compaction.
Inherited from 0.24.3: Anthropic return codes 529 / overloaded_error are retried as RateLimited, routing Overloaded onto yoyo's handle_provider_retry door — which ignores the retry_after_partial opt-in (#997).
Concepts
yoagent::provider::prices — bundling gate for preset costs (enable_bundled, enable_live).
AgentMessage::Custom / CustomMessage — renamed extension variant; wire format unchanged.
Turn/Message lifecycle events — TurnStart/TurnEnd, MessageStart/MessageEnd; counting assumptions may break.
ToolError::Cancelled — failure signal for a sub-agent cancelled before answering.
Dependency pinning — yoagent "0.24" / lock 0.24.2 vs. explicit Cargo.toml bump to 0.25.
Details
Upgrade mechanics for yoyo
yoyo pins the minor line (yoagent = "0.24", lockfile 0.24.2), so a plain cargo update only reaches patch releases such as 0.24.3. Moving to 0.25.0 requires editing Cargo.toml; the inherited 0.24.3 changes come along with a normal update.
1. Breaking: pricing becomes opt-in
Previously, presets carried cost data by default. In 0.25.0 every ModelConfig constructor and preset is created with cost = None unless the process explicitly enables a price source at startup:
yoyo's format/cost.rs derives display cost via anthropic_preset(model).and_then(|p| p.cost). Without the startup call this chain returns None, so /cost output silently degrades for all yoagent-priced Claude presets. The fix is a one-time initialization call, but the failure mode is silent — it will not error, only change reported numbers.
2. Breaking: Extension renamed to Custom
AgentMessage::Extension becomes AgentMessage::Custom, and the payload type ExtensionMessage becomes CustomMessage. This affects source only: the enum is untagged and serialized with role: extension, so persisted data and wire traffic are unchanged. yoyo had five affected sites.
The defect: if cancellation arrived while a model response was landing, the agent could still execute tool calls that had not yet begun — including running a command or writing a file. After the fix, a cancelled run executes only tool calls already in flight; not-yet-started calls are abandoned.
Two lifecycle invariants changed with it:
Every TurnStart is now paired with a TurnEnd.
A final failure or cancellation now emits MessageStart/MessageEnd.
Any consumer that counts or balances these events must be updated, since counts shift relative to earlier versions.
4. Sub-agent cancellation becomes a failure
A sub-agent cancelled before it produces an answer now surfaces ToolError::Cancelled. Previously the parent saw a success carrying the sentinel string "(sub-agent produced no text output)", which conflated real cancellation with an empty-but-successful result. Callers matching on that string must switch to error handling.
5. Extensions: one plug-in contract for the whole tree
yoagent::extension introduces a single plug-in surface exposing five hooks — tools, before_tool, on_input, before_model, and finish — so a caller implements one contract rather than several narrower ones.
The important structural property is with_tree_extension: it applies host policy to delegated runs at any depth. That closes a gap where a parent ToolMiddleware stopped at the top-level agent and never reached sub-agents. For yoyo this is a candidate replacement for guards that are currently hand-wired into child BashTools — the class of problems seen in #709/#977.
Rendering diagram…
6. Compaction spend in SessionStats
LlmCompaction usage is now reported under SessionStats::compaction, making compaction cost visible in session statistics rather than invisible overhead.
7. Inherited from 0.24.3: Anthropic 529 handling
529 / overloaded_error responses from Anthropic are now retried as RateLimited. The consequence for yoyo is routing: Overloaded now travels through yoyo's handle_provider_retry door, which ignores the retry_after_partial opt-in (#997). Retry behavior for that error class therefore follows the generic path, not the partial-retry configuration.