Sessions
A session is a continuous period of AI activity from a single identity — typically one Claude Code or Cursor workspace. Sessions give you a per-task view of cost, tool calls, and governance decisions.
What is a session
Sessions are created automatically when a developer or agent begins sending requests through the proxy. A session is marked expired once it has been idle longer than the configurable idle window (session_idle_window_minutes, default 60 minutes — set to 0 to disable idle expiry) or when the developer explicitly disconnects. Independently of the idle window, every session also has a hard expiry — 8 hours after it was created (refreshed if the same conversation continues past it) — after which it is permanently closed. If a developer resumes an idle-expired session before its hard expiry, it automatically revives rather than starting a new session.
Each session captures:
- The identity (developer email or agent ID) and the tool they were using
- A declared purpose — optionally set by the developer or inferred from the first request
- Total token usage, estimated cost, and model mix for the session
- Every tool call and LLM request made during the session, with governance decisions
How requests are grouped into a session
Some AI tools tell the proxy which conversation a request belongs to; others send nothing at all. AIControls handles both.
- Claude Code, Codex CLI, and the VS Code extension send a session identifier with every request. Their sessions are exact: the boundary is whatever the tool itself considers one conversation.
- Cursor and Gemini CLI send no session identifier. There is nothing in their requests — no header, no conversation ID — that says which conversation a request belongs to. Cursor does include a user ID, but it is the same value for every conversation that person starts.
For tools in the second group, AIControls groups requests itself, using two signals in order:
- Transcript continuity. These APIs are stateless, so every request resends the whole conversation so far. When a request's conversation history matches an open session's history with new messages appended, it is the same conversation. This is the primary signal and it is exact in the common case.
- Activity gap. For requests whose body carries no readable conversation (an embeddings call, an unrecognised request format), consecutive requests from the same identity and tool are grouped while the gap between them stays under the idle window.
A session appears once a second request continues the first. Isolated one-off calls — an inline completion, a code action, a single classification prompt — are not conversations and do not create sessions, so the Sessions page stays a list of actual work rather than a list of requests. Those calls are still fully recorded in the Audit Log with their identity, decision, tokens, and cost.
Sessions grouped this way are marked inferred in the session list and detail panel. The grouping is accurate enough to make the Context tab, cost attribution, and the governance timeline useful, but it is not something the tool told us, so it is never presented as though it were:
- Two conversations started in quick succession from the same workspace can occasionally be shown as one.
- A conversation whose history the tool rewrites mid-flight (a client-side context compaction) can be split into two sessions.
Inferred sessions are deliberately exempt from the concurrent-session limit and carry no session budget, so a grouping decision can never be the reason a request is blocked or downgraded to a cheaper model.
To switch inference off, set Settings → Data Retention & Session Limits → Session grouping to Client-reported only. Sessions then appear only for tools that send a session identifier. The setting applies to new requests — sessions already recorded keep the boundaries they were recorded with.
Session list
Navigate to Identity → Sessions to see all sessions, filterable by developer, status (active, ended, expired, budget exceeded, paused), and purpose. An admin can pause or resume an active session from the list without affecting the developer's connection.
The User column shows the developer, with their team and the AI client the session came through beneath the email — Claude Code, OpenAI Codex, or, for a Cross-App Access session, the governed agent's name. The client is derived from the session's own recorded requests, so it also appears on sessions recorded before it was displayed.
Session detail
Click any session to open the detail panel. It shows:
- Summary — AI-generated description of what the session accomplished, which tools were used, and key outcomes
- Cost breakdown — input tokens, output tokens, cache hits, and estimated cost by model
- Governance timeline — every allow, deny, warn, and approval decision in chronological order
- Tool calls — the full list of MCP tool calls with arguments and outcomes
- Action Graph — a visual, single-hop trace of the session: delegator (human, if any) → agent → each tool (or, for MCP calls, each MCP server) the agent called → the resource it reached, colored by decision (allowed, denied, pending). Calls are grouped by tool or server identity rather than listed one row per call — a group with more than one distinct call shows a count and expands to reveal the individual calls and the resource each one reached. Denied calls show as blocked before the resource node, since they never reached it. A Timeline toggle switches to a chronological list using the same rows as the Tool calls tab. This traces one identity through one agent only — it does not show agent-to-agent delegation chains
- Context — context-window fill, a token breakdown by segment (system prompt, user messages, assistant turns, tool results, tool definitions), and per-turn growth. Available for every provider, including Bedrock, Vertex, and Foundry. A turn appears here once the provider has reported its token usage; Bedrock's streaming response format and Claude-on-Vertex's streaming endpoint do not report usage, so those turns are still audited and still counted toward the session, but they contribute no per-turn context row
The detail panel shows both Start (when the session was first created) and Last turn (the timestamp of its most recent activity); the session list is sorted by Last turn so the most recently active sessions surface first.
Concurrent sessions
A developer can have more than one session running at the same time — one agent per workspace, or an orchestrator driving several sub-agents. Productivity → Activity measures that overlap per developer over the selected window and reports it in the Concurrent column, both in the Users view and in the per-team drawer that opens when you click a row in the Teams view. Activity opens on the Projects view — switch views with the selector above the table.
- Peak — the largest number of that developer's sessions running at the same moment. This is the number shown in the column.
- Multi-session share — the percentage of the developer's active time with two or more sessions running at once. Active time is the time at least one session was running, not the whole window, so someone who works in short bursts isn't penalised against someone logged in all day.
- Average — the mean number of simultaneous sessions across that same active time, weighted by how long each level of overlap lasted. It is an average over active time, not over the window.
Hover a value to see the average and multi-session share behind the peak. Rows are ordered by peak, and by multi-session share when peaks tie. A developer whose sessions never overlapped shows — rather than 1 — there was no concurrency to report, not missing data.
Three scoping rules shape the numbers:
- Only sessions started inside the selected window are counted. A session that was already running when the window opened is left out entirely, so a short window understates overlap — widen it if a developer's peak looks lower than you expect.
- Overlap runs from a session's start to its Last turn, and never past the end of the window. A session that records no activity at all after it is created counts for a bounded period after its start rather than all the way to its hard expiry, so an abandoned session can't inflate a developer's concurrency for hours.
- A session that was created, recorded no activity, and is already closed contributes nothing. This is why a developer can show a Sessions count with no concurrency at all — the sessions existed, but none of them ran long enough to overlap.
Session budgets
Sessions inherit the default session cap set in Settings → Budgets → Limits. When a session hits its cap, further requests are blocked until the session ends or an admin resets the budget from the session detail panel.
Individual developers can request a higher cap for a specific session by setting it in their PAT. Admins can override any session cap from the detail panel.
Adoption dashboard
The Adoption dashboard is built from the same session data described above: it reports how many provisioned developers are actively using AI tooling, with anonymous agents and service accounts counted separately from human developers. See Adoption Analytics for how Active Users, adoption rate, new adopters, and churn are defined (kept here as a single source of truth so the two pages can't drift).