Changelog
Copy Markdownv0.9.0
Reworks the optional :horde distribution backend so cluster membership is
correct, dynamic, and scopable. Full write-up in
#134.
members: :participation — dynamic, role-scoped membership
config :sagents, :distribution, :horde
config :sagents, :horde, members: :participationMembership 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.
Also in this release
See #134 for details on each:
members: :autois now real — potential behaviour change. It previously froze to a one-time node snapshot; it now drives Horde'sNodeListenerfor genuine dynamic membership with dead-node pruning. The undocumented static:membersforms (list /function/0/{m, f, a}) are removed; the only values are:auto(default) and:participation.- Registration-timeout resilience —
AgentsDynamicSupervisor.start_agent_sync/1retries on Horde's hardcoded-5s:viaregistration timeout, via new:registration_retries/:registration_retry_backoffoptions. - FileSystem distribution-safety — dropped
Process.alive?/1checks 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.
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 #79 #116 #127 #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
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
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
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
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 diff. Configs passing tools: / tool_descriptions: to Sagents.Middleware.FileSystem must remove the "replace_file_lines" entry. #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
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
Added
- Direct-delivery transport —
Sagents.Publisher/Sagents.SubscriberreplacePhoenix.PubSubwith monitored point-to-point delivery, an:initial_subscribersstart option, and a Presence-based recovery loop for crash-restart and Horde migration. #79 - Session/Factory lifecycle in the library —
Sagents.Sessionowns the session-start lifecycle (router consult, factory invocation, state seeding, supervisor wiring, subscribers) and is idempotent on resume.Sagents.Factory/Sagents.FactoryRouterbehaviours,Sagents.Routers.Singlefor one-factory apps, and a typed%FactoryConfig{}for per-request data. #97 - Restorable interrupts — an agent that shut down (inactivity timeout, deploy, crash) with a pending
ask_userquestion or HITL approval now boots back into:interruptedstatus with the originalinterrupt_dataintact, rather than silently demoting to an error. New optionalSagents.Middleware.restorable_interrupt?/1callback,set_interrupted/3persistence callback, and cheap pre-deserializationinterrupted?/1read. #96 :haltterminal interrupt via the newSagents.Middleware.Haltable— tools can hard-stop a workflow (e.g. a gating validation tool) without giving the LLM a chance to continue. IncludesAgentServer.dismiss_interrupt/1for UIs to acknowledge a halt and a[:sagents, :agent, :halt]telemetry event. #115Sagents.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_successstop condition;run/3returns the tool'sprocessed_contentwhen present. #108 #129 #128Sagents.AgentResult— read helpers for pulling tool results, arguments, processed content, or final text out ofAgent.execute/3return values. #107:until_tooland:until_tool_successstop conditions onSagents.Agent.execute/3andSagents.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. #128Sagents.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:keysand:propagatorsconfiguration shapes. #82Sagents.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- TodoList
:inlinemode — each successfulwrite_todosadditionally persists atodo_snapshotsynthetic display message into the transcript. #101 #102 AskUserQuestionconfig pinning — optionalallow_other/allow_cancelinit options force those values for every question instead of leaving them to the LLM. #124- SubAgent
:initial_messagesfor seeding per-call messages, and:include_task_listto opt out of the auto-generated task menu. #100 #78 Sagents.AgentServer.save_synthetic_message_from/2— lets middleware persist user-facing transcript entries through the same display-message pipeline LLM messages use.AskUserQuestionrecords the user's answer this way. #88 #89Sagents.State.runtimevirtual field for process-local values that must never be persisted, withmerge_runtime/2. #84agent_idon tool execution context (context.agent_id) so tools can publish events without reaching intostate. #86- Tooling hardening: Credo, Dialyzer,
sobelow, andmix_auditwired intomix precommitand CI. #93 #90 #106
Changed
- BREAKING: Transport, SubAgent tool arguments, session/factory API, debug subscriptions, the
FileSystemtool set, andSagents.Todoids all changed — see the Upgrading section above. #79 #78 #97 #94 #110 #116 - The generated persistence templates denormalize the tool-call linking id into a dedicated indexed
tool_call_idcolumn, switching the hot tool-execution queries from a JSONBfragment(...)to indexed equality. New generations are clean; existing host apps absorb this by regenerating as described above. #127 Sagents.Middlewaredocuments the full interrupt-data catalog (:ask_user_question,:halt,:subagent_hitl, HITL action-request map,:multiple_interrupts) and the "halt wins" policy. #115- Upgraded to Elixir 1.20 and bumped the
langchaindependency floor to>= 0.8.11. #122 #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.
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, which retains every entry back to the initial release.