All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
v0.6.1 (2026-07-05)
Changed
- Hygiene: removed internal issue-tracker identifiers and CI account/role identifiers from the public surface (a source comment, test annotations, and repo docs). No API or behavior change — the library surface is identical to v0.6.0; this release simply ships a clean latest.
v0.6.0 (2026-07-04)
Added
ReqManagedAgents.Providers.Local— the third provider: the agent loop runs in-process (:request_response, one model call per turn) over a pluggablechat_funwith a neutral OpenAI-chat-completions-shaped wire contract (chat_fun.(%{model:, messages:, tools:}) :: {:ok, response} | {:error, reason}). Pointing the chat_fun at any OpenAI-compatible endpoint is a bareReq.post— including a gateway lane with a granted key for hard data-plane budget enforcement. Events are synthesized under thelocal.*namespace (local.model_response,local.duplicate_tool_call,local.directive);provision/2is identity. Limitation:Providers.Localis primarily scoped torun/2(one request per conn) — the final-turn directive counts polls across the conn's lifetime, so long-livedstart_linksessions with many follow-ups may see it fire early on later requests.- Local loop guards (relocated from biai-managed-agents
Core.Runner, for weak-instruction-following local models): duplicate-call dedup (self-answered, never re-surfaced), consecutive-error corrective directives (≥2 per tool), final-turn directive (names the spec'sterminal_tool), and transient-error retry with exponential backoff around the chat call (HTTP 408/≥500 + transport errors;max_chat_retries/retry_backoff_ms/sleep_funopts). - Optional
req_llmdependency (~> 1.10, raise-at-first-use viaLocal.Deps) backing the default chat_fun;model_config[:api_key]and[:base_url]thread into the ReqLLM call. - api_key carry-in: the Claude Managed Agents provider builds its client from
model_config: %{api_key:, base_url:}when no:clientis injected. (Bedrock AgentCore signs with SigV4 — not applicable.) - Metadata carry-in:
model_config[:metadata]merges into every telemetry event's metadata and intoSessionInfo.metadataforhandle_event/3— decision correlation (mimir_request_id,decision_id) rides uniformly across providers.
Changed
- Positioning: "one Session loop, any loop host — server-side (Claude Managed Agents, AgentCore) or in-process (Local)". README, package description, and moduledocs updated; vocabulary and result shapes unchanged.
v0.5.0 (2026-07-04)
Added
turn_guard— the between-turn governance hook, invoked after each turn's usage accumulation with%{usage: %ReqManagedAgents.Usage{}, turns:, session_id:}, returning:contor{:halt, reason}. On halt the run stops with{:error, {:halted, reason}}and a:terminatedSessionResultis notified. This contract is frozen: hosts compose policy (budget caps, grant checks) on top; RMA ships only the mechanism. Semantics: the guard runs before themax_turnscheck and wins when both would trip on the same turn; it fires on terminal-tool re-prompt turns (whoseturnscounter keeps incrementing); guards must not raise.- Terminal-tool enforcement:
require_terminal_tool: true+terminal_tool: "name"+max_reprompts(default 2). An:end_turnthat never called the terminal tool is re-driven with a re-prompt; exhausted re-prompts finish withstop_reason: :no_terminal_tool. Re-prompt turns count against:max_turns. rma.text_delta— one documented synthetic event (%{"type" => "rma.text_delta", "text" => chunk}) emitted throughhandle_eventalongside (never instead of) the raw event, on every provider that implements the new optionalProvider.text_delta/1. Never stored inSessionResult.events.- Outcomes (GH #31):
Event.define_outcome/3, the:outcomeSession option — a%ReqManagedAgents.Outcome{}struct or a map with the same atom keys — honored by the Claude Managed Agents kickoff (user.define_outcome; mutually exclusive with:prompt, outcome wins), optionalProvider.supports_outcomes?/0({:error, :outcome_unsupported}on Bedrock AgentCore), and terminal mapping for outcome stop reasons (satisfied/max_iterations_reached→:end_turn,failed→:terminated;span.outcome_evaluation_endwithneeds_revisionis not terminal). Shape validation viaReqManagedAgents.Outcome.new/1(one gate shared by the start-time check and the kickoff): a non-nil:outcomethat is not a valid struct or atom-keyed%{description: binary, rubric: binary}fails fast at start with{:error, {:invalid_opts, :outcome}}before provider-support is checked. Session.send_event/2— post a pre-built raw user event (e.g.user.tool_confirmation) into a running streaming session.
Changed
TurnResult/SessionResultstop_reasontypespec widened toString.t() | map() | atom() | nil(:no_terminal_tool).
v0.4.2 (2026-07-04)
Changed
- Internal housekeeping: source comments and test names no longer reference internal tracker ids. No behavior, API, or documentation changes.
v0.4.1 (2026-07-04)
Fixed
Session.run/2timeout now shuts down the in-flight poll task (Bedrock AgentCore invoke) and the streaming SSE consumer, so the client HTTP stream is torn down instead of continuing after the caller received{:error, :timeout}. Server-side execution may still run to the provider's own limit — on AgentCore,timeoutSecondsremains the authoritative server budget (Session moduledoc updated to match).
v0.4.0 (2026-07-04)
Added
ReqManagedAgents.Provisioner.Storebehaviour (get/2,put/3,delete/2,delete_value/2) — pluggable provision and tag storage. Two implementations:Store.ETS(default; in-process, unchanged semantics) andStore.File(JSON,path:required, atomic writes, single-writer assumption; handles and tags survive OS-process restarts for CLI/mix-task/cron consumers).:storeoption threads throughProvisioner.ensure/3,evict/2,ensure_environment/3,tag/4,resolve/2,prune_environments/3, and the facadeprovision/3/teardown/3.ReqManagedAgents.ensure_environment/3(facade; delegates toProvisioner.Environments) — content-addressed, build-if-absent environment lifecycle. Provider-side name is<base>_<digest8>; a 409 recovers by name (same name = same image, always). Returned handle:%{environment_id:, name:, digest:}. Error taxonomy:{:environment_archived, name}(name exists but archived) /{:environment_name_conflict, name}(absent after 409 — unexpected provider state).Provisioner.tag/4— writes a movablebase:tag → digestpointer to the configured store.Provisioner.resolve/2— resolves"base:tag"to a handle; never falls back;{:error, :unknown_tag}on miss; raisesArgumentErroron a malformed ref (no colon).Provisioner.prune_environments/3— explicit image GC for a named base: archives versions beyond the newestkeep:(REQUIRED, no default — deliberate friction for a permanent operation), always protecting tagged digests. Strict 8-hex suffix membership (a"data"prune never touches"data_analysis"images). Returns{:ok, %{archived: [...], kept: [...]}}or{:error, {:partial, archived_so_far, {failed_name, reason}}}on partial failure.- Base-scoped digest index (
"digest:<base>:<digest8>"store entries written at ensure time) soresolve/2can look up handles without a linear scan; the base scope prevents two bases sharing an identical spec from resolving each other's environments. Provisioner.Runtimes:runtimes: [%{lang:, version:, via: :mise}]on env specs participates in the spec digest automatically (a runtime change = a new image). When runtimes are declared,ensure_environment/3returnsbootstrap: %{script:, instructions:}in the handle (derived on every call, never stored).bootstrap_script/1renders the mise install script;system_prompt_block/1renders the agent instruction.required_hosts/1feeds the allowlist merge intonetworking.allowed_hostswhen networking is:limited. Spike-proven: ~11s end-to-end on ubuntu-24.04 (precompiled OTP via mise, no kerl compile). Onlyvia: :miseis supported.
Changed
- Provisioner cache keys namespaced: handles now stored under
"provision:" <> hash(previously bare hash). Cache-only — existing entries are invisible to the new reader (re-provisions once per BEAM start on first upgrade); no consumer API impact. - Facade
teardown/3forwards:storetoProvisioner.evict/2so teardown clears the persistent store when aStore.Fileis in use.
v0.3.0 (2026-07-03)
Known limitations
Claude Managed Agents: sandbox-written files not retrievableRESOLVED same day (post-release probe): it's the outputs-dir convention. Only files the agent writes under/mnt/session/outputs/become session artifacts (session-scoped, downloadable); files written elsewhere are non-downloadable residue.Artifacts.ClaudeFilesworks as shipped — direct your agent's deliverables to/mnt/session/outputs/in its system prompt.
Added
ReqManagedAgents.Artifacts— one vocabulary (list/fetch/put/delete, name-keyed, session-scoped) over provider-native session storage, with two stores:Artifacts.ClaudeFiles(Anthropic Files API) andArtifacts.AgentCoreSessionStorage(AgentCoresessionStoragemount, command-backed, report-scale).%Artifact{}struct.%SessionInfo{}handed to handlers via optionalHandler.handle_tool_call/4andhandle_event/3(existing 3-/2-arity handlers work unchanged);SessionResult.session_id.- CMA Files API completion:
Client.list_files/2(session-scoped viascope_id) andClient.delete_file/2, onClient.Behaviourtoo. - AgentCore: opaque
environment/environment_variablesprovision-spec fields (filesystem mounts, custom containers, env vars — pass-through, spec-hash covered) andClient.invoke_agent_runtime_command/2(direct microVM shell; streamed stdout/stderr via optionalon_output;%AgentCore.CommandResult{}).
v0.2.1 (2026-07-03)
Changed
- Internal code hygiene only — the codebase now passes
mix credo --strict(alias ordering/aliasing, implicittry), and CI gates on--strictso it stays that way. No user-facing changes.
v0.2.0 (2026-07-03)
Changed
- Bedrock AgentCore
InvokeHarnessnow streams incrementally: turns are guarded by an inter-chunkidle_timeout(default 300s) instead of a 10-minute whole-body wall clock, so long-running turns complete while dead connections fail fast. Handler.handle_event/2fires live during AgentCore turns (previously only after the turn completed) and is documented as at-least-once across retried attempts.Client.new'sreceive_timeoutnow governs control-plane calls only; the invoke data plane is guarded by the per-invokeidle_timeoutinstead (callers who usedreceive_timeoutto cap invokes should now passidle_timeout/timeout_seconds).
Added
- Per-invocation AgentCore server budgets on
Session.run/2opts:timeout_seconds,max_iterations,max_tokens(wire:timeoutSeconds/maxIterations/maxTokens). idle_timeoutopt on the AgentCore invoke path.[:req_managed_agents, :stream, :event]telemetry now also fires for AgentCore turns.
v0.1.0 (2026-07-03)
First public release. Provider-agnostic client for managed agent runtimes: the provider runs the agent loop; your custom tools execute locally on your node. The provider only ever sees each tool's name, description, input schema, and the text result you return.
Providers
ReqManagedAgents.Providers.ClaudeManagedAgents— Anthropic Claude Managed Agents (public beta,managed-agents-2026-04-01).:streamingtransport over long-lived SSE.ReqManagedAgents.Providers.BedrockAgentCore— AWS Bedrock AgentCore Harness (GA) via ConverseStream.:request_responsetransport overapplication/vnd.amazon.eventstream(decoded byaws_event_stream). The AWS dependencies (ex_aws_auth,aws_event_stream) are optional — Anthropic-only consumers don't pull them; AgentCore raises an actionable error at first use if they're missing.
Core
ReqManagedAgents.Providerbehaviour — one canonical turn vocabulary across backends:custom_tool_use/custom_tool_result(client-executed, return-of-control tools only), a three-atom terminal (:end_turn | :requires_action | :terminated), and%TurnResult{}/%SessionResult{}with per-turn token usage.- Two drivers over the same vocabulary:
ReqManagedAgents.run_to_completion/1(synchronous) andReqManagedAgents.Session(supervised GenServer; reconnect with event consolidation/deduplication, concurrent tool execution, full-history paging). ReqManagedAgents.Handlerbehaviour for local tool execution;ReqManagedAgents.Provisionerfor idempotent provider-side agent/harness provisioning and teardown.- Anthropic control plane: agents, environments, sessions, events, Files API (upload / attach / download).
- Telemetry event tree (
[:req_managed_agents, …]— request, stream, tool, session) with caller metadata injection, plus an optional OpenTelemetry bridge emittinggen_ai.*spans.
Hardening (validated against live provider APIs)
- AgentCore ConverseStream tool blocks keyed by
toolUseId(robust to index-reuse in live streams); resume turns carry both the assistanttoolUseand usertoolResultmessages, as the harness requires. - Exception/error stream frames surface as distinct errors rather than silent terminals; bounded per-turn invoke retry on transport errors and truncated streams.
- SigV4 signing is session-token aware (works with STS/OIDC temporary credentials).
- Client structs redact secrets from
inspect/1output (api_key, AWScredentials) — aKeyErrorfrom missing session opts or a crash report can't leak them into logs.