Agent Session Management#
Agent session management is a cross-cutting feature area in Phoenix (PXI) covering the persistence, REST API, GraphQL API, web UI picker, and CLI integration for agent chat sessions. The core goal is that conversations survive backend restarts, work across multiple Phoenix replicas, and remain consistent across all client surfaces — web UI, pxi CLI, and Slack .
All engineering work is tracked under the 🗺️ agent session persistence epic (#14206), which had 24 of 25 engineering items complete as of 2026-07-31 .
Important distinction: Agent sessions use ownership-based SQL scoping — not the project-level session filter DSL. These are two separate, non-overlapping surfaces .
Session Persistence and Data Model#
Sessions are stored with a temporary flag distinguishing short-lived chats from persisted ones. Key session fields include: title, created_at, updated_at, user, first_input, latest_output, and messages.
The session lifecycle encompasses creation, compaction (#14583), expiry, and the distinction between temporary and persisted states. REST endpoints only return non-temporary, non-expired sessions .
The AgentSession GraphQL type is defined in src/phoenix/server/api/types/AgentSession.py, with every field gated by the CanAccessAgentSession permission class .
REST API#
All agent session REST routes live in src/phoenix/server/api/routers/agents.py :
| Method | Route | Description |
|---|---|---|
POST | /agents/{agent_id}/sessions | Create a persisted session; validates agent_id ∈ {"assistant", "server"}; sets user_id from auth context; returns a GlobalID |
GET | /agents/{agent_id}/sessions | List persisted sessions with cursor pagination |
GET | /agents/{agent_id}/sessions/{session_id} | Fetch a single session with full persisted transcript |
Authorization behavior :
- Auth enabled: list/get queries are scoped to
user_id == <request user>; another user's session returns 404 - Auth disabled: all persisted sessions for the agent are returned
- Viewers receive 403; missing sessions return 404
_get_request_user_id(request)extracts the authenticated user ID or returnsNone
OpenAPI schemas (in schemas/openapi.json) : AgentSessionSummary, AgentSessionData, ListAgentSessionsResponseBody, GetAgentSessionResponseBody. Generated typed bindings are kept in sync across app/src/api/__generated__/v1.ts, js/packages/phoenix-client/src/__generated__/api/v1.ts, and the Python client.
POST /agents/{agent_id}/sessions was introduced in PR #14581 ; GET routes were added in PR #14711 .
GraphQL API and Authorization#
Authorization is enforced via the CanAccessAgentSession Strawberry permission class in src/phoenix/server/api/agent_helpers.py :
- Non-admin: access denied if
owner_id != viewer_id - Admin: unrestricted access
get_agent_session_owner_filter()returns a SQLWHERE user_id == viewer_idpredicate for non-admins, orNonefor admins
The agentSessions query supports a viewerOnly: Boolean! = false argument (added in PR #14898), which restricts results to the viewer's own sessions regardless of role . This is resolved in src/phoenix/server/api/queries.py.
Web UI Session Picker#
The web UI surfaces agent sessions in two places with different scoping:
| Surface | Component | Scope |
|---|---|---|
| Chat window session picker | AgentSessionsResource.tsx | viewerOnly: true — viewer's own sessions only |
| Admin settings table | (separate view) | Admin-wide, all users |
PR #14898 fixed the chat picker to pass viewerOnly: true so admins no longer see every user's session in their own picker .
SessionListMenu.tsx uses a store-and-network fetch strategy — it loads instantly from the Relay store cache, then refetches in the background when the menu opens, so sessions created in other tabs appear without a stale view (PR #14857) .
Additional UX fixes in PR #14857: the thinking indicator now appears immediately on first send and persists until content streams; the chat empty state is suppressed during background polling .
CLI Session Management (pxi)#
The pxi terminal UI in js/packages/phoenix-cli/src/pxi/ supports full session lifecycle management via slash commands:
| Command | Action |
|---|---|
/sessions | Open inline searchable picker for restoring persisted chats |
/new | Start a new persisted session |
/temporary | Start a temporary (non-persisted) session |
/clear | Clear current session |
Tab completion is available for all slash commands .
client.ts (js/packages/phoenix-cli/src/pxi/client.ts) :
- Builds persisted route URLs via
buildAgentChatUrl({ endpoint }) - Implements a legacy fallback: if the persisted endpoint returns 404/405, retries against the legacy URL (for compatibility with older Phoenix versions)
- Parses session metadata from
data-session-createdanddata-session-summarystream chunks to capture session identity and update titles
Session picker UX (PR #14857): The /sessions picker loads instantly with cached data and refreshes in the background, preserving filter queries and selections .
PR history:
- PR #14581 — established the stateful
POST /agents/{agent_id}/sessionsroute foundation - PR #14469 — migrated PXI CLI to use persisted sessions with legacy fallback
- PR #14711 — added
/sessionspicker and wiredGETREST routes - PR #14857 — improved picker responsiveness and cache behavior
Known Issues and Ongoing Work#
As of 2026-07-31, the following work remains open under the #14206 epic :
- CLI session picker pagination (#14951):
listSessionshardcodeslimit: 20and discards thenext_cursortoken, so users with more than 20 sessions cannot see older ones. - Engineering: 1 item pending (#14817)
- Dogfooding feedback: ~9 items pending (#14880–#14895 range)
- Review findings (7-31): 6 open items (#14948–#14953)
- Stretch: #14885
- Post-merge: #14623
- Punted: #14451
Key Source Files#
| File | Purpose |
|---|---|
src/phoenix/server/api/routers/agents.py | REST routes: POST/GET session endpoints |
src/phoenix/server/api/agent_helpers.py | CanAccessAgentSession permission; get_agent_session_owner_filter() |
src/phoenix/server/api/types/AgentSession.py | AgentSession GraphQL type with per-field permission enforcement |
src/phoenix/server/api/queries.py | agentSessions GraphQL resolver with viewerOnly arg |
app/src/components/agent/AgentSessionsResource.tsx | Web UI session fetcher; passes viewerOnly: true for picker |
app/src/components/agent/SessionListMenu.tsx | Session picker menu with background refresh |
js/packages/phoenix-cli/src/pxi/App.tsx | PXI CLI root; session picker slash commands |
js/packages/phoenix-cli/src/pxi/client.ts | Chat client; persisted route + legacy fallback |
schemas/openapi.json | OpenAPI schemas for agent session endpoints |