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.
Unreleased
0.8.0 - 2026-02-10
Added
- Persistence subsystem with production-grade adapters and maintenance/query surfaces
EctoSessionStoreadapter with migrations and schemas (supports PostgreSQL, SQLite viaEcto.Adapters.SQLite3)S3ArtifactStoreadapter for S3-compatible object storageCompositeSessionStoreadapter combining SessionStore + ArtifactStoreQueryAPIandMaintenanceports with Ecto implementations (EctoQueryAPI,EctoMaintenance)- Persistence modules:
EventPipeline,EventValidator,RetentionPolicy,ArtifactRegistry
flush/2andappend_events/2callbacks on theSessionStoreportflush/2atomically persists final session/run state (and any queued events) in transactional backends like Ectoappend_events/2persists a batch of events with atomic sequence assignment (used byEventPipeline.process_batch/3)- Implemented in
EctoSessionStore,InMemorySessionStore, andCompositeSessionStore
EventBuildermodule (AgentSessionManager.Persistence.EventBuilder) for pure event normalize/enrich/validate processing without persistenceExecutionStatemodule (AgentSessionManager.Persistence.ExecutionState) for in-memory run state accumulation (session, run, events, provider metadata)OptionalDependencymodule for standardized error reporting when optional dependencies (Ecto, ExAws) are missingRuntime.ExitReasonsmodule for shared adapter/store exit-reason classification across SessionManager + provider boundaries- SessionManager subsystem modules for cleaner orchestration boundaries
InputMessageNormalizerProviderMetadataCollector
- Top-level persistence convenience API on
AgentSessionManagersession_store/1forSessionStorerefs (pidor{Module, context})query_api/1forQueryAPIrefsmaintenance/1forMaintenancerefs
- Conditional compilation for optional Ecto, AWS, and provider SDK dependencies
EctoSessionStore,EctoQueryAPI,EctoMaintenance, Ecto migrations/schemas wrapped in conditional blocksS3ArtifactStore.ExAwsClientprovides fallback when ExAws is missingArtifactRegistryEcto-dependent metadata tracking wrapped with fallback logicAmpAdapter,ClaudeAdapter, andCodexAdaptercompile only when their SDK deps are available- Library can be used without Ecto, ExAws, or provider SDK dependencies installed
SessionManagerstore-failure hardening- All
SessionStorecalls wrapped insafe_store_call/2 - Store process exits converted to
:storage_connection_failederrors - Prevents
SessionServerexecution tasks from crashing when store teardown races with failed-run finalization
- All
ProviderAdaptererror normalizationcall_with_error/4andcall_exit_error/4for structured exit normalization:noproc,:timeout, and unexpected exits normalized intoCore.Errorstructsprovider_unavailabletolerance incancel_runrequest path
Core.Serializationmodule consolidatingatomize_keys/1andstringify_keys/1acrossEctoSessionStoreandEctoQueryAPI- Shared Ecto converters module (
EctoSessionStore.Converters) used by both store and query adapters for schema <-> core transformations - Ecto migrations (V1 base schema, V2 provider/artifact fields, V3 foreign keys) and Ecto schemas for sessions, runs, and events
Eventstruct extensions:schema_version,provider,correlation_idfieldsSessionstruct extension:deleted_atfor soft-delete support- Persistence test hardening suites for regression resistance
ecto_session_store_concurrency_test.exs(concurrent append/session consistency)session_store_contract_multi_impl_test.exs(contract tests across InMemory + Ecto refs)ecto_session_store_migration_compat_test.exsandecto_session_store_migration_down_test.exs(migration prerequisite + rollback coverage)
- Persistence live examples:
sqlite_session_store_live.exs,ecto_session_store_live.exs,s3_artifact_store_live.exs,composite_store_live.exs,persistence_query.exs,persistence_maintenance.exs,persistence_multi_run.exs,persistence_live.exs,persistence_s3_minio.exs - Approval gates and human-in-the-loop policy enforcement
- New
request_approvalpolicy action as a validon_violationvalue, emittingtool_approval_requestedevents without automatically cancelling the run - Strictness hierarchy for policy stacking:
cancel>request_approval>warn - Core event types for
tool_approval_requested,tool_approval_granted, andtool_approval_deniedwith validation and persistence support cancel_for_approval/4inSessionManagerfor streamlined run cancellation pending human reviewTranscriptBuildersynthetic tool responses when a run is awaiting approval, reflecting the paused state in the transcript- Comprehensive tests for runtime enforcement and stacking,
approval_gates.exsexample, and updated policy enforcement guide
- New
- Interactive interrupt example (
interactive_interrupt.exs) demonstrating mid-stream run cancellation and follow-up prompts within the same session - Case-insensitive tool name matching in
approval_gates.exspolicy rules (lowercase and title case variants) - Shell Runner (
ShellAdapterprovider andWorkspace.Exec)ShellAdapterimplementsProviderAdapteras a GenServer withTask.Supervisor-backed concurrent execution- Three input formats: string (shell-wrapped via
/bin/sh -c), structured map (explicit command/args/cwd), and messages (compatibility extraction from user content) - Full canonical event sequence:
run_started,tool_call_started,tool_call_completed/failed,message_received,run_completed/failed/cancelledwithprovider: :shellattribution - Security controls:
allowed_commandsanddenied_commandslists with denylist-takes-precedence semantics - Configurable
success_exit_codesfor treating non-zero exits as success - Cancellation kills both the Elixir task and underlying OS process via pid
Workspace.Execfor low-level command execution viaPort.openwith timeout, output capture, max output bytes truncation, environment variables, and OS process managementrun_streaming/3returns lazyStream.resourcefor incremental outputon_outputcallback support in structured map input for streaming- Advertises
command_executionandstreamingcapabilities - New guide:
guides/shell_runner.md - New example:
shell_exec.exsdemonstrating mixed provider + shell execution
- CodexAdapter
ItemStarted/ItemCompletedevent handling- Emit
tool_call_startedandtool_call_completedevents for Codex command execution, file change, and MCP tool call items - Normalize
CommandExecutionandFileChangeitem types to canonical tool event payloads - ID-based
agent_messagestracking withstreamed_agent_message_idsto avoid content duplication across multi-message turns active_toolsstate preserves tool inputs during the call lifecycle
- Emit
- Ash Framework persistence integration as an optional alternative to raw Ecto adapters
- New Ash resources and domain for
asm_*tables - New adapters:
AshSessionStore,AshQueryAPI, andAshMaintenance - Atomic per-session sequence assignment via
AssignSequence - Ash-focused tests, guide (
guides/ash_session_store.md), and live example (examples/ash_session_store.exs)
- New Ash resources and domain for
- PubSub integration for real-time event broadcasting via Phoenix PubSub
PubSubSinkrendering sink broadcasts events to configurable PubSub topicsAgentSessionManager.PubSubhelper module withevent_callback/2andsubscribe/2AgentSessionManager.PubSub.Topicfor canonical topic naming (asm:session:{id},asm:session:{id}:run:{rid})- Optional dependency on
phoenix_pubsub ~> 2.1with graceful stub fallback
AgentSessionManager.WorkflowBridgemodule for workflow/DAG engine integrationWorkflowBridge.StepResultnormalized result type with routing signalsWorkflowBridge.ErrorClassificationfor retry/failover/abort decisionsstep_execute/3supporting one-shot and multi-run execution modessetup_workflow_session/3andcomplete_workflow_session/3lifecycle helpersclassify_error/1mapping ASM errors to workflow routing actionsexamples/workflow_bridge.exslive exampleguides/workflow_bridge.mdintegration guide- Secrets redaction for persisted events (
EventRedactormodule)- Configurable pattern-based secret detection and
[REDACTED]replacement - Integrates into
EventBuilderpipeline between build and enrich steps - Default patterns for AWS keys, AI provider tokens, GitHub PATs, JWTs, passwords, connection strings, private keys, environment variables
- Custom pattern support via application config or per-pipeline context override
redact_map/2public API for user callback and telemetry handler wrapping- Opt-in via
redaction_enabled: true(default: disabled for backward compatibility) - Telemetry event
[:agent_session_manager, :persistence, :event_redacted]emitted on redaction - Five new
Configkeys:redaction_enabled,redaction_patterns,redaction_replacement,redaction_deep_scan,redaction_scan_metadata
- Configurable pattern-based secret detection and
StudioRenderer— CLI-grade interactive renderer for terminal display- Human-readable tool summaries instead of raw JSON output
- Status symbols:
◐(running),✓(success),✗(failure),●(info) - Three tool output modes:
:summary,:preview,:full - Clean text streaming with indentation and visual phase separation
- Automatic non-TTY fallback (no cursor control when piped)
Studio.ANSIshared utility module for colors, cursor control, and symbolsStudio.ToolSummaryper-tool summarization for bash, Read, Write, Edit, Glob, Grep, Task, WebFetch, WebSearch
- New example:
rendering_studio.exsdemonstrating StudioRenderer with all three tool output modes - New guide section: StudioRenderer in
guides/rendering.md CostCalculatorwith model-aware pricing and USD cost tracking (AgentSessionManager.Cost.CostCalculator)- Three-tier model resolution: exact match, prefix match with date suffix stripping, provider default fallback
- Cache token support for Claude prompt caching (
cache_read_tokens,cache_creation_tokens) cost_usdfield added toRunstruct with serialization supportget_cost_summary/2callback onQueryAPIwith per-run aggregation (total, by_provider, by_model)- Policy
Runtimemodel-aware rate resolution viaCostCalculator.resolve_rates/3 MigrationV4addingcost_usdfloat column toasm_runstable- Cost display in
CompactRenderer,VerboseRenderer, andJSONLSink SessionManagerfinalization prefers SDK-providedtotal_cost_usd, falls back toCostCalculator- New guide:
guides/cost_tracking.md - New example:
cost_tracking.exs
AgentSessionManager.Configcentralization of all operational defaults- All library timeouts, buffer sizes, and limits moved into centralized Config with three-layer resolution (process-local, application env, hardcoded fallback)
- New guide: Configuration Reference
AgentSessionManager.Modelscentral registry for model identifiers and pricing- Runtime model name, default provider model, and per-token pricing lookup
AgentSessionManager.Test.Modelsfor test fixture management- New guide: Model Configuration
Changed
SessionStoreis the single execution persistence boundary -- event persistence remains per-event viaappend_event_with_sequence/2, withappend_events/2available for batch paths andflush/2used during finalizationSessionStorerefs now support bothpidand{Module, context}dispatch -- Ecto-backed usage can call adapters directly (for example{EctoSessionStore, Repo}) instead of routing through a single GenServerQueryAPIandMaintenancerefactored to module-backed refs --{EctoQueryAPI, Repo}and{EctoMaintenance, Repo}replace dedicated GenServer adapters, removing process lifecycle overheadEventPipelineprocessing -- builds events viaEventBuilder;process/3persists per-event withappend_event_with_sequence/2, whileprocess_batch/3persists viaappend_events/2- Session lifecycle event persistence unified through
EventPipelineso internal lifecycle emissions and adapter callback events use the same validation/persistence path - Session finalization integrates
ExecutionStatefor consistent in-memory accumulation and final flush behavior - Provider metadata extraction no longer depends on run-scoped
SessionStore.get_events/3read-back; metadata is captured from callback/result event data during execution ArtifactRegistrydecoupled from adapter schema modules via behaviour-based injection to preserve persistence layeringInMemorySessionStoreevent appends moved to queue-based buffering to avoid O(n) append overhead under loadCompositeSessionStoredirect-call timeout handling now propagateswait_timeout_msand isolates artifact failures from session persistence pathscrypto.strong_rand_bytesused for workspace artifact keys instead ofSystem.unique_integer- Current-only contract cleanup (backward-compatibility shims removed):
- Boolean continuation value
trueis no longer accepted; use:auto,:replay,:native, orfalse - Adapter tool events emit canonical keys only:
tool_call_id,tool_name,tool_input,tool_output; provider-native aliases (call_id,tool_use_id,arguments,input,output,content) removed TranscriptBuilderand renderers consume canonical tool keys only; fallback chains through legacy keys removedEvent.from_map/1requires explicitschema_version(integer >= 1);nil/missing no longer defaults to 1QueryAPIandMaintenanceaccept module-backed{Module, context}refs only; GenServer dispatch paths return:validation_errorSessionServer.status/1no longer includes the legacyactive_run_idstatus field- Session provider metadata is stored under
session.metadata[:provider_sessions]without top-level duplication; top-level:provider_session_idand:modelkeys are no longer merged
- Boolean continuation value
- ClaudeAdapter refactoring: removed
execute_with_mock_sdkpath and receive-based event stream processing (~300 lines); added stop_reason tracking through streaming context,:cancelledresult subtype handling from SDK stream, and extractedextract_usage_counts,extract_stop_reason,emit_success_events,maybe_emit_run_completedhelpers EventBuilderno longer duplicates provider intoevent.metadata; provider lives onEvent.providerstruct field onlyEventNormalizerremoves type inference heuristics (content-key sniffing, status-based refinement, fuzzy type mappings);resolve_typevalidates againstEvent.valid_type?/1using canonical names onlyMockSDKgainsquery/3interface returningStream.resourcewithClaudeAgentSDK.Messageemission, including tool_use content block handling and error scenarioscodex_sdkdependency updated from path ref to~> 0.8.0hex package- PubSub defaults are now configured per use (
PubSubSink/PubSub.event_callback) and are no longer exposed as globalAgentSessionManager.Configkeys FileSinkANSI strip regex extended to also remove cursor control sequences (\r,\e[2K,\e[NA)- Execution timeout propagated to underlying SDK options: Amp (
stream_timeout_ms) and Claude (timeout_ms) with configurable fallback - Unbounded/infinite timeout support across all adapters with 7-day (604,800,000 ms) emergency cap to prevent runaway processes
- Codex adapter
sdk_optsselective merge filtering against target module keys (supports both atom and string keys) - Codex adapter extracts confirmed model names and reasoning effort from thread metadata events
- Amp adapter includes model state in execution context
- Ash query filtering enhancements:
min_tokensfilter,since/untiltemporal filtering, multi-status filtering, strict limit validation, improved cursor pagination - All adapters (Shell, Ecto, Ash) use dynamic configuration from
Configmodule RetentionPolicypulls defaults from central ConfigConcurrencyLimiteruses configurable parallel session limits from ConfigCostCalculatoruses centralized pricing table fromModelsClaudeAdapterand examples use dynamic default model fromModels- Ash adapters use
exprmacro in query filters; large functions broken into focused helpers WorkflowBridgekeyword list normalization improved for adapter options and continuation parameters- Ash tests gated behind
RUN_ASH_TESTSenvironment variable - Provider adapters (
CodexAdapter,AmpAdapter,ClaudeAdapter) now normalize provider failures into a stableprovider_errorpayload and preserve provider-specific extras inError.details - Session/run failure paths now persist and emit structured provider diagnostics (
provider_error,details) alongside legacyerror_message - Added shared
AgentSessionManager.Core.ProviderErrornormalization to enforce the contract:provider,kind,message,exit_code,stderr,truncated?- Truncation limits are configurable via
:error_text_max_bytesand:error_text_max_lines
Fixed
- Cursor pagination correctness (C-1) -- cursor tokens are decoded and applied via keyset filters with explicit ordering validation; invalid cursors return structured errors instead of silently repeating page 1
delete_session/2and hard-delete cascade safety (C-2, C-5) -- session/run/event deletion paths are transactional and delete dependent rows deterministically to prevent orphaned records- Event persistence error handling (C-3) --
EventPipelinepersistence failures are surfaced to callers and SessionManager callback/store interactions are protected from teardown crashes - Ecto insert crash path (C-4) -- bang inserts in GenServer call paths replaced with safe insert + rollback handling to avoid store process crashes
- Migration prerequisite mismatch (C-6) -- EctoSessionStore now fails fast when required V2 columns are missing, preventing runtime crashes from V1-only schemas
- Maintenance correctness (M-1, M-2, M-9) -- health checks use
max(sequence_number), retention age checks useupdated_at, and protected prune event types includerun_started/run_completed - Optional dependency error semantics (M-10) -- missing optional dependencies now normalize to
:dependency_not_availableinstead of generic storage failure codes - Composite direct call timeout behavior (M-8 scoped) --
CompositeSessionStore.get_events/3correctly adjusts call timeout for long-poll options - Event redaction now scans nested
provider_errorpayloads (includingstderr) on failure events (:error_occurred,:run_failed,:session_failed)
Removed
- Persistence abstractions removed in favor of
SessionStore+flush/2:AgentSessionManager.Ports.DurableStoreAgentSessionManager.Adapters.NoopStoreAgentSessionManager.Adapters.SessionStoreBridge
- Raw
SQLiteSessionStorereplaced byEctoSessionStore+ecto_sqlite3 - Event emitter path removed; event build/validation/persistence flows through
EventBuilder+EventPipeline
See guides/migrating_to_v0.8.md for migration details.
Documentation
- Persistence guides:
persistence_overview.md,ecto_session_store.md,sqlite_session_store.md,s3_artifact_store.md,composite_store.md,event_schema_versioning.md,custom_persistence_guide.md,migrating_to_v0.8.md - Persistence module group and guides added to HexDocs configuration
- New guide:
guides/shell_runner.mdcoveringWorkspace.ExecandShellAdapterusage, input formats, configuration, event mapping, security controls, and mixed AI+shell workflows - Update
guides/provider_adapters.mdwith ShellAdapter section, event mapping table, and Shell Runner module group in HexDocs - Update
README.mdwith approval gate functionality, interactive interrupt example, and Shell Runner feature - Update
examples/README.mdwithinteractive_interrupt.exs,approval_gates.exs, andshell_exec.exsentries - Add
interactive_interrupt.exsandshell_exec.exstorun_all.shautomated test suite - Update
README.mdand persistence overview with per-event persistence andflush/2finalization details - Document V2 migration prerequisites + V3 foreign-key migration expectations for SQLite and PostgreSQL flows
- Document cursor semantics/order requirements, SessionStore ref shapes (
pidvs{Module, context}), and optional SDK dependency behavior - Update
examples/README.mdwith all persistence adapter and query/maintenance examples - Rewrite
guides/event_schema_versioning.mdfor strict-only contract (no backward-compatible defaults) - Document structured
provider_errorpropagation, stderr truncation limits, and failure-event payload semantics acrossREADME.md,guides/error_handling.md,guides/events_and_streaming.md, and related rendering/streaming guides - Remove "backward compatible" / "legacy" language from all guides
- Update
guides/session_continuity.md: remove booleantruedocumentation, simplify mode table - Update
guides/provider_adapters.md: remove alias emission notes, canonical keys only
0.7.0 - 2026-02-09
Added
- Generic rendering system with pluggable Renderer x Sink architecture (
AgentSessionManager.Rendering)Rendering.stream/2orchestrator consumes anyEnumerableof event maps, renders each event, and writes to all sinks simultaneously- Full lifecycle: init -> render loop -> finish -> flush -> close
Rendererbehaviour with three built-in implementationsCompactRenderer- single-line token format (r+,r-,t+Name,t-Name,>>,tk:N/M,msg,!,?) with optional ANSI color support via:coloroptionVerboseRenderer- line-by-line bracketed format ([run_started],[tool_call_started], etc.) with inline text streaming and labeled fieldsPassthroughRenderer- no-op renderer returning empty iodata for every event, for use with programmatic sinks
Sinkbehaviour with four built-in implementationsTTYSink- writes rendered iodata to a terminal device (:stdiodefault), preserving ANSI codesFileSink- writes rendered output to a plain-text file with ANSI codes stripped; supports both:path(owns the file) and:io(pre-opened device, caller manages lifecycle) optionsJSONLSink- writes events as JSON Lines with:fullmode (all fields, ISO 8601 timestamps) and:compactmode (abbreviated type codes, millisecond epoch timestamps)CallbackSink- forwards raw events and rendered iodata to a 2-arity callback function for programmatic processing
- ANSI color support in
CompactRendererwith configurable:coloroption (defaulttrue) StreamSessionone-shot streaming session lifecycle moduleStreamSession.start/1replaces ~35 lines of hand-rolled boilerplate (store + adapter + task + Stream.resource + cleanup) with a single function call- Returns
{:ok, stream, close_fun, meta}- a lazy event stream, idempotent close function, and metadata map - Automatic
InMemorySessionStorecreation when no store is provided - Adapter startup from
{Module, opts}tuples; passes through existing pids/names without ownership - Ownership tracking: only terminates resources that StreamSession created
- Configurable idle timeout (default 120s) and shutdown grace period (default 5s)
- Error events emitted for adapter failures, task crashes, exceptions, and timeouts (never crashes the consumer)
- Atomic idempotent close via
:atomics.compare_exchange
StreamSession.Supervisorconvenience supervisor for production use- Starts
Task.SupervisorandDynamicSupervisorfor managed task and adapter lifecycle - Optional - StreamSession works without it using
Task.start_linkdirectly
- Starts
StreamSession.Lifecycleresource acquisition and release (store, adapter, task)StreamSession.EventStreamlazyStream.resourcewith receive-based state machine- Four new rendering examples:
rendering_compact.exs,rendering_verbose.exs,rendering_multi_sink.exs,rendering_callback.exs - New example:
stream_session.exsdemonstrating StreamSession in rendering and raw modes - New guides:
guides/rendering.md,guides/stream_session.md - Rendering test helpers in
test/support/rendering_helpers.ex - Full test coverage for all renderers, sinks, and StreamSession
Changed
- Rendering examples refactored to use
StreamSession.start/1, eliminating duplicatedbuild_event_streamboilerplate from each examples/run_all.shupdated with StreamSession and rendering example entries
Documentation
- Add
guides/rendering.mdandguides/stream_session.mdto HexDocs extras and "Core Concepts" group - Add Rendering and Stream Session module groups to HexDocs
- Update
examples/README.mdwith rendering and StreamSession example documentation - Add rendering and StreamSession sections to
README.md - Bump installation snippets in
README.mdandguides/getting_started.mdto~> 0.7.0
0.6.0 - 2026-02-08
Added
- Normalized
max_turnsoption for controlling agentic loop turn limits across all adapters- Claude:
nil(default) = unlimited turns, integer = pass--max-turns Nto CLI - Codex:
nil(default) = SDK default of 10, integer = override viaRunConfig - Amp: stored but ignored (CLI-enforced, no SDK knob)
- Claude:
- Normalized
system_promptoption for configuring system prompts across adapters- Claude: maps to
system_promptonClaudeAgentSDK.Options - Codex: maps to
base_instructionsonCodex.Thread.Options - Amp: stored in state (no direct SDK equivalent)
- Claude: maps to
sdk_optspassthrough for arbitrary provider-specific SDK options- All adapters accept
:sdk_optskeyword list, merged into the underlying SDK options struct - Normalized options (
:permission_mode,:max_turns, etc.) always take precedence oversdk_opts - Unknown keys are silently ignored (only struct-valid fields are applied)
- All adapters accept
- Normalized permission modes via
AgentSessionManager.PermissionModewith provider-specific mapping- Five modes:
:default,:accept_edits,:plan,:full_auto,:dangerously_skip_permissions PermissionMode.all/0,valid?/1, andnormalize/1for validation and string/atom coercion- ClaudeAdapter maps
:full_autoand:dangerously_skip_permissionstopermission_mode: :bypass_permissionsonClaudeAgentSDK.Options - CodexAdapter maps
:full_autotofull_auto: trueand:dangerously_skip_permissionstodangerously_bypass_approvals_and_sandbox: trueonCodex.Thread.Options - AmpAdapter maps
:full_autoand:dangerously_skip_permissionstodangerously_allow_all: trueonAmpSdk.Types.Options :accept_editsand:planare Claude-specific; no-op on Codex and Amp adapters- All three adapters accept
:permission_modeoption onstart_link/1 - ClaudeAdapter
execute_with_agent_sdknow forwards built SDK options throughquery/3(was%{})
- Five modes:
- Long-poll cursor reads via optional
wait_timeout_msparameter onSessionStore.get_events/3InMemorySessionStoreimplements deferred-reply long-poll usingGenServer.reply/2without blocking the server loopSessionManager.stream_session_events/3forwardswait_timeout_msto the store, eliminating busy polling when supported- Stores that do not support
wait_timeout_msignore it and fall back to immediate return cursor_wait_follow.exslive example demonstrating long-poll follow
- Adapter event metadata persistence in
SessionManager.handle_adapter_event/4- Adapter-provided
DateTimetimestamps stored inEvent.timestampinstead ofDateTime.utc_now() - Adapter-provided
metadatamap merged intoEvent.metadata Event.metadata[:provider]always set to the adapter's provider name string
- Adapter-provided
- Native continuation modes for session continuity
:continuationoption expanded to accept:auto,:replay,:native,true, andfalse:nativemode uses provider-native session continuation (errors if unsupported):replaymode always replays transcript from events regardless of provider support:automode (andtruealias) uses native when available, falls back to replay
- Token-aware transcript truncation via
continuation_optsmax_chars-- hard character budget for transcript contentmax_tokens_approx-- approximate token budget using 4-chars-per-token heuristic- Truncation keeps the most recent messages within the lower effective limit
- Per-provider continuation handles stored under
session.metadata[:provider_sessions]keyed map for multi-provider sessions - Workspace artifact storage via
ArtifactStoreport andFileArtifactStoreadapter- Large patches stored as artifacts with
patch_refin run metadata instead of embedding raw patches ArtifactStore.put/4,get/3,delete/3API (opts optional)- Without an artifact store configured, patches are embedded directly
- Large patches stored as artifacts with
- Git snapshot untracked file support using alternate
GIT_INDEX_FILEto stage all content without mutatingHEAD- Snapshot metadata includes
head_ref,dirty, andincludes_untrackedfields
- Snapshot metadata includes
- Hash backend configurable ignore rules via
ignore: [paths: [...], globs: [...]] - Weighted provider routing via
strategy: :weightedwith custom weight maps and health-based score penalties- Score formula:
weight - failure_count * 0.5; ties broken bypreferorder - Per-run weight overrides via
adapter_opts: [routing: [weights: ...]]
- Score formula:
- Session stickiness via
sticky_session_idin routing options with configurablesticky_ttl_ms- Best-effort: falls back to normal selection when sticky adapter is unavailable
- Circuit breaker for per-adapter fault isolation (
:closed/:open/:half_openstates)- Pure-functional data structure stored in router state, no extra processes
- Configurable
failure_threshold,cooldown_ms, andhalf_open_max_probes
Routing telemetry events:
[:agent_session_manager, :router, :attempt, :start | :stop | :exception]- Policy stacks via
policies: [...]list option with deterministic left-to-right merge semantics- Names joined with
+, limits overridden by later values, tool rules concatenated, stricteston_violationwins
- Names joined with
- Provider-side enforcement via
AdapterCompilercompiling policy rules toadapter_opts- Deny rules to
denied_tools, allow rules toallowed_tools, token limits tomax_tokens - Reactive enforcement remains the safety net alongside provider-side hints
- Deny rules to
- Policy preflight checks reject impossible policies (empty allow lists, zero budgets, contradictory rules) before adapter execution
- Multi-slot concurrent session runtime (
max_concurrent_runs > 1) inSessionServer- Submitted runs queue in FIFO order, dequeued as slots free up
- Automatic slot release on completion, failure, cancellation, and task crash
- Durable subscriptions with gap-safe backfill + live delivery in
SessionServersubscribe/2withfrom_sequence:replays stored events then follows live appends- Cursor-based resumption after disconnect: re-subscribe with
last_seen_seq + 1
- Transcript caching in
SessionServerusingTranscriptBuilder.update_from_store/3for incremental updates- Cache reduces store reads from O(total_events) to O(new_events) per run
- Invalidated on error; correctness does not depend on cache
- Runtime telemetry events under
[:agent_session_manager, :runtime, ...]namespacerun:enqueued,run:started,run:completed,run:crashed,drain:complete
- ConcurrencyLimiter and ControlOperations integration with
SessionServervia:limiterand:control_opsoptions- Crash-safe acquire/release and cancellation routing through the runtime layer
- New examples:
cursor_wait_follow.exs,routing_v2.exs,policy_v2.exs,session_concurrency.exs - New guide:
guides/advanced_patterns.mdcovering cross-feature integration patterns
Fixed
- ClaudeAdapter
max_turns: 1hardcoding -- Claude was limited to a single turn, preventing tool results from being fed back to the model. Now defaults tonil(unlimited). - ClaudeAdapter streaming halted on tool use --
handle_streaming_event(%{type: :message_stop})unconditionally halted the stream, cutting off multi-turn tool use. Now continues whenstop_reasonis"tool_use", allowing the CLI to execute tools and deliver subsequent turns. - ClaudeAdapter missing
cwdpassthrough -- The adapter never passedcwdtoClaudeAgentSDK.Options, so the Claude CLI ran in the Elixir process's working directory instead of the configured project directory. Now accepts:cwdoption and passes it through. - ClaudeAdapter
setting_sources: ["user"]hardcoding -- removed; usesdk_opts: [setting_sources: [...]]to configure.
Changed
- SessionStore port
get_events/3now accepts optionalwait_timeout_msparameter (non-breaking; stores may ignore it) InMemorySessionStoreextended with waiter tracking for long-poll supportsession_continuity.exsandworkspace_snapshot.exsexamples updated with Phase 2 featuresexamples/run_all.shupdated with full Phase 2 provider matrix
Documentation
- Add routing and runtime telemetry event tables with measurements and metadata to
guides/telemetry_and_observability.md - Expand
AdapterCompilersection inguides/policy_enforcement.mdwith mapping rules table, merged policy compilation, and provider support matrix - Add multi-slot worked example and transcript caching section to
guides/session_server_runtime.md - Add new Phase 2 examples to
guides/live_examples.md - Add
guides/advanced_patterns.mdcovering routing + policies, SessionServer + subscriptions + workspace, stickiness + continuity, and policy + workspace + artifacts - Update
guides/cursor_streaming_and_migration.mdwith long-poll reference implementation - Update
guides/session_continuity.mdwith continuation modes, token-aware truncation, and per-provider handles - Update
guides/workspace_snapshots.mdwith artifact store, untracked file support, and ignore rules - Update
guides/provider_routing.mdwith weighted routing, stickiness, circuit breaker, and routing telemetry - Update
guides/policy_enforcement.mdwith policy stacks, provider-side enforcement, and preflight checks - Update
guides/session_server_runtime.mdwith multi-slot concurrency and operational APIs - Update
guides/session_server_subscriptions.mdwith durable subscriptions and multi-slot interleaving - Bump installation snippets in
README.mdandguides/getting_started.mdto~> 0.6.0
0.5.1 - 2026-02-07
Changed
- Canonical tool event payloads across all three adapters (Claude, Codex, Amp)
- All
tool_call_started,tool_call_completed, andtool_call_failedevents now emit canonical keys:tool_call_id,tool_name,tool_input, andtool_output - Adapters emit canonical keys only (
tool_call_id,tool_name,tool_input,tool_output) normalize_tool_input/1helper added to each adapter to guaranteetool_inputis always a mapfind_tool_call/2helper added to AmpAdapter to enrichtool_call_completedandtool_call_failedevents withtool_nameandtool_inputfrom the originating tool call
- All
Documentation
- Update
guides/provider_adapters.mdwith canonical tool event payload reference - Update
guides/session_continuity.mdwith canonicaltool_call_idguidance - Bump installation snippets in
README.mdandguides/getting_started.mdto~> 0.5.1
0.5.0 - 2026-02-07
Added
- AmpAdapter for Sourcegraph Amp provider integration, bringing the adapter count to three (Claude, Codex, Amp)
- Streaming execution, tool call handling, and cancel support via transport close
- Amp SDK message types mapped to canonical normalized events
amp_direct.exsexample demonstrating threads, permissions, MCP, and modes
- Cursor-backed event streaming with durable per-session sequence numbers
append_event_with_sequence/2andget_latest_sequence/2callbacks on theSessionStoreport:afterand:beforecursor filters onget_events/3for sequence-based paginationSessionManager.stream_session_events/3for resumable follow/poll consumption across reconnectscursor_pagination.exsandcursor_follow_stream.exslive provider examples
- Session continuity with provider-agnostic transcript reconstruction
Transcriptstruct andTranscriptBuilderwithfrom_events/2,from_store/3, andupdate_from_store/2- Sequence-based ordering with timestamp and deterministic tie-breaker fallbacks
- Streaming chunk collapse into single assistant messages
- Canonical tool call ID handling via
tool_call_id continuation: :autoandcontinuation_optssupport inexecute_run/4andrun_once/4- Transcript-aware prompt building in all three adapters
session_continuity.exslive provider example
- Workspace snapshots with pre/post snapshot, diff, and optional rollback instrumentation
Workspaceservice withGitBackend(snapshots, diffs, patch capture, rollback) andHashBackend(snapshots and diffs only)SnapshotandDiffstructs withto_mapand summary helpers:workspace_snapshot_takenand:workspace_diff_computedevent types- Run metadata enrichment with compact diff summaries
workspace: [...]option support inexecute_run/4andrun_once/4workspace_snapshot.exslive provider example
- Provider routing via
ProviderRouterimplementing theProviderAdapterbehaviour- Capability-driven adapter selection with type+name and type-only matching
RoutingPolicywith prefer/exclude ordering and attempt limiting- Simple in-process health tracking with consecutive-failure counts and cooldown-based temporary skipping
- Retryable failover up to configured attempt limit (only for
Error.retryable?/1errors) - Cancel routing to the adapter currently handling the active run
- Routing metadata (
routed_provider,routing_attempt,failover_from,failover_reason) attached to results and events provider_routing.exslive provider example
- Policy enforcement opt-in per execution via
:policyinexecute_run/4andrun_once/4Policystruct with limits (max_total_tokens,max_duration_ms,max_tool_calls,max_cost_usd), tool rules (allow/deny), andon_violationaction (:cancelor:warn)Evaluatorfor pure policy checks against runtime state snapshotsRuntimeGenServer for mutable per-run counters and single-cancel semantics:policy_violationevent type and error code- Cancel mode overrides provider success; warn mode preserves success with violation metadata
- Optional cost-limit enforcement using configured provider token rates
policy_enforcement.exslive provider example
- SessionServer opt-in per-session runtime GenServer with FIFO queueing and subscriptions
submit_run/3,await_run/3,execute_run/3,cancel_run/2,subscribe/2,unsubscribe/2,status/1API- Strict sequential execution (MVP constraint:
max_concurrent_runsmust be1) RunQueuepure FIFO data structure with enqueue/dequeue/remove and max size enforcement- Queued run cancellation marks
:cancelledin store with:run_cancelledevent without reaching adapter - Store-backed subscriptions delivering
{:session_event, session_id, event}withfrom_sequencecursor replay,run_idfiltering, andtypefiltering - Optional
ConcurrencyLimiterintegration via:limiteroption for run slot acquire/release - Task crash handling via
Process.monitorwith automatic limiter release and awaiter notification
- SessionSupervisor with
DynamicSupervisorandRegistrychild tree for per-session process lifecycle - SessionRegistry with
via_tuple/2andlookup/2helpers for named registration AmpMockSDKwith scenario-based event stream generation for adapter testing without networkBlockingTestAdapterfor deterministic sequential execution testingRouterTestAdapterwith configurable outcomes, sleep/cancel support, and scripted event emissionsession_runtime.exs,session_subscription.exs, andsession_limiter.exslive provider examples- Input messages persisted as
:message_sentevents before adapter execution
Changed
- SessionStore port now requires
append_event_with_sequence/2andget_latest_sequence/2callbacks (breaking for custom store implementations) - SessionManager routes all event persistence through sequenced appends with monotonic per-session sequence numbers
- Sequence numbers included in telemetry event data for adapter callback events
- All three adapters (Claude, Codex, Amp) prepare transcript-aware prompts when session transcript context is present
InMemorySessionStoretracks per-session sequence counters with full sequenced event storage and idempotent duplicate detectionamp_sdkadded as a dependency- Existing examples (
oneshot,live_session,common_surface,contract_surface_live) updated to support theampprovider run_all.shrefactored with argument parsing,--providerfiltering, and run plan display
Documentation
- Add
guides/cursor_streaming_and_migration.mdwith migration checklist for custom stores - Add
guides/session_continuity.mdandguides/workspace_snapshots.md - Add
guides/provider_routing.mdandguides/policy_enforcement.md - Add
guides/session_server_runtime.mdandguides/session_server_subscriptions.md - Update
guides/architecture.mdmodule map with all new subsystems (routing, policy, workspace, runtime) - Update
guides/concurrency.mdwith runtime limiter integration - Update
guides/events_and_streaming.mdwith cursor APIs and workspace/policy event types - Update
guides/sessions_and_runs.mdwith feature options and sequence assignment - Update
guides/live_examples.mdwith all new example scripts - Update
guides/provider_adapters.mdwith AmpAdapter documentation - Update
README.mdwith SessionServer usage, cursor streaming, continuity, workspace, routing, and policy sections - Update
examples/README.mdfor three-provider support and all new examples - Add Routing, Policy, Runtime, and Workspace modules to HexDocs group listings and extras menu
- Bump installation snippets in
README.mdandguides/getting_started.mdto~> 0.5.0
0.4.1 - 2026-02-06
Changed
- Harden adapter execution lifecycle in Claude and Codex adapters by replacing linked worker processes with supervised
Task.Supervisor.async_nolink/2tasks - Add deterministic task result and
:DOWNhandling so adapter calls always resolve with tuple results, including worker-failure paths - Fix concurrent execution reply routing to avoid reply loss when run IDs collide
- Standardize adapter cancellation success return shape to
{:ok, run_id}and align control operations with this contract - Normalize
ControlOperations.interrupt/2to use providercancel/2semantics, avoiding unsafe provider-specific interrupt calls - Remove
Process.whereischeck-then-act dispatch race inProviderAdapterand use call-first fallback behavior on:noproc - Replace dynamic atom creation in core deserialization paths with safe existing-atom conversion via shared serialization utilities
- Expand
Core.Errortaxonomy for stream/state mismatch cases (:stream_closed,:context_mismatch,:invalid_cursor,:session_mismatch,:run_mismatch)
Added
- A shared core serialization utility for safe key conversion and map helpers
- Serialization safety tests and adapter worker-failure isolation tests
Documentation
- Update provider and architecture guides to document supervised nolink adapter task execution model
- Document interrupt-to-cancel behavior in the concurrency guide
- Bump installation snippets in
README.mdandguides/getting_started.mdto~> 0.4.1
0.4.0 - 2026-02-06
Changed
- Ensure adapter
execute/4results include emitted execution events inresult.eventsfor both Claude and Codex adapters - Ensure Claude
:run_completedevents includetoken_usageon both streaming andClaudeAgentSDKquery execution paths - Normalize adapter callback event aliases (for example
"run_start","delta","run_end") to canonicalEvent.typevalues before persistence inSessionManager - Update SessionManager telemetry emission to use normalized event types on ingest
- Bump codex_sdk dependency from ~> 0.7.1 to ~> 0.7.2
Added
- New live example:
examples/contract_surface_live.exsto verify runtime contract guarantees across Claude and Codex - New live example test:
test/examples/contract_surface_live_test.exs - New guide:
guides/live_examples.md - Add live contract-surface example runs to
examples/run_all.sh - Add HexDocs extras/menu entry for
guides/live_examples.md
Documentation
- Update
README.mdandguides/getting_started.mddependency snippets to~> 0.4.0 - Remove stale mock-mode example commands from top-level docs and guide snippets
- Expand provider/events docs to reflect current
execute/4events contract and ingest normalization behavior
0.3.0 - 2026-02-06
Changed
- Switch ClaudeAdapter from
ClaudeAgentSDK.Query.run/3toClaudeAgentSDK.Streamingfor real token-level streaming deltas - Simplify event mapping: replace
content_block_start/delta/stopwithtext_deltaandtool_use_startevents - Change default model from
claude-sonnet-4-20250514toclaude-haiku-4-5-20251001 - Add
toolsoption passthrough to ClaudeAdapterstart_link/1and SDK options - Set
max_turns: 1in SDK options for single-turn execution - Update oneshot example to stream content deltas to stdout instead of progress dots to stderr
- Bump claude_agent_sdk dependency from ~> 0.10.0 to ~> 0.11.0
- Bump codex_sdk dependency from ~> 0.6.0 to ~> 0.7.1
0.2.1 - 2026-02-05
Changed
- Bump claude_agent_sdk dependency from ~> 0.9.2 to ~> 0.10.0
0.2.0 - 2026-02-05
Added
SessionManager.run_once/4convenience function that collapses the full session lifecycle (create, activate, start run, execute, complete/fail) into a single callexecute_run/4now accepts an:event_callbackoption for real-time event streaming alongside internal persistenceexamples/oneshot.exsone-shot execution example usingrun_once/4
0.1.1 - 2026-01-27
Added
AgentSessionManager.Configmodule for centralized configuration with process-local overrides- Process-local configuration layering (process dictionary -> application env -> built-in default)
- Process isolation test for
Telemetry.set_enabled/1 common_surface.exsexample demonstrating provider-agnostic SessionManager lifecycleclaude_direct.exsexample demonstrating Claude-specific SDK features (Orchestrator, Streaming, Hooks, Agent profiles)codex_direct.exsexample demonstrating Codex-specific SDK features (Threads, Options, Sessions)- Tests for all new example scripts (
common_surface_test.exs,claude_direct_test.exs,codex_direct_test.exs) - Configuration guide (
guides/configuration.md) documenting the layered config system
Changed
AuditLogger.set_enabled/1now sets a process-local override instead of mutating global application envTelemetry.set_enabled/1now sets a process-local override instead of mutating global application envAuditLogger.enabled?/0andTelemetry.enabled?/0now resolve throughAgentSessionManager.Config.get/1- Telemetry tests run with
async: true(previouslyasync: falsedue to global state mutation) - Telemetry
refute_eventhelper now filters bysession_idto avoid false failures from concurrent tests - Standardized test process cleanup using
cleanup_on_exit/1from Supertester, replacing manualon_exitblocks - Updated
examples/run_all.shto include the new example scripts - Updated
examples/README.mdwith documentation for all new examples
0.1.0 - 2026-01-27
Added
- Initial project setup
- Basic project structure with mix.exs configuration
- Project logo and assets