Current section

Files

Jump to
sagents CHANGELOG.md
Raw

CHANGELOG.md

# Changelog
## v0.10.1
Adds `:otel_attributes`, a flat map of your application's context (tenant, user,
feature) that lands on **every** OpenTelemetry span an agent produces: the
`invoke_agent` span, each `chat` span, and each `execute_tool` span, including
tools running in their own process.
```elixir
{:ok, agent} =
Sagents.Agent.new(%{
model: model,
name: "support_agent",
otel_attributes: %{
"user.id" => current_user.id,
"organization.id" => org.id,
"myapp.plan" => org.plan
},
middleware: [...]
})
```
No middleware, no callbacks, no OpenTelemetry knowledge. Additive only: nothing
changed arity or return type, and no migration is required. Requires `langchain`
0.9.5 or later for the attributes to reach spans.
Full details in the
[Observability guide](https://github.com/sagents-ai/sagents/blob/main/docs/observability.md).
### Added
- `:otel_attributes` on `Sagents.Agent`, plus `Sagents.Agent.put_otel_attributes/2`
for values that are only known after the agent is built.
[#155](https://github.com/sagents-ai/sagents/pull/155)
- An `AgentServer` now stamps its `conversation_id` onto the state it executes,
so `gen_ai.conversation.id` is set with no configuration. Combined with the
agent's `:name`, traces group by conversation and by agent out of the box.
Tools can read both from `custom_context`.
[#155](https://github.com/sagents-ai/sagents/pull/155)
- Sub-agents inherit the parent's `:otel_attributes` and conversation id, and add
their own lineage: `gen_ai.agent.id` is the sub-agent's id and
`sagents.parent_agent_id` is the parent's.
[#155](https://github.com/sagents-ai/sagents/pull/155)
- `Sagents.State.conversation_id`, a virtual field the `AgentServer` supplies on
each execution. Not persisted; the server remains the source of truth.
[#155](https://github.com/sagents-ai/sagents/pull/155)
## v0.10.0
Headlined by a **pending-message queue**: a user can now type while the agent is
working without their message being lost, and a tool can hand the model
instructions as a real user turn rather than as tool-result data.
The rest is a correctness pass on sub-agents. Interrupts raised inside a
sub-agent now behave the way their type says they should: a `:halt` reaches the
parent as a halt, a tool-raised interrupt fails cleanly instead of crashing the
caller, and sub-agents no longer receive an `ask_user` tool no one can answer.
Plus two new hooks for host applications: a middleware callback for shaping
display messages, and a way for a mode to report *why* it paused.
**No breaking API changes.** No function changed arity or return type, and no
migration is required. There are four intentional behavior changes worth knowing
about before you upgrade, described below.
### Upgrading from v0.9.0 to v0.10.0
- **`AgentServer.add_message/2` no longer returns an error while a run is in
flight.** It used to reply
`{:error, "Cannot execute, server is in state: running"}` and then discard the
message anyway; it now queues the message and returns `:ok`. The arity and the
`:ok | {:error, term()}` type are unchanged, so the compiler will not flag
this. Host code that matched that error tuple to render an "agent is busy"
notice should drop the branch and instead subscribe to
`{:agent, {:message_queued, %Message{}}}` if it wants to show queued state.
[#152](https://github.com/sagents-ai/sagents/pull/152)
- **Sub-agents no longer inherit `Sagents.Middleware.AskUserQuestion`.** If a
sub-agent genuinely needs it, name it in that sub-agent's own `:middleware`
list on its `SubAgent.Config`. Explicit configuration still receives it.
Inherited `ask_user` calls previously produced an empty approval prompt rather
than a visible question, so there is little working behavior to preserve.
[#151](https://github.com/sagents-ai/sagents/pull/151)
- **A sub-agent `:halt` now reaches the parent as `:halt`**, not as
`%{type: :subagent_hitl}`. Parent code matching on `:subagent_hitl` to catch
halts needs to match `:halt` instead.
[#150](https://github.com/sagents-ai/sagents/pull/150)
- **A failed `until_tool` termination now returns the tool's own error content**
as `{:error, content}` instead of a generic extraction failure.
[#143](https://github.com/sagents-ai/sagents/pull/143)
### Added
- **Pending-message queue on `AgentServer`.** A single-slot queue on
`ServerState` holds a message that arrives mid-run and delivers it as an
ordinary `:user` message at the head of a follow-up run, so nothing downstream
has to know where it came from. Two doors into it:
[#152](https://github.com/sagents-ai/sagents/pull/152)
- `add_message/3` (the human door) queues instead of erroring when the server
is `:running`. Non-`:user` roles are still rejected. Two messages queued
during one run merge their content parts into one turn.
- `queue_message_from_tool/3` (the tool door) lets a tool hand the model a
playbook or slash-command body as instruction rather than as tool-result
data. It is a `cast` so a tool cannot deadlock against a concurrent
`:cancel`, and returns `{:error, :no_server}` under a bare `Agent.execute/3`
or inside a sub-agent so callers can take a fallback path.
- A `:display` option on both doors splits the transcript half from the
model-visible half. `:none` is model-visible and transcript-invisible; an
explicit `%LangChain.Message{}` supplies both halves, which may differ in
role. Resolution happens at queue time, so a user sees their own words
immediately rather than a turn later.
- Drain policy is explicit per terminal clause: `{:ok, _}` drains and starts a
follow-up run, while `{:interrupt, _, _}`, `{:pause, _}` and `{:error, _}`
hold. The drained branch deliberately does not broadcast
`{:status_changed, :idle, nil}`, so a UI never flickers "done" between the
two runs.
- A circuit breaker caps the framework at 10 consecutive self-started runs.
The counter resets on the human door and never on the tool door. This is
distinct from `:max_runs`, which counts LLM calls within one execution and
resets on every fresh chain. A tripped breaker still appends the message; it
only declines to start another run.
- `pending_message` is serialized alongside state and restored at boot, so a
node dying mid-run does not lose the user's words. Payloads written before
this release simply lack the key and read as "nothing queued".
- New events: `{:agent, {:message_queued, %Message{}}}`, plus
`{:messages_drained, count}`, `{:pending_message_held, :error}` and
`{:auto_execution_limit_reached, limit}` on the debug channel.
- Known scope limit: delivery is at the **run** boundary, not the turn
boundary. A message typed twenty tool calls into a long job is late, not
lost. Turn-boundary delivery is deliberately out of scope.
- New optional middleware callback `transform_display_message/2`, the outbound
mirror of `MessagePreprocessor`. Each middleware gets a chance to annotate
`metadata` (or rewrite content) on a message before it is persisted as a
display message and broadcast to subscribers. The message in agent state is
left untouched, so the LLM never sees the annotation. Passthrough default;
composes across the stack. [#139](https://github.com/sagents-ai/sagents/pull/139)
- Pause cause on the `:paused` status event. A mode step may now return
`{:pause, chain, reason}`; `Sagents.Mode.Steps.normalize_pause/1` folds the
reason into `custom_context.pause_reason`, the agent reads it onto the new
virtual `State.pause_reason` field, and `AgentServer` broadcasts it as the
payload of `{:agent, {:status_changed, :paused, pause_reason}}`, which was
previously always `nil`. `Agent.execute/3` still returns `{:pause, state}`, and
a pause without a reason broadcasts `nil` as before.
[#149](https://github.com/sagents-ai/sagents/pull/149)
### Changed
- `AgentServer.add_message/2` returns `:ok` instead of an error when a run is in
flight, because the message is now queued rather than rejected. See the
Upgrading section. [#152](https://github.com/sagents-ai/sagents/pull/152)
- `langchain` moves from 0.8.12 to 0.9.4 in `mix.lock`, along with transitive
bumps to `ecto`, `finch`, `mint`, `hpax`, `plug`, `plug_crypto` and `req`. The
`mix.exs` requirement is unchanged; `>= 0.8.11` already allowed this.
[#152](https://github.com/sagents-ai/sagents/pull/152)
- `Sagents.Middleware.AskUserQuestion` is added to a new
`@never_inherited_middleware` list alongside `Sagents.Middleware.SubAgent`, so
neither is inherited by a sub-agent from its parent's stack. Explicitly
configured `additional_middleware` is unaffected.
[#151](https://github.com/sagents-ai/sagents/pull/151)
- CI workflow dependency bumps: `actions/checkout` 6.0.2 → 7.0.0
([#132](https://github.com/sagents-ai/sagents/pull/132)), `actions/cache` and
its `save`/`restore` variants 5.0.5 → 6.1.0
([#136](https://github.com/sagents-ai/sagents/pull/136),
[#137](https://github.com/sagents-ai/sagents/pull/137),
[#138](https://github.com/sagents-ai/sagents/pull/138)), and `erlef/setup-beam`
1.24.0 → 1.24.1 ([#140](https://github.com/sagents-ai/sagents/pull/140)).
### Fixed
- A user message added while the agent was `:running` is no longer silently
destroyed. It was written into the rolling server state, then wiped moments
later when `handle_execution_result/2` replaced that state wholesale with the
canonical state from `Agent.execute/3`, while the caller was told the server
was busy. The message is now queued and delivered on the next run.
[#152](https://github.com/sagents-ai/sagents/pull/152)
- A `:halt` raised inside a sub-agent is propagated to the parent as a halt
instead of being wrapped as `:subagent_hitl`. Previously the wrapper defeated
every guarantee halt makes: the parent resumed and called the LLM again, the
interrupt was not restorable across a cold start, and the author's message was
never shown, so the user saw an empty approval dialog. The sub-agent process is
now stopped, and the propagated halt preserves `:source_tool` and adds
`:source_task`. Applies to both the initial run and a post-approval resume.
[#150](https://github.com/sagents-ai/sagents/pull/150)
- `SubAgent.extract_result/1` now reads the matched terminating tool's result on
an `until_tool` run. Every `until_tool` termination ends on a tool-result
message, which the previous `ChainResult.to_string/1` path could not handle, so
extraction failed on success and failure alike and the parent received a
generic error. A tool result flagged `is_error` becomes `{:error, content}`;
runs ending in assistant prose are unchanged. Fixes
[#141](https://github.com/sagents-ai/sagents/issues/141).
[#143](https://github.com/sagents-ai/sagents/pull/143)
- `SubAgent.resume/3` no longer crashes with a `KeyError` when the sub-agent's
interrupt was raised from inside a tool body. Such an interrupt carries neither
`:action_requests` nor `:hitl_tool_call_ids`, and is not resumable through this
path; it now returns `{:error, {:unsupported_interrupt, :tool_raised}}`, which
`Middleware.SubAgent.handle_resume/5` already turns into a clean error tool
result for the parent. Fixes
[#142](https://github.com/sagents-ai/sagents/issues/142).
[#144](https://github.com/sagents-ai/sagents/pull/144)
- Creating a file with empty or whitespace-only content no longer discards it.
Ecto's default `:empty_values` treated `""`, `"\n"`, `" "`, and `"\t"` as
absent and replaced them with `nil` while still reporting a successful write.
`FileEntry.internal_changeset/2` now passes `empty_values: []` so content is
stored verbatim. Only the first write to a path was affected; overwrites bypass
the changeset and always worked.
[#147](https://github.com/sagents-ai/sagents/pull/147)
## v0.9.0
Reworks the optional `:horde` distribution backend so cluster membership is
correct, dynamic, and scopable. Full write-up in
[#134](https://github.com/sagents-ai/sagents/pull/134).
### `members: :participation` — dynamic, role-scoped membership
config :sagents, :distribution, :horde
config :sagents, :horde, members: :participation
Membership becomes exactly the nodes that run `Sagents.Supervisor`, discovered
via an OTP `:pg` group and kept current on `:nodeup`/`:nodedown` by the new
`Sagents.Horde.MembershipManager`. Gate `Sagents.Supervisor` to your
agent-hosting role(s) and membership follows automatically — no node-name
predicate, and dead nodes are pruned for free. Prefer this over `:auto` whenever
the Erlang cluster also contains nodes that should not host agents.
### `:partition` — isolate participation into independent groups
config :sagents, :horde,
members: :participation,
partition: System.get_env("FLY_REGION")
An optional per-node `:partition` (any stable, opaque grouping key) scopes
membership further so a node only clusters with same-partition nodes. The
motivating case is geographic — set it to a Fly.io `FLY_REGION` so an agent for
an Illinois user is never placed on, or routed through, a node in France — but it
works for any per-node grouping. Cross-partition request routing remains an
infra/app concern (e.g. Fly `fly-replay`). See
[`docs/clustering.md`](https://github.com/sagents-ai/sagents/blob/main/docs/clustering.md).
### Also in this release
See [#134](https://github.com/sagents-ai/sagents/pull/134) for details on each:
- **`members: :auto` is now real** — potential behaviour change. It previously
froze to a one-time node snapshot; it now drives Horde's `NodeListener` for
genuine dynamic membership with dead-node pruning. The undocumented static
`:members` forms (list / `function/0` / `{m, f, a}`) are removed; the only
values are `:auto` (default) and `:participation`.
- **Registration-timeout resilience**`AgentsDynamicSupervisor.start_agent_sync/1`
retries on Horde's hardcoded-5s `:via` registration timeout, via new
`:registration_retries` / `:registration_retry_backoff` options.
- **FileSystem distribution-safety** — dropped `Process.alive?/1` checks on
potentially-remote pids that could raise.
## v0.8.0
A large release that reworks the runtime foundations of the library: a new direct point-to-point event transport (replacing `Phoenix.PubSub`), session/factory lifecycle ownership moved into the library, interrupts that survive a process restart, a richer interrupt model (`:halt`, configurable `ask_user`), structured data extraction through the full middleware stack, cross-process caller-context propagation, and new tool-driven stop conditions.
This entry consolidates everything relevant to upgrading from the previous public release, **`v0.7.x`**. The `v0.8.0` line went through 13 release candidates; several breaking changes were introduced and then superseded *within* the RC cycle and therefore do not affect anyone moving directly from `v0.7.x` to `v0.8.0`. For the complete, blow-by-blow history of every intermediate change, see the archived [`v0.8.0-rc.13` changelog](https://github.com/sagents-ai/sagents/blob/v0.8.0-rc.13/CHANGELOG.md).
**Breaking changes** — see the Upgrading section below.
### Upgrading from v0.7.x to v0.8.0
The recommended path is to **re-run the generators on a clean, committed workspace and merge your customizations back in**, then apply a handful of host-code renames.
**1. Regenerate scaffolding.** Run `mix sagents.setup` (or the individual `mix sagents.gen.*` tasks) with the same options you used originally, accept the overwrites, and merge your customizations back with a diff tool. This absorbs the structural changes in one step: the new `Session` / `Factory` / `FactoryRouter` triad (replacing the old monolithic `coordinator.ex` + `factory.ex`), the new `agent_subscriber_session.ex` template, integer todo ids in `valid_todo_entry?/1`, the denormalized `tool_call_id` column in the persistence schema/context, and restorable-interrupt support. [#97](https://github.com/sagents-ai/sagents/pull/97) [#79](https://github.com/sagents-ai/sagents/pull/79) [#116](https://github.com/sagents-ai/sagents/pull/116) [#127](https://github.com/sagents-ai/sagents/pull/127) [#96](https://github.com/sagents-ai/sagents/pull/96)
**2. Transport: `Sagents.PubSub` is removed.** Replace any direct `Sagents.PubSub.subscribe/1` / `broadcast/2` calls with `use Sagents.Subscriber` plus the generated `subscribe/2` helper, or pass `:initial_subscribers` when starting servers to enroll the caller inside `init/1` and avoid the start/subscribe race. Existing `handle_info/2` clauses keep matching — event payload shapes (`{:agent, _}`, `{:file_system, _}`, `{:status_changed, _, _}`, `{:llm_deltas, _}`, etc.) are unchanged. [#79](https://github.com/sagents-ai/sagents/pull/79)
**3. SubAgent: `subagent_type``task_name`.** The `task` and `get_task_instructions` tools now take `task_name`. Rename the key in any interrupt-data pattern match (`%{type: :subagent_hitl, task_name: type, sub_agent_id: id}`) and in any `context.resume_info` maps you build for sub-agent resume. Persisted v1 state is migrated to v2 automatically by `StateSerializer`. The available-tasks listing moved into an `## Available Tasks` system-prompt section (suppressible via `:include_task_list`); update any custom prompts referencing the old wording. [#78](https://github.com/sagents-ai/sagents/pull/78)
**4. Session API rename.** `Coordinator.ensure_session_running/1` is now `ensure_agent_session_running/1` — update LiveViews, controllers, and tests. Factory helpers that were `get_model/0` / `get_middleware/0` become `build_model/1` / `build_middleware/1`, branching on a `%FactoryConfig{}` struct. Per-request data (timezone, tool_context, project records) now flows through `request_opts → FactoryRouter.resolve/3 → %FactoryConfig{} → Factory.create_agent/2` rather than being threaded as positional args. [#97](https://github.com/sagents-ai/sagents/pull/97)
**5. Debug subscriptions.** `AgentServer.subscribe_debug/1` / `unsubscribe_debug/1` are removed in favor of `AgentServer.subscribe(agent_id, :debug)` / `unsubscribe(agent_id, :debug)`. The single-arg `subscribe(agent_id)` form is unchanged. [#94](https://github.com/sagents-ai/sagents/pull/94)
**6. FileSystem: `replace_file_lines` removed.** If your config, prompts, or evals reference it, either drop it (`replace_file_text` covers the same use cases for most agents) or re-add it as a project-local tool — the previous implementation lives in the [#110](https://github.com/sagents-ai/sagents/pull/110) diff. Configs passing `tools:` / `tool_descriptions:` to `Sagents.Middleware.FileSystem` must remove the `"replace_file_lines"` entry. [#110](https://github.com/sagents-ai/sagents/pull/110)
**7. Todo ids are integers.** Host code calling `Sagents.Todo.new/1`, `State.get_todo/2`, or `State.delete_todo/2` with string ids must switch to integers (`Todo.new/1` now validates `greater_than: 0`). Code that builds todos from incoming maps should migrate to `Sagents.Todo.list_from_maps/1`, which assigns positional defaults for missing/non-numeric ids and coerces stringified integers. Persisted snapshots with legacy base64 string ids rehydrate to positional ids automatically on load. [#116](https://github.com/sagents-ai/sagents/pull/116)
**8. Opt middleware into interrupt restoration (optional).** Custom middleware that produce restorable, data-only `interrupt_data` should implement `Sagents.Middleware.restorable_interrupt?/1` returning `true` for matching shapes. The default of `false` preserves the old safe demote-on-load behaviour with no code changes. Built-in `AskUserQuestion` and `HumanInTheLoop` already opt in; `SubAgent` deliberately does not. [#96](https://github.com/sagents-ai/sagents/pull/96)
### Added
- **Direct-delivery transport**`Sagents.Publisher` / `Sagents.Subscriber` replace `Phoenix.PubSub` with monitored point-to-point delivery, an `:initial_subscribers` start option, and a Presence-based recovery loop for crash-restart and Horde migration. [#79](https://github.com/sagents-ai/sagents/pull/79)
- **Session/Factory lifecycle in the library**`Sagents.Session` owns the session-start lifecycle (router consult, factory invocation, state seeding, supervisor wiring, subscribers) and is idempotent on resume. `Sagents.Factory` / `Sagents.FactoryRouter` behaviours, `Sagents.Routers.Single` for one-factory apps, and a typed `%FactoryConfig{}` for per-request data. [#97](https://github.com/sagents-ai/sagents/pull/97)
- **Restorable interrupts** — an agent that shut down (inactivity timeout, deploy, crash) with a pending `ask_user` question or HITL approval now boots back into `:interrupted` status with the original `interrupt_data` intact, rather than silently demoting to an error. New optional `Sagents.Middleware.restorable_interrupt?/1` callback, `set_interrupted/3` persistence callback, and cheap pre-deserialization `interrupted?/1` read. [#96](https://github.com/sagents-ai/sagents/pull/96)
- **`:halt` terminal interrupt** via the new `Sagents.Middleware.Haltable` — tools can hard-stop a workflow (e.g. a gating validation tool) without giving the LLM a chance to continue. Includes `AgentServer.dismiss_interrupt/1` for UIs to acknowledge a halt and a `[:sagents, :agent, :halt]` telemetry event. [#115](https://github.com/sagents-ai/sagents/pull/115)
- **`Sagents.Extract`** — structured data extraction that flows through the agent's full middleware stack. The submit tool is owned by the agent and selected via the `:until_tool` / `:until_tool_success` stop condition; `run/3` returns the tool's `processed_content` when present. [#108](https://github.com/sagents-ai/sagents/pull/108) [#129](https://github.com/sagents-ai/sagents/pull/129) [#128](https://github.com/sagents-ai/sagents/pull/128)
- **`Sagents.AgentResult`** — read helpers for pulling tool results, arguments, processed content, or final text out of `Agent.execute/3` return values. [#107](https://github.com/sagents-ai/sagents/pull/107)
- **`:until_tool` and `:until_tool_success` stop conditions** on `Sagents.Agent.execute/3` and `Sagents.SubAgent` — complete a run when a target tool is called (or, for `:until_tool_success`, returns a non-error result), enabling the validate-and-retry pattern. [#128](https://github.com/sagents-ai/sagents/pull/128)
- **`Sagents.Middleware.ProcessContext`** — propagates caller-process state (OpenTelemetry trace context, Sentry context, request-scoped Logger metadata, tenant scope) across the three process boundaries an agent invocation crosses, via `:keys` and `:propagators` configuration shapes. [#82](https://github.com/sagents-ai/sagents/pull/82)
- **`Sagents.StreamingSession`** — host-agnostic streaming helpers (`handle_tool_call_identified/2`, `handle_tool_execution_update/3`) returning *changes maps* the host merges itself, with multi-tool-safe delta semantics. [#104](https://github.com/sagents-ai/sagents/pull/104)
- **TodoList `:inline` mode** — each successful `write_todos` additionally persists a `todo_snapshot` synthetic display message into the transcript. [#101](https://github.com/sagents-ai/sagents/pull/101) [#102](https://github.com/sagents-ai/sagents/pull/102)
- **`AskUserQuestion` config pinning** — optional `allow_other` / `allow_cancel` init options force those values for every question instead of leaving them to the LLM. [#124](https://github.com/sagents-ai/sagents/pull/124)
- **SubAgent `:initial_messages`** for seeding per-call messages, and **`:include_task_list`** to opt out of the auto-generated task menu. [#100](https://github.com/sagents-ai/sagents/pull/100) [#78](https://github.com/sagents-ai/sagents/pull/78)
- **`Sagents.AgentServer.save_synthetic_message_from/2`** — lets middleware persist user-facing transcript entries through the same display-message pipeline LLM messages use. `AskUserQuestion` records the user's answer this way. [#88](https://github.com/sagents-ai/sagents/pull/88) [#89](https://github.com/sagents-ai/sagents/pull/89)
- **`Sagents.State.runtime`** virtual field for process-local values that must never be persisted, with `merge_runtime/2`. [#84](https://github.com/sagents-ai/sagents/pull/84)
- **`agent_id` on tool execution context** (`context.agent_id`) so tools can publish events without reaching into `state`. [#86](https://github.com/sagents-ai/sagents/pull/86)
- Tooling hardening: Credo, Dialyzer, `sobelow`, and `mix_audit` wired into `mix precommit` and CI. [#93](https://github.com/sagents-ai/sagents/pull/93) [#90](https://github.com/sagents-ai/sagents/pull/90) [#106](https://github.com/sagents-ai/sagents/pull/106)
### Changed
- **BREAKING:** Transport, SubAgent tool arguments, session/factory API, debug subscriptions, the `FileSystem` tool set, and `Sagents.Todo` ids all changed — see the Upgrading section above. [#79](https://github.com/sagents-ai/sagents/pull/79) [#78](https://github.com/sagents-ai/sagents/pull/78) [#97](https://github.com/sagents-ai/sagents/pull/97) [#94](https://github.com/sagents-ai/sagents/pull/94) [#110](https://github.com/sagents-ai/sagents/pull/110) [#116](https://github.com/sagents-ai/sagents/pull/116)
- The generated persistence templates denormalize the tool-call linking id into a dedicated indexed `tool_call_id` column, switching the hot tool-execution queries from a JSONB `fragment(...)` to indexed equality. New generations are clean; existing host apps absorb this by regenerating as described above. [#127](https://github.com/sagents-ai/sagents/pull/127)
- `Sagents.Middleware` documents the full interrupt-data catalog (`:ask_user_question`, `:halt`, `:subagent_hitl`, HITL action-request map, `:multiple_interrupts`) and the "halt wins" policy. [#115](https://github.com/sagents-ai/sagents/pull/115)
- Upgraded to Elixir 1.20 and bumped the `langchain` dependency floor to `>= 0.8.11`. [#122](https://github.com/sagents-ai/sagents/pull/122) [#106](https://github.com/sagents-ai/sagents/pull/106)
For the per-RC `Added` / `Changed` / `Fixed` detail behind this summary — including bug fixes resolved within the RC cycle — see the archived [`v0.8.0-rc.13` changelog](https://github.com/sagents-ai/sagents/blob/v0.8.0-rc.13/CHANGELOG.md).
---
Changelog entries for `v0.1.0` through `v0.7.0` have been removed to give the `v0.8.0` line a clean slate. The full detailed history remains available in git — see the [`v0.8.0-rc.13` changelog](https://github.com/sagents-ai/sagents/blob/v0.8.0-rc.13/CHANGELOG.md), which retains every entry back to the initial release.