Repository navigation
v0.23.0
A minor bump because it breaks: the native feature below breaks builds that
already set default-features = false.
Breaking
default-features = falsenow turns off the newnativefeature.
Before this release yoagent had no default features, so that line did
nothing. It now removes the filesystem and shell tools,default_tools(),
StdioTransport/with_mcp_server_stdio,FileBackend,
PriceTable::fetch_cachedand all of reqwest's default features. The last
one is the dangerous part: without reqwest's TLS the build still succeeds,
but every HTTPS provider call fails at runtime (as a retryable network
error, so each turn spends its retries first). Migration: native users
who setdefault-features = falseaddfeatures = ["native"].- A retried provider attempt now ends with a
MessageEnd, followed by the
newAgentEvent::ProviderRetry. Previously the abandoned attempt had no
closingMessageEnd(and, depending on the scheduler, some or all of its
events never reached consumers). Now every attempt that opened a message
is closed: a retried one by aMessageEndwithStopReason::Errorand an
error_messagesaying it will be retried, then aProviderRetry { attempt, max_attempts, error, delay_ms }. Migration: a consumer
that treats an errorMessageEndas the turn's failure should check
whether the next event isProviderRetry(see
the event contract). The bundled
consumers do: the GASP recorder no longer records retried attempts as model
calls, andexamples/release_smoke/examples/long_horizonno longer
report a run whose retry succeeded as failed.
Added
-
wasm32-unknown-unknownbuilds (e.g. Cloudflare Workers). A new
defaultnativefeature holds what needs a native host (see Breaking).
cargo build --target wasm32-unknown-unknown --no-default-featuresbuilds
the agent loop, all providers (over the host'sfetch), HTTP MCP,
SharedStatein memory, sub-agents, compaction and thedecision
feature;openapiandgaspstay native-only, and WASI targets are not
supported. Newyoagent::rtgivesspawn,sleep,timeout,
JoinHandleandInstant: Tokio's own on native targets (unchanged
behaviour, paused-clock tests intact), and the host executor,
setTimeoutandperformance.now()on wasm32.StreamProvider,
AgentTool,McpTransport,CompactionStrategy,ToolSource,
SharedStateBackend,TurnHook,ToolMiddleware,InputFilter,
AsyncInputFilterandDecisionBackendnow require
rt::MaybeSend + rt::MaybeSync. That is a blanket overSend + Syncon
native targets, so existing implementations and bounds are unaffected;
implementations that run on wasm32 use
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]/
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]. On wasm32,
LlmCompactiontakes its existing no-runtime path and never summarises,
and there are no environment variables, so keys must be passed explicitly.
Wall-clock reads on the agent path (types::now_ms,ExecutionTracker,
SigV4) useweb-time, which isstd::timeon native targets and the host
clock on wasm32 (wherestd::timepanics). Provider SSE streams disable
the eventsource library's own reconnect (retry::Never): providers
already stop at the first stream error and the agent loop owns retries, so
it never took effect, but its timer panicked on wasm32 and a reconnect
would re-send a completion request.PriceTable::fetchkeeps its timeout
on wasm32 (a per-request timeout). Items that neednativecarry a docs.rs
badge. Guide: WebAssembly & Cloudflare Workers.
CI gains awasm32clippy job (default anddecision, with a lint that
bans the std clock and Tokio calls that panic there) and a native
--no-default-featuresrow. -
AgentEvent::ProviderRetry(wire tagproviderRetry), sent when a
provider attempt fails with a retryable error and will be retried: the
failed attempt (1-based), the attempts allowed, the error text, and the
backoff it waits. See Breaking for how it pairs withMessageEnd. -
ToolSource: tools resolved per run. A new trait
(yoagent::ToolSource, moduletool_source) with one async method,
tools() -> Vec<Arc<dyn AgentTool>>, installed with
Agent::with_tool_source/SubAgentTool::with_tool_source. Each source is
consulted once at the start of every run (everyprompt*and
continue_loop*call; once per delegation for a sub-agent), and its tools
are offered alongside the agent's own for that run only — they are dropped
again when the run ends. On a name collision the agent's own tools win, then
earlier sources, then the earlier tool within a source; each dropped
duplicate is logged, and the run is never refused. The surviving sourced
tools are sorted by name, so a source returning the same set in another
order never changes the request prefix (any change to the set still
rewrites the prompt cache — per-run consultation bounds that to run
boundaries). Sources have no timeout: a sub-agent stops waiting when the
parent run is cancelled; on anAgent, dropping the prompt future leaves
its state untouched. A panicking source (even one that panics before
returning its future) is contained and contributes nothing. For tool sets that change while the agent
lives (plugin systems, reconnecting MCP servers); nothing changes for agents
without a source. Non-breaking:AgentTool,AgentContextand
AgentLoopConfigare unchanged. -
yoagent-rutis(new crate,integrations/yoagent-rutis/): a bridge
that lets rutis plugins extend a live
Agent— contribute tools (viaToolSource), gate tool calls, add turn
notes, reject input and observeAgentEvents on the rutis bus. Its own
crate, not yet on crates.io (it needs a yoagent release withToolSource);
yoagent itself does not depend on rutis. -
wasm32 tests run in CI.
tests/wasm32.rsruns under Node with
wasm-bindgen-test: thertshims (sleepwaits on the host timer,
timeout,spawnand abort,Instant) and two whole agent runs, a tool
turn and a retried provider failure on real timers. Lint alone cannot see
code that compiles for wasm32 but panics or hangs there. Native
dev-dependencies are now target-scoped so the test builds for wasm32; no
library code changes.
Changed
- A cancelled run ends as
StopReason::Aborted, notError. When the
provider returnsProviderError::Cancelled, or the run is cancelled while
a provider call fails, the turn's message (and its closingMessageEnd)
now carriesAborted, soon_erroris no longer called for a
cancellation. Cancelling during a retry's backoff ends the turn at once
instead of after the delay and one more request. A cancelled
SubAgentTooldelegation still fails the tool call, and a cancelled
prompt_structuredstill returnsStructuredPromptError::Provider
("Cancelled"), as before.
Fixed
- Failed provider attempts reach consumers deterministically. On a
retryable error the loop used to abort the event forwarder, so how much of
the failed attempt's partial output had already reached consumers depended
on the scheduler. The loop now drains the forwarder after each attempt,
then closes the attempt (see Breaking). The final failed attempt is closed
with the same error message the turn returns. History is unchanged: it
holds only the successful answer (or the final error). A custom
StreamProvidermust drop every clone of its event sender beforestream
returns, or the loop waits for it.