# Session class > **Alpha:** The `Harness` feature is in alpha stage and subject to breaking changes in minor versions until it graduates from its alpha status. A `Session` owns all the state tied to a single conversation. The [`Harness`](https://mastra.ai/reference/harness/harness-class) is the shared host — agents, storage, config, the thread lock, and the event bus — while the `Session` holds everything that is per-conversation: identity, the active thread binding and reads, mode and model selection, run and abort state, the live agent stream, tool suspensions, follow-ups, approvals, permission grants, token usage, and the display-state snapshot. Access the session through `harness.session`. For a conceptual introduction, see the [Harness overview](https://mastra.ai/docs/harness/overview). ## Usage example ```typescript // Read per-conversation state through harness.session const modeId = harness.session.mode.get() const modelId = harness.session.model.get() const threadId = harness.session.thread.getId() const grants = harness.session.getGrants() // Render from the coalesced display-state snapshot harness.subscribe(event => { if (event.type === 'display_state_changed') { render(harness.session.displayState.get()) } }) ``` ## Properties The session is organized into sub-objects, each owning one domain of per-conversation state. **identity** (`SessionIdentity`): Resource identity for the conversation. See identity methods below. **thread** (`SessionThread`): Active thread binding and thread/message reads. See thread methods below. **mode** (`SessionMode`): Active mode selection. See mode methods below. **model** (`SessionModel`): Active model selection, including per-mode persistence. See model methods below. **run** (`SessionRun`): Run and trace identity plus abort state for the in-flight run. See run methods below. **stream** (`SessionStream`): The live subscription to the agent thread stream. See stream methods below. **suspensions** (`SessionSuspensions`): Parked interactive tool calls awaiting a resume. See suspensions methods below. **followUps** (`SessionFollowUps`): Queue of messages submitted while a run is in progress. See follow-up methods below. **approval** (`SessionApproval`): The pending tool-approval gate. See approval methods below. **displayState** (`SessionDisplayState`): The canonical HarnessDisplayState snapshot a UI renders from. See display-state methods below. **state** (`SessionState`): The schema-validated, session-owned Harness state. See state methods below. ## Methods ### Permissions Session-scoped grants auto-approve tools without prompting. Grants are ephemeral — they reset when the session restarts and are never persisted. #### `grantCategory(category)` Grant a tool category for the current session. Tools in this category are auto-approved. ```typescript harness.session.grantCategory('edit') ``` #### `grantTool(toolName)` Grant a specific tool for the current session. ```typescript harness.session.grantTool('mastra_workspace_execute_command') ``` #### `getGrants()` Return the currently granted categories and tools. ```typescript const grants = harness.session.getGrants() // { categories: string[], tools: string[] } ``` ### Tool approvals #### `respondToToolApproval({ decision, requestContext? })` Respond to a pending tool approval request, raised by a `tool_approval_required` event. Pass `always_allow_category` to also grant the tool's whole category for the rest of the session. ```typescript harness.session.respondToToolApproval({ decision: 'approve' }) harness.session.respondToToolApproval({ decision: 'decline' }) harness.session.respondToToolApproval({ decision: 'always_allow_category' }) ``` ### Run control #### `getCurrentRunId()` Return the run ID of the in-flight run, or `null` when idle. Prefers the live stream's active run, falling back to the last stored run ID. ```typescript const runId = harness.session.getCurrentRunId() ``` #### `abortRun()` Abort the in-flight run: aborts the live stream, requests abort on the run, and clears parked tool suspensions. ```typescript harness.session.abortRun() ``` ### Token usage #### `getTokenUsage()` Return a copy of the running token-usage tally for the active thread. ```typescript const usage = harness.session.getTokenUsage() // { promptTokens, completionTokens, totalTokens, ... } ``` ## Identity `session.identity` owns the resource ID for the conversation. ### `session.identity.getResourceId()` Return the current resource ID. ```typescript const resourceId = harness.session.identity.getResourceId() ``` ### `session.identity.getDefaultResourceId()` Return the resource ID the session was created with. ```typescript const defaultResourceId = harness.session.identity.getDefaultResourceId() ``` To change the resource ID, use [`harness.setResourceId()`](https://mastra.ai/reference/harness/harness-class), which also tears down the active thread. ## Thread `session.thread` owns the active thread binding plus thread and message reads. Thread lifecycle (create, switch, clone, delete, rename) lives on the [`Harness`](https://mastra.ai/reference/harness/harness-class) because it coordinates the shared thread lock and event bus. ### `session.thread.getId()` Return the active thread ID, or `null` when no thread is bound. ```typescript const threadId = harness.session.thread.getId() ``` ### `session.thread.list(options?)` List threads from storage. By default only threads for the current resource are returned, and transient forked subagent threads are hidden. ```typescript const threads = await harness.session.thread.list() const allThreads = await harness.session.thread.list({ allResources: true }) const everything = await harness.session.thread.list({ includeForkedSubagents: true }) ``` ### `session.thread.getById({ threadId })` Return a single thread by ID, or `null` if it doesn't exist. ```typescript const thread = await harness.session.thread.getById({ threadId: 'thread-abc123' }) ``` ### `session.thread.listActiveMessages(options?)` Retrieve messages for the active thread. Returns an empty array when no thread is bound. ```typescript const messages = await harness.session.thread.listActiveMessages({ limit: 50 }) ``` ### `session.thread.listMessages({ threadId, limit? })` Retrieve messages for a specific thread. ```typescript const messages = await harness.session.thread.listMessages({ threadId: 'thread-abc123' }) ``` ### `session.thread.firstUserMessage({ threadId })` Retrieve the first user message for a thread, or `null` if none. ```typescript const firstMsg = await harness.session.thread.firstUserMessage({ threadId: 'thread-abc123' }) ``` ### `session.thread.firstUserMessages({ threadIds })` Retrieve the first user message for many threads at once, returned as a map. ```typescript const firstByThread = await harness.session.thread.firstUserMessages({ threadIds: ['thread-a', 'thread-b'], }) ``` ### `session.thread.getSetting({ key })` / `setSetting({ key, value })` / `deleteSetting({ key })` Read, write, and remove per-thread settings stored on the active thread's metadata. ```typescript await harness.session.thread.setSetting({ key: 'omThreshold', value: 0.8 }) const value = await harness.session.thread.getSetting({ key: 'omThreshold' }) await harness.session.thread.deleteSetting({ key: 'omThreshold' }) ``` ## Mode `session.mode` owns the active mode selection. Switching modes is performed on the [`Harness`](https://mastra.ai/reference/harness/harness-class) because it emits events and rebinds the stream. ### `session.mode.get()` Return the active mode ID. ```typescript const modeId = harness.session.mode.get() ``` ### `session.mode.resolve()` Return the full `HarnessMode` object for the active mode, resolved against the harness's configured modes. ```typescript const mode = harness.session.mode.resolve() ``` ## Model `session.model` owns the active model selection, including per-mode model memory. Switching models is performed on the [`Harness`](https://mastra.ai/reference/harness/harness-class). ### `session.model.get()` Return the active model ID. ```typescript const modelId = harness.session.model.get() ``` ### `session.model.hasSelection()` Check whether a model is currently selected. ```typescript if (harness.session.model.hasSelection()) { // Ready to send messages } ``` ## Run `session.run` owns run and trace identity plus abort state for the in-flight run. ### `session.run.getRunId()` / `getTraceId()` Return the stored run ID and trace ID for the current run, or `null` when idle. ```typescript const runId = harness.session.run.getRunId() const traceId = harness.session.run.getTraceId() ``` ### `session.run.isRunning()` Return whether a run is currently in progress. ```typescript if (harness.session.run.isRunning()) { // A run is active } ``` ## Stream `session.stream` owns the live subscription to the agent thread stream and its dedup key. ### `session.stream.activeRunId()` Return the run ID active on the live stream, or `null` when no stream is open. ```typescript const runId = harness.session.stream.activeRunId() ``` ### `session.stream.isActive()` Return whether the stream currently has an active run. ```typescript if (harness.session.stream.isActive()) { // The current thread's stream is producing output } ``` ## Suspensions `session.suspensions` owns parked interactive tool calls (such as `ask_user` and `request_access`) awaiting a resume. ### `session.suspensions.hasPending()` Return whether any tool is currently suspended. ```typescript if (harness.session.suspensions.hasPending()) { // At least one interactive tool is waiting for a response } ``` ### `session.suspensions.has({ toolCallId })` Return whether a specific tool call is suspended. ```typescript const waiting = harness.session.suspensions.has({ toolCallId: event.toolCallId }) ``` Resume a suspended tool with [`harness.respondToToolSuspension()`](https://mastra.ai/reference/harness/harness-class). ## Follow-ups `session.followUps` owns the FIFO queue of messages submitted while a run is in progress. ### `session.followUps.count()` Return the number of queued follow-ups. ```typescript const queued = harness.session.followUps.count() ``` ### `session.followUps.isEmpty()` Return whether the follow-up queue is empty. ```typescript if (!harness.session.followUps.isEmpty()) { // Messages are waiting to be processed } ``` ## Approval `session.approval` owns the pending tool-approval gate. ### `session.approval.isArmed()` Return whether a tool is currently awaiting an approval decision. ```typescript if (harness.session.approval.isArmed()) { // Show the approval prompt } ``` Respond with [`session.respondToToolApproval()`](#respondtotoolapproval-decision-requestcontext-). ## Display state `session.displayState` owns the canonical `HarnessDisplayState` snapshot a UI renders from, and the reducer that keeps it in sync with every harness event. ### `session.displayState.get()` Return the current `HarnessDisplayState` snapshot for UI rendering. ```typescript const displayState = harness.session.displayState.get() ``` ### `session.displayState.restoreTasks(tasks)` Restore the task portion of the snapshot after a UI replays persisted task tool history. This is a pure update of the snapshot and does not emit an event, so re-render explicitly after calling it. ```typescript harness.session.displayState.restoreTasks(replayedTasks) ``` After every event the harness emits `display_state_changed`, so high-frequency events such as `message_update`, `tool_update`, and `tool_input_delta` are coalesced into the next snapshot. Subscribe with [`harness.subscribe()`](https://mastra.ai/reference/harness/harness-class) and read the latest snapshot from `session.displayState.get()`. ## State `session.state` owns the schema-validated Harness state for the conversation. It holds the current snapshot, validates updates against the `stateSchema` passed to the Harness, serializes concurrent writes, and emits a `state_changed` event on every change. ### `session.state.get()` Return a readonly copy of the current state snapshot. ```typescript const state = harness.session.state.get() ``` ### `session.state.set(updates)` Merge a partial update into the state. Updates are queued so concurrent calls apply in order, validated against the schema, and emit `state_changed` with the changed keys. ```typescript await harness.session.state.set({ yolo: true }) ``` ### `session.state.update(updater)` Run an updater against the current snapshot and apply its result atomically within the write queue. Use this for read-modify-write changes that must see the latest state. The updater returns `updates` to merge, optional `events` to emit, and a `result` value that `update()` resolves to. ```typescript const added = await harness.session.state.update(current => ({ updates: { count: (current.count ?? 0) + 1 }, result: (current.count ?? 0) + 1, })) ``` ## Related - [Harness class](https://mastra.ai/reference/harness/harness-class) - [Harness overview](https://mastra.ai/docs/harness/overview) - [Threads and state](https://mastra.ai/docs/harness/threads-and-state) - [Tool approvals](https://mastra.ai/docs/harness/tool-approvals)