Releases: yologdev/yoagent
Release list
v0.13.0
Serializable event stream — the wire contract release
AgentEvent and StreamDelta now derive Serialize/Deserialize/PartialEq. The wire format is internally tagged camelCase JSON:
{"type":"messageUpdate","message":{...},"delta":{"type":"text","delta":"hi"}}
{"type":"toolExecutionEnd","toolCallId":"tc_1","toolName":"bash","result":{...},"isError":false}It is a frozen public contract: variant tags, field names, and the tagging scheme are guarded by exhaustive-match snapshot tests (a new variant fails to compile until its tag is pinned) and an exact-JSON snapshot of the full nested payload. External frontends — websocket fanout servers, TypeScript clients, JSONL pipes — can consume the agent's event stream directly. First consumer: yoyo-serve.
Changed
- Message payload serialization normalized to uniform camelCase (
usage.cacheRead/cacheWrite/totalTokens,errorMessage,providerMetadata). Session files written by older versions still load (serde aliases).⚠️ Files written by 0.13 load in older versions without error but silently drop the renamed fields (token counts read 0;errorMessage/providerMetadata— including Gemini thought signatures — are lost). Don't round-trip session files through yoagent < 0.13. - serde floor raised for
rename_all_fieldssupport.
Docs
- Wire-format section incl. streaming semantics: accumulate from
delta, reset at eachMessageStart, resync atMessageEnd.
Full changelog: https://github.com/yologdev/yoagent/blob/main/CHANGELOG.md
v0.12.0
0.12.0 headline: your agent is a git repo — and yoagent is the first tested GASP-conformant runtime.
GASP bridge (features = ["gasp"])
let recorder = GaspRecorder::init("./my-agent-repo", "my-agent", "worker-1",
GoalRef::New { title: "ship the feature".into() }).await?;
let (tx, record_handle) = recorder.recording_sender("implement the parser", None);
agent.prompt_with_sender("implement the parser", tx).await;
let run_id = record_handle.await??.expect("run recorded");Record agent runs into a GASP agent repo: an append-only semantic event log (goals, runs, model calls, tool calls) that folds into a typed graph, one git commit per run. Restore = git clone — the agent's durable self travels as a repo. Crash-safe (stale runs closed, dropped streams handled), UI-safe (events tee to your app before recording; a recorder failure never blinds the UI), and redactable (with_summarizer).
Conformance-checked in CI: every commit emits an agent repo and runs the GASP protocol's 7-check suite against a fresh clone — envelope round-trip, replay, vocabulary, append-only git history, causation integrity, restore, domain↔ops consistency. All pass.
Pairs with 0.11's session trees: Session::to_jsonl is a natural format for the transcripts/ cold tier.
Meta Model API — day-one support
// provider auto-selected; key from META_API_KEY (or Meta's MODEL_API_KEY)
let agent = Agent::from_config(ModelConfig::meta("muse-spark-1.1", "Muse Spark 1.1"));Meta opened its first paid API this week; yoagent supports it out of the box — 1M context, reasoning wired (ThinkingLevel tunes Meta's reasoning_effort; note their server default is medium), launch pricing pre-configured including the $0.15/M cached-input rate, so session_cost_usd() and telemetry cost fields are accurate from day one. Specs fact-checked against Meta's official docs. US-only public preview at launch.
Also
- MSRV coverage restored for all features (via
yoagent-state0.4.2, whose declared 1.85 MSRV is now compiler-enforced upstream). --provider metain the CLI example.
Full details in CHANGELOG.md.
v0.11.0
0.11.0 is the differentiators release: five features that move yoagent from a solid agent loop to a full agent runtime.
Tool middleware — permission hooks for every tool call
#[async_trait::async_trait]
impl ToolMiddleware for ReadOnlyPolicy {
async fn before_tool(&self, call: &ToolCallRequest<'_>) -> ToolDecision {
match call.tool_name {
"write_file" | "bash" => ToolDecision::Deny("read-only session".into()),
_ => ToolDecision::Allow,
}
}
}
agent.with_tool_middleware(ReadOnlyPolicy)Async approve/deny/modify hooks gating every tool call — the mechanism behind approval prompts and policy engines. A denial becomes an error tool result the LLM adapts to; the loop never aborts. Panicking middleware is contained. yoagent ships the hook, you ship the policy.
Structured outputs — prompt_structured::<T>()
let invoice: Invoice = agent.prompt_structured("Extract the invoice: ...", schema).await?;Typed, schema-validated replies enforced natively: Anthropic tool-forcing, OpenAI json_schema strict, Gemini responseSchema. Provider failures surface as StructuredPromptError::Provider (never masked as parse errors); the raw text is preserved on parse failure.
Session trees — branching history
session.checkpoint("first-draft")?;
// ...later: rewind and take a different direction
session.seek_checkpoint("first-draft")?;
let agent = Agent::from_config(cfg).with_messages(session.path_messages());id/parent_id conversation trees: fork from any point, checkpoints, edit-and-rerun without losing the original branch, JSONL persistence with load-time validation. The shape of GASP's transcripts/ tier.
Cross-provider thinking — 7/7 protocols
thinking_level is now honored everywhere: Gemini/Vertex thinkingConfig (thought summaries streamed back), Bedrock budget-based thinking (reasoning deltas + signature replay for multi-turn tool use), Azure reasoning effort. The "not yet wired" warnings are gone.
Telemetry — tracing spans
agent_loop → llm_stream (tokens_in/out/cached, cost_usd, error) → tool (is_error)
Structured spans with token and dollar-cost fields. Bridge to OpenTelemetry app-side via tracing-opentelemetry — yoagent gains no OTel dependency; negligible overhead with no subscriber. cargo run --example telemetry to see it live.
Compatibility: no breaking changes to released APIs. StreamConfig is now #[non_exhaustive] (use StreamConfig::new) — this only affects code constructing it literally for custom providers.
Full details in CHANGELOG.md.
v0.10.0
The headline of 0.10.0 is a config-first construction API. Build an agent from a single ModelConfig — the provider, model id, context window, pricing, and API key resolution all come from one place.
// before (0.9): pair provider + config by hand, model id passed twice
let agent = Agent::new(OpenAiCompatProvider)
.with_model_config(ModelConfig::zai("glm-4.7", "GLM 4.7"))
.with_model("glm-4.7")
.with_api_key(key);
// after (0.10): provider inferred from config.api; key from ZAI_API_KEY
let agent = Agent::from_config(ModelConfig::zai("glm-4.7", "GLM 4.7"));Added
Agent::from_config(ModelConfig)— the new primary constructor (provider + env key resolved automatically).Agent::from_provider(provider, ModelConfig)— explicit provider / test doubles.Agent::from_config_with(®istry, ModelConfig) -> Result<_, AgentBuildError>— custom registry.Agent::set_model(ModelConfig)— switch model mid-session (never silently clobbers an explicit provider).SubAgentTool::from_config/from_config_with/from_providermirrors.ModelConfig::mock(), exportedAgentBuildError,ProviderRegistry::resolve(),StreamProvider::protocol().
Deprecated (removed in 1.0 — still work, with a compiler nudge)
Agent::new,Agent::with_model,Agent::with_model_configSubAgentTool::new,SubAgentTool::with_model,SubAgentTool::with_model_config
with_api_key is not deprecated. Nothing is removed in 0.10 — existing 0.9 code compiles and runs (with deprecation warnings) unless you build with deny(warnings).
Fixed
- Google/Vertex usage no longer double-counts cached tokens.
Retry-Afterclamped tomax_delay_ms.- Compaction budget calibration subtracts measured overhead instead of ratio-scaling (the old formula could collapse the budget toward zero).
session_cost_usd()returnsNonefor unpriced models instead of0.0.- Missing API keys log a warning naming the env var to set.
Full migration guide (before/after table) in CHANGELOG.md.
v0.9.0
A breaking release batching the changes anticipated in #33: the reqwest 0.13/rustls upgrade, support for the current model generation, two new provider gateways, queue inspection, and a crate-wide exhaustiveness policy.
⚠️ Breaking changes
Dependencies / MSRV
- reqwest 0.12 → 0.13 with rustls as the TLS backend — OpenSSL is no longer a build dependency (faster builds on Android/Windows).
reqwest-eventsourcereplaced by the maintainedkameleoon-reqwest-eventsourcefork (aliased;reqwest_eventsource::paths unchanged). Rust 1.85+ required (was 1.75). Public API surfaces reqwest 0.13 / fork types (classify_eventsource_error,OpenApiError::HttpError). (#52)
Anthropic provider
- Adaptive thinking is the default (
thinking: {"type": "adaptive"}+output_config.effort) — required by Claude Fable 5 / Opus 4.7+/4.8 / Sonnet 5. Pre-4.6 models with thinking enabled needAnthropicCompat::legacy()(budget-based, now clamped to the API's 1024-token minimum). (#53) ModelConfig.base_urlis now honored ({base}/messages); theanthropic()preset default includes/v1(old persisted configs with the un-versioned host keep working). Custom headers apply; Bearer auth available viaAnthropicCompat.bearer_auth.- New
StopReason::Refusalvariant (safety-system declines, e.g. Claude Fable 5);model_context_window_exceededmaps to the standard overflow-error shape so compaction-retry hooks fire. anthropic()preset defaults:max_tokens8192 → 16000,reasoning: true.
Exhaustiveness policy (#33 item 1, #55)
#[non_exhaustive]onContent(matches need a wildcard arm), on theToolCall/Thinkingvariants andMessage::Assistant(construct viaContent::tool_call()/tool_call_with_metadata()/thinking()/thinking_signed()andMessage::assistant(); patterns need..), and onModelConfig(construct via presets or the newModelConfig::custom(); field mutation still works, literals/FRU do not). Future field additions become non-breaking.StopReasonstays deliberately exhaustive: a new variant should be a compile error downstream, not a silent wildcard fall-through.ModelConfiggains theanthropic: Option<AnthropicCompat>field (serde-compatible with older persisted configs).
Features
- New model presets with real pricing:
claude_fable_5(),claude_opus_4_8(),claude_sonnet_5(),claude_haiku_4_5(),gpt_5_5()— correct context windows, output caps, andCostConfigvalues. (#53) - OpenCode Zen & Go gateways (#48):
ModelConfig::opencode_zen(id)/opencode_go(id)select the API protocol from the model family (Responses / Anthropic Messages with Bearer auth / Chat Completions). Seedocs/providers/opencode.md. - Queue inspection API (#50):
steering_queue_snapshot()/follow_up_queue_snapshot(),_len(), atomictake_*_queue(), and batchsteer_all()/follow_up_all()— enables pi-style pending-message UIs. Edit-window semantics documented indocs/concepts/agent-loop.md. ModelConfig::custom(api, provider, base_url, id, name)for protocols without a preset (Bedrock, Vertex, Azure).
Fixes
- Google/Gemini: mid-stream
{"error": ...}payloads and transport failures now surface as errors (previously a silently truncated "successful" turn);SAFETY-family finish reasons map toStopReason::Refusal; SSE splitting takes the earliest separator (mixed CRLF/LF buffers no longer merge events); an empty text part no longer swallows afunctionCallin the same part. (#55) - Anthropic: a refused turn no longer poisons the conversation (empty assistant messages are skipped when serializing history) and carries an explanatory
error_message; legacy thinking budgets below the API minimum no longer 400. - CI now builds with
--all-features, so theopenapifeature is compiled and linted on every PR.
Testing
New wiremock-based provider stream tests (Anthropic + Google) cover refusal/overflow stop-reason mapping, all auth branches, thought-signature round-trips, and SSE edge cases — scenarios previously reachable only with live API keys.
Full changelog: v0.8.4...v0.9.0
v0.8.4
What's Changed
- feat:
SubAgentTool::with_skills(#46) — attach aSkillSetto a sub-agent so it sees the skills index. The index is appended to the sub-agent's system prompt at dispatch time, mirroringAgent::with_skills. Backward compatible (additive API). Closes #45.
Full Changelog: v0.8.3...v0.8.4
v0.8.3
What's Changed
- Add first-class Qwen / DashScope support via
ModelConfig::qwen(...). - Add
OpenAiCompat::qwen()for Qwen reasoning-content parsing and OpenAI-compatible token behavior. - Add
ModelConfig::openai_compat(...)for local/custom deployments that need explicit model-family compatibility flags. - Document hosted Qwen, regional DashScope URLs, local Qwen, and combined Qwen-on-Ollama compatibility.
Validation
v0.8.2
What's Changed
- Add first-class Ollama support via
ModelConfig::ollama(base_url, model_id). - Honor
requires_assistant_after_tool_resultfor OpenAI-compatible providers, fixing Ollama tool-result continuation compatibility. - Keep generic
ModelConfig::local(...)behavior neutral for other local OpenAI-compatible servers. - Preserve backward compatibility for serialized
OpenAiCompatconfigs by defaulting the new flag tofalse.
Validation
v0.8.1
What's Changed
- Updated DeepSeek support for the current V4 API shape:
- default CLI model is now
deepseek-v4-flash - DeepSeek uses
https://api.deepseek.com - DeepSeek requests use
max_tokens,thinking, andreasoning_effort - DeepSeek cache hit/miss usage fields are parsed when present
- default CLI model is now
- Added Model Presets documentation covering first-class provider constructors and DeepSeek legacy aliases.
- Fixed execution-limit accounting so cached prompt tokens count toward
max_total_tokensfor Claude and OpenAI-compatible providers. - Added regression coverage for cached-token execution limits.
Validation
cargo fmt --checkcargo test
v0.8.0
Changes since v0.7.5
Breaking
Content::ToolCallnow includesprovider_metadata: Option<serde_json::Value>(#32)
Features
- SharedState: pluggable key-value store for sub-agent communication (#35)
SharedStateBackendtrait withMemoryBackend(default) andFileBackendbackendsSubAgentTool::with_shared_state()— opt-in, injectsshared_statetool automatically
- Multi-provider sub-agents:
SubAgentTool::with_model_config()for non-Anthropic providers (#35) - Inter-turn delay:
turn_delayonAgentLoopConfigandSubAgentTool(#35) - Sub-agent error surfacing:
StopReason::Errornow propagated asToolError::Failed(#35)
Fixes
- Gemini: fix streaming and function calling (#32)
- Filter empty text blocks from sub-agent output (#35)
Examples
examples/rlm.rs— recursive 2-level agent delegation with Grokexamples/code_review.rs— 3 parallel sub-agents reviewing via shared stateexamples/shared_state.rs— parallel analysis with shared state