Session State And Lifecycle
ACP Kit treats session state as a first-class contract.
Each session carries the information needed to:
- replay ACP transcript history
- resume the current workspace and config state
- keep plan state stable across prompts
- reflect mode, model, and approval metadata accurately
ACP 0.11 Session Contract
pydantic-acp targets ACP Python SDK 0.11.0. Model selection is now a
session config option, not a session/set_model RPC. Configure a concrete
model only when the adapter exposes a selectable "model" option; otherwise
keep the agent default with AcpProvider.model() or create_acp_model(...)
without model_name.
Client capability negotiation is respected:
- no
session.configOptionscapability means no config option surface is sent - select options require
session.configOptions - boolean options additionally require
session.configOptions.boolean plan_update_mode="content"usesplan_updateandplan_removedonly when the client advertisesplan; it otherwise falls back to fullplanupdates
This preserves a usable full-plan surface for older clients while allowing newer clients to reconcile a named plan incrementally.
What Is Stored
An AcpSessionContext captures:
session_idcwdcreated_atandupdated_at- session-local
config_values session_model_id- ACP transcript updates
- serialized message history
plan_entriesandplan_markdown- MCP server metadata
- adapter-owned session metadata
additional_directories
ACP client-supplied MCP servers are stored in session.mcp_servers so load, fork, resume, and
/mcp-servers can reflect the same session surface. They become runnable Pydantic AI MCP tools
only when the agent build includes SessionMcpBridge.
ACP 0.11 also permits an ACP-transport descriptor. It is preserved across the
session lifecycle so the hosting application can retain its identity and
metadata, but pydantic-acp does not connect it or advertise
McpCapabilities.acp: the SDK does not expose a public ACP MCP router yet.
from acp.schema import AcpMcpServer
delegated_agent = AcpMcpServer(
id="workspace-reviewer",
name="Workspace reviewer",
type="acp",
)
response = await acp_agent.new_session(
cwd="/workspace",
mcp_servers=[delegated_agent],
)
Use HTTP, SSE, or stdio descriptors with SessionMcpBridge when the Pydantic
agent must actually invoke MCP tools. Use AcpMcpServer only to preserve a
delegated ACP endpoint for a host that owns the connection.
additional_directories is persisted through new, load, fork, resume, and
list operations. ClientHostContext treats those directories as extra session
roots for host-backed file and terminal requests. An explicit
workspace_root remains a hard boundary, so a client cannot use an additional
directory to escape host policy.
Typed Elicitation
Session-aware agent factories and providers can ask the connected client for
input through AcpSessionContext. Build the exact ACP mode object and let the
context reject unsupported client capabilities before any request is sent:
from acp.schema import (
ElicitationFormSessionMode,
ElicitationSchema,
)
from pydantic_acp import AcpSessionContext
async def request_confirmation(session: AcpSessionContext) -> None:
mode = ElicitationFormSessionMode(
session_id=session.session_id,
requested_schema=ElicitationSchema(),
)
await session.create_elicitation("Confirm deployment", mode)
create_elicitation(...) requires the matching ClientCapabilities.elicitation
mode and a connected ACP client. It fails explicitly rather than fabricating a
host UI.
Session Lifecycle Operations
pydantic-acp supports the full ACP session lifecycle:
- create
- load
- list
- fork
- resume
- close
When a stored session is loaded or resumed, the adapter can replay transcript and history state so the client sees a consistent session surface.
Session Stores
MemorySessionStore
Use MemorySessionStore when process-local state is enough:
from pydantic_acp import AdapterConfig, MemorySessionStore
config = AdapterConfig(session_store=MemorySessionStore())
FileSessionStore
Use FileSessionStore when sessions should survive restarts:
from pathlib import Path
from pydantic_acp import AdapterConfig, FileSessionStore
config = AdapterConfig(
session_store=FileSessionStore(root=Path(".acp-sessions")),
)
This is the recommended default for local tools and editor integrations.
FileSessionStore is designed as a durable local-host store, not a distributed coordination layer.
Current behavior:
- writes use a temp file,
fsync, and atomic replace - session ids are restricted to ASCII letters, digits,
_, and-, with a 128-character limit - the store takes a process-local lock and a filesystem advisory lock when available
- malformed or partially-written session files are skipped by public load/list flows instead of crashing the whole operation
- stale temp files from interrupted writes are cleaned up on startup
That makes it appropriate for:
- editor integrations
- local desktop agents
- single-host ACP services
It is not a substitute for a real multi-writer shared backend.
Recovery Guarantees Versus Recovery Metrics
pydantic-acp does not publish a built-in "session recovery success rate" metric for
FileSessionStore.
What the adapter does guarantee is the recovery behavior:
- valid saved sessions can be loaded, listed, resumed, and forked after restart
- malformed saved files are skipped by public load/list flows instead of crashing the store
- interrupted temp-file writes are cleaned up on the next store startup
If your product needs an operational success-rate number, treat that as host-owned monitoring. For example, measure:
- successful
load_sessionorresume_sessioncalls after restart - skipped malformed session files
- file permission or disk errors around the session root
ACP Kit gives you the durability and recovery semantics; SLO-style recovery percentages belong in your deployment telemetry.
Transcript Replay And History Replay
The adapter stores two related but different views of a run:
- ACP transcript updates what the ACP client saw
- message history what the underlying Pydantic AI run should receive on the next turn
That split matters because ACP rendering and model message history are not the same thing.
replay_history_on_load=True keeps these aligned across session reloads.
Cancellation
cancel(session_id) is implemented as a real runtime cancellation path, not a no-op.
When a prompt is cancelled:
- the active task is cancelled
- the session history remains well-formed
- the transcript gets a final user-visible cancellation note
- the prompt result reports
stop_reason="cancelled"
This keeps “Stop” behavior compatible with long-running tool calls, plan workflows, and approval flows.
Plan Persistence
Native ACP plan state lives on the session:
plan_entriesplan_markdown
If you configure native_plan_persistence_provider, each plan update can also be mirrored to a host-owned storage destination such as a workspace file.
How Session State Interacts With Factories
When you use agent_factory or AgentSource, the adapter passes the current AcpSessionContext into the build path.
That lets you build session-aware agents such as:
- workspace agents keyed to
session.cwd - agents whose default model changes by workspace
- tools that read from the bound ACP client and active session id
Example: File-backed Session State
from pathlib import Path
from pydantic_ai import Agent
from pydantic_acp import AdapterConfig, FileSessionStore, run_acp
agent = Agent("openai:gpt-5", name="persistent-agent")
run_acp(
agent=agent,
config=AdapterConfig(
session_store=FileSessionStore(root=Path(".acp-sessions")),
replay_history_on_load=True,
),
)
Use this pattern whenever you want ACP sessions to behave like durable workspaces rather than ephemeral chats.
If you also want native ACP plans mirrored into workspace-owned storage, pair this with
native_plan_persistence_provider from the plan workflow docs.