@mastra/mcp-docs-server 1.1.49-alpha.1 → 1.1.49-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,116 @@
1
+ # Modes
2
+
3
+ Modes define the different personalities or capability sets available within a Harness. Each mode layers its own instructions and tool overrides on top of a shared backing agent. The Harness ensures only one mode is active at a time and handles transitions between them.
4
+
5
+ ## When to use modes
6
+
7
+ Use modes when a single conversational session needs to switch between distinct behaviors. For example, a planning mode that reasons about tasks and a build mode that executes them. Modes share the same thread and state, so the agent retains context across switches.
8
+
9
+ ## Quickstart
10
+
11
+ Import the `Harness` class and create a new instance with your agent and modes:
12
+
13
+ ```typescript
14
+ import { Harness } from '@mastra/core/harness'
15
+ import { myAgent } from './agents'
16
+
17
+ const harness = new Harness({
18
+ id: 'multi-mode',
19
+ agent: myAgent,
20
+ modes: [
21
+ {
22
+ id: 'plan',
23
+ name: 'Plan',
24
+ metadata: { default: true },
25
+ instructions: 'Reason about the task before making changes.',
26
+ },
27
+ { id: 'build', name: 'Build', instructions: 'Implement the approved plan.' },
28
+ ],
29
+ })
30
+
31
+ await harness.init()
32
+ ```
33
+
34
+ ## Defining modes
35
+
36
+ Each mode requires an `id`. When a top-level `agent` is provided, modes layer instructions and tool overrides on the shared agent. Each mode can also specify a `defaultModelId` to bootstrap model selection:
37
+
38
+ ```typescript
39
+ import { Harness } from '@mastra/core/harness'
40
+
41
+ const harness = new Harness({
42
+ id: 'multi-mode',
43
+ agent: myAgent,
44
+ modes: [
45
+ {
46
+ id: 'plan',
47
+ name: 'Plan',
48
+ metadata: { default: true },
49
+ defaultModelId: 'anthropic/claude-sonnet-4-6',
50
+ instructions: 'Reason about the task before making changes.',
51
+ },
52
+ {
53
+ id: 'build',
54
+ name: 'Build',
55
+ defaultModelId: 'anthropic/claude-sonnet-4-6',
56
+ instructions: 'Implement the approved plan.',
57
+ },
58
+ ],
59
+ })
60
+ ```
61
+
62
+ ### Mode tool overrides
63
+
64
+ Modes support two strategies for tool configuration. Use `tools` to replace the backing agent's tools entirely, or `additionalTools` to layer extra tools on top:
65
+
66
+ ```typescript
67
+ // Replace — agent sees only these tools in plan mode
68
+ const planMode = { id: 'plan', tools: { planTool } }
69
+
70
+ // Augment — agent keeps its own tools plus these
71
+ const buildMode = { id: 'build', additionalTools: { deployTool } }
72
+ ```
73
+
74
+ You can't set both `tools` and `additionalTools` on the same mode.
75
+
76
+ ### Mode transitions
77
+
78
+ A mode can declare a `transitionsTo` target. When the `submit_plan` built-in tool runs in that mode, the Harness transitions to the target mode on approval:
79
+
80
+ ```typescript
81
+ const planMode = {
82
+ id: 'plan',
83
+ name: 'Plan',
84
+ transitionsTo: 'build',
85
+ instructions: 'Reason about the task and submit a plan.',
86
+ }
87
+ ```
88
+
89
+ On plan approval, the Harness automatically switches to `build` mode. On rejection, the agent remains in `plan` mode to revise.
90
+
91
+ ## Switching modes
92
+
93
+ Call `switchMode()` to change the active mode. The Harness aborts any in-progress generation, saves the current model to the outgoing mode, loads the incoming mode's model, and emits `mode_changed` and `model_changed` events:
94
+
95
+ ```typescript
96
+ await harness.switchMode({ modeId: 'build' })
97
+ ```
98
+
99
+ ## Querying modes
100
+
101
+ ```typescript
102
+ // List all configured modes
103
+ const modes = harness.listModes()
104
+
105
+ // Get the current mode ID
106
+ const modeId = harness.session.mode.get()
107
+
108
+ // Get the full mode object
109
+ const mode = harness.session.mode.resolve()
110
+ ```
111
+
112
+ ## Related
113
+
114
+ - [Harness overview](https://mastra.ai/docs/harness/overview)
115
+ - [Threads and state](https://mastra.ai/docs/harness/threads-and-state)
116
+ - [API reference](https://mastra.ai/reference/harness/harness-class)
@@ -0,0 +1,129 @@
1
+ # Harness overview
2
+
3
+ > **Alpha:** The `Harness` feature is in alpha stage and subject to breaking changes in minor versions until it graduates from its alpha status.
4
+
5
+ The Harness is a session controller for building interactive agent applications. It handles the runtime concerns that sit between your UI and the agent loop: managing conversation threads, switching between agent modes, persisting state, gating tool execution with approvals, and coordinating subagents. You can focus on what your agent does rather than how to wire it together.
6
+
7
+ A Harness exposes a [`Session`](https://mastra.ai/docs/harness/session) — the per-conversation runtime state that tracks the active mode, model, thread binding, permission grants, follow-up queue, and token usage. The Harness is the shared host; the Session is the conversation running inside it. In a multi-user host, the same Harness can back many Sessions at once.
8
+
9
+ [Mastra Code](https://code.mastra.ai/) is the flagship Harness implementation: A terminal-based coding agent with multi-model support, persistent conversations, and plan-then-execute workflows.
10
+
11
+ ## What you can build
12
+
13
+ The Harness is designed for applications where a user has an ongoing, interactive session with one or more AI agents:
14
+
15
+ | Application | How the Harness helps |
16
+ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | **Coding agents** (TUI or IDE) | Plan mode reasons about changes, build mode executes them. Thread persistence resumes conversations across sessions. Tool approvals gate destructive operations like file writes. |
18
+ | **Multi-step assistants** | A research mode gathers information, a drafting mode produces output. Shared state tracks progress. Subagents handle focused subtasks (e.g., web search, code review). |
19
+ | **Copilot UIs** | Model switching lets users pick the right model for the job. Permission policies control which tools run automatically vs. requiring confirmation. Event subscriptions drive real-time UI updates. |
20
+ | **Autonomous task runners** | Background agents with structured task lists, heartbeat handlers for health checks, and observational memory for learning across threads. |
21
+
22
+ ## When to use the Harness
23
+
24
+ Use the Harness when your application needs:
25
+
26
+ - Multiple agent modes that share one conversation thread (e.g., plan → build → review)
27
+ - A control layer between your UI and the agent loop (model switching, state persistence, thread management)
28
+ - Tool approval flows and permission policies for human-in-the-loop gating
29
+ - Subagent orchestration to delegate focused subtasks with constrained tools
30
+ - Session continuity with persistent threads, state, and observational memory across restarts
31
+
32
+ For single-shot agent calls or basic request-response patterns, use the [Agent class](https://mastra.ai/docs/agents/overview) directly. The Harness adds value when you need session state, mode switching, or interactive approval flows.
33
+
34
+ ## Key capabilities
35
+
36
+ - **Session**: Per-conversation state — active thread, mode, model, grants, follow-ups, token usage, and the display snapshot — accessed through `harness.session`. See [Session](https://mastra.ai/docs/harness/session).
37
+ - **Modes**: Define distinct agent personalities (instructions, tools, model) and switch between them without losing conversation context. See [Modes](https://mastra.ai/docs/harness/modes).
38
+ - **Threads and state**: Persist conversations and structured state across sessions, users, and mode switches. See [Threads and state](https://mastra.ai/docs/harness/threads-and-state).
39
+ - **Subagents**: Spawn focused child agents with constrained tools for subtasks, optionally forking the parent conversation. See [Subagents](https://mastra.ai/docs/harness/subagents).
40
+ - **Tool approvals and permissions**: Configure which tools require user confirmation, grant session-wide exceptions, and handle interactive tool suspension. See [Tool approvals](https://mastra.ai/docs/harness/tool-approvals).
41
+ - **Model management**: Switch models per-mode at runtime, track usage, and resolve gateway-backed models.
42
+ - **Follow-ups and steering**: Queue messages while the agent is running, or inject mid-stream instructions to redirect the agent.
43
+ - **Event system**: Subscribe to typed events (message updates, mode changes, tool approvals) or coalesced `HarnessDisplayState` snapshots to drive your UI.
44
+ - **Observational memory**: Automatic summarization and reflection across threads for long-running agent sessions.
45
+
46
+ ## Quickstart
47
+
48
+ Import the `Harness` class and create a new instance with an agent, storage backend, and modes:
49
+
50
+ ```typescript
51
+ import { Agent } from '@mastra/core/agent'
52
+ import { Harness } from '@mastra/core/harness'
53
+ import { LibSQLStore } from '@mastra/libsql'
54
+
55
+ const agent = new Agent({
56
+ name: 'assistant',
57
+ instructions: 'Help the user plan and complete tasks.',
58
+ model: 'openai/gpt-5.5',
59
+ })
60
+
61
+ const harness = new Harness({
62
+ id: 'my-agent',
63
+ agent,
64
+ storage: new LibSQLStore({ url: 'file:./data.db' }),
65
+ modes: [
66
+ {
67
+ id: 'plan',
68
+ name: 'Plan',
69
+ metadata: { default: true },
70
+ instructions: 'Reason about changes before making them.',
71
+ },
72
+ { id: 'build', name: 'Build', instructions: 'Implement the approved plan.' },
73
+ ],
74
+ })
75
+
76
+ harness.subscribe(event => {
77
+ if (event.type === 'message_update') {
78
+ console.log(event.message)
79
+ }
80
+ })
81
+
82
+ await harness.init()
83
+ await harness.selectOrCreateThread()
84
+ await harness.sendMessage({ content: 'Hello!' })
85
+ ```
86
+
87
+ > **Note:** Visit the [Harness reference](https://mastra.ai/reference/harness/harness-class) for the full constructor parameters and method signatures.
88
+
89
+ ## Architecture
90
+
91
+ The Harness sits between your application layer and the underlying agent loop:
92
+
93
+ ```text
94
+ ┌──────────────────────────┐
95
+ │ Your App (TUI/Web/API) │
96
+ └────────────┬─────────────┘
97
+ │ commands (sendMessage, switchMode, approve)
98
+ │ events (message_update, tool_approval_required)
99
+ ┌────────────▼─────────────┐
100
+ │ Harness (shared host) │
101
+ │ Config · Storage │
102
+ │ Thread lifecycle │
103
+ │ Permission policy │
104
+ │ Subagent spawning │
105
+ │ Event bus │
106
+ │ ┌─────────────────────┐ │
107
+ │ │ harness.session │ │
108
+ │ │ identity · thread │ │
109
+ │ │ mode · model · run │ │
110
+ │ │ grants · follow-ups │ │
111
+ │ │ display state │ │
112
+ │ └─────────────────────┘ │
113
+ └────────────┬─────────────┘
114
+
115
+ ┌────────────▼──────────────┐
116
+ │ Agent + Memory + Tools │
117
+ └───────────────────────────┘
118
+ ```
119
+
120
+ Your app sends commands (send a message, switch mode, approve a tool call) and receives typed events. The Harness manages the lifecycle internally: persisting threads, routing to the correct mode agent, enforcing permissions, and emitting events as state changes.
121
+
122
+ ## Next steps
123
+
124
+ - [Session](https://mastra.ai/docs/harness/session): The per-conversation state on a Harness
125
+ - [Modes](https://mastra.ai/docs/harness/modes): Define and switch between agent personalities
126
+ - [Threads and state](https://mastra.ai/docs/harness/threads-and-state): Manage persistent conversations
127
+ - [Subagents](https://mastra.ai/docs/harness/subagents): Delegate focused subtasks
128
+ - [Tool approvals and permissions](https://mastra.ai/docs/harness/tool-approvals): Human-in-the-loop gating
129
+ - [API reference](https://mastra.ai/reference/harness/harness-class): Full constructor and method docs
@@ -0,0 +1,136 @@
1
+ # Session
2
+
3
+ A `Session` holds the per-conversation state of a Harness: who the conversation is for, which thread is active, the selected mode and model, permission grants, queued follow-ups, token usage, application state, and the display snapshot. You reach it through `harness.session`.
4
+
5
+ ## Harness vs. Session
6
+
7
+ The Harness is the shared host. It owns the machinery that doesn't change from one conversation to the next: configuration, storage, the agent registry, the thread lock, the event bus, and permission _policy_. The Session is the per-conversation state that sits on top of that machinery.
8
+
9
+ A useful way to remember the split: the **Harness** is the application, and the **Session** is the current conversation running inside it. Operations that mutate shared infrastructure or coordinate the event bus stay on the Harness; everything that describes "this conversation right now" lives on the Session.
10
+
11
+ | Lives on the Harness (shared host) | Lives on the Session (per-conversation) |
12
+ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
13
+ | Configuration, storage, and memory | Resource ID and active thread binding ([`session.identity`](#identity), [`session.thread`](#thread)) |
14
+ | Thread lifecycle: `createThread`, `switchThread`, `cloneThread`, `renameThread`, `deleteThread` | Thread and message reads ([`session.thread.list()`](#thread)) |
15
+ | The event bus (`subscribe`, event emission) | Selected mode and model ([`session.mode`](#mode-and-model), [`session.model`](#mode-and-model)) |
16
+ | Permission _policy_ (`setPermissionForCategory`, `getToolCategory`) | Permission _grants_ ([`session.grantCategory`](#permission-grants)) |
17
+ | Mode and model _definitions_ (`config.modes`) | Run state, abort, follow-ups, suspensions ([`session.run`](#run-state), [`session.followUps`](#follow-ups)) |
18
+ | The `stateSchema` definition | Application state and its updates ([`session.state`](#state)) |
19
+ | The shared agent loop and subagent spawning | Token usage and the display snapshot ([`session.displayState`](#display-state)) |
20
+
21
+ This is why thread _creation_ is `harness.createThread()` (it acquires the shared lock and emits an event) while thread _reads_ are `harness.session.thread.list()` (they describe the current conversation's view).
22
+
23
+ In single-player apps there is one Harness and one Session, so the split is mostly organizational. In a multi-user host, the same Harness can serve many conversations — each backed by its own Session — without one conversation's mode, grants, or active thread leaking into another.
24
+
25
+ ## Identity
26
+
27
+ `session.identity` holds the resource ID the conversation belongs to and the currently bound thread ID. Threads are scoped to a resource ID, so this is what groups a conversation's threads together:
28
+
29
+ ```typescript
30
+ const resourceId = harness.session.identity.getResourceId()
31
+ const threadId = harness.session.thread.getId()
32
+ ```
33
+
34
+ To change the resource a conversation operates under, use `harness.setResourceId()` — it lives on the Harness because it also clears the active thread and releases the shared lock.
35
+
36
+ ## Thread
37
+
38
+ `session.thread` owns the active thread binding and read access to threads and messages. The Harness performs lifecycle transitions; the Session reads the result:
39
+
40
+ ```typescript
41
+ // Lifecycle transitions live on the Harness
42
+ await harness.switchThread({ threadId: 'thread-abc123' })
43
+
44
+ // Reads live on the Session
45
+ const threads = await harness.session.thread.list()
46
+ const messages = await harness.session.thread.listActiveMessages()
47
+ ```
48
+
49
+ See [Threads and state](https://mastra.ai/docs/harness/threads-and-state) for the full lifecycle.
50
+
51
+ ## Mode and model
52
+
53
+ The Harness defines the available modes in `config.modes`. The Session tracks which mode and model are _currently_ selected:
54
+
55
+ ```typescript
56
+ // Switch mode (Harness orchestration — aborts the run, emits events)
57
+ await harness.switchMode({ modeId: 'build' })
58
+
59
+ // Read the current selection (Session state)
60
+ const modeId = harness.session.mode.get()
61
+ const mode = harness.session.mode.resolve()
62
+ const modelId = harness.session.model.get()
63
+ ```
64
+
65
+ See [Modes](https://mastra.ai/docs/harness/modes) for how modes carry their own model.
66
+
67
+ ## Permission grants
68
+
69
+ The Harness owns permission _policy_ — which categories or tools require approval. The Session owns the _grants_ a user makes during the conversation. A grant lets a tool run for the rest of the session without prompting again:
70
+
71
+ ```typescript
72
+ // Grant a category or tool for the rest of the session
73
+ harness.session.grantCategory('edit')
74
+ harness.session.grantTool('mastra_workspace_execute_command')
75
+
76
+ // Inspect current grants
77
+ const grants = harness.session.getGrants()
78
+ ```
79
+
80
+ Grants are intentionally in-memory and reset when the session restarts. See [Tool approvals](https://mastra.ai/docs/harness/tool-approvals) for the approval flow.
81
+
82
+ ## Run state
83
+
84
+ `session.run` tracks the in-flight generation: whether a run is active, its run and trace IDs, and the abort signal. Use it to reflect run status in your UI:
85
+
86
+ ```typescript
87
+ if (harness.session.run.isRunning()) {
88
+ // show a stop button
89
+ }
90
+
91
+ // Abort the active run (Harness orchestration clears related state)
92
+ harness.abort()
93
+ ```
94
+
95
+ ## Follow-ups
96
+
97
+ While a run is in progress, queued follow-up messages live on `session.followUps`. They are sent when the current run finishes:
98
+
99
+ ```typescript
100
+ const queued = harness.session.followUps.count()
101
+ ```
102
+
103
+ ## State
104
+
105
+ `session.state` holds the conversation's application state — structured values that agents and the UI share, such as model preferences, feature flags, or progress. The Harness defines the shape with a `stateSchema`; the Session owns the live snapshot, validates updates against the schema, and emits `state_changed` on every write:
106
+
107
+ ```typescript
108
+ const state = harness.session.state.get()
109
+ await harness.session.state.set({ theme: 'light' })
110
+ ```
111
+
112
+ See [Threads and state](https://mastra.ai/docs/harness/threads-and-state) for defining a schema.
113
+
114
+ ## Display state
115
+
116
+ `session.displayState` is the coalesced `HarnessDisplayState` snapshot — the single object a UI renders from. It folds run status, streaming messages, active tools, tasks, token usage, and pending approvals into one place:
117
+
118
+ ```typescript
119
+ const snapshot = harness.session.displayState.get()
120
+
121
+ // React to changes
122
+ harness.subscribe(event => {
123
+ if (event.type === 'display_state_changed') {
124
+ render(harness.session.displayState.get())
125
+ }
126
+ })
127
+ ```
128
+
129
+ > **Note:** Visit the [Session reference](https://mastra.ai/reference/harness/session) for the full list of sub-objects and method signatures.
130
+
131
+ ## Related
132
+
133
+ - [Harness overview](https://mastra.ai/docs/harness/overview)
134
+ - [Threads and state](https://mastra.ai/docs/harness/threads-and-state)
135
+ - [Tool approvals](https://mastra.ai/docs/harness/tool-approvals)
136
+ - [Session reference](https://mastra.ai/reference/harness/session)
@@ -0,0 +1,109 @@
1
+ # Subagents
2
+
3
+ Subagents let a parent agent delegate focused tasks to child agents with constrained tools and instructions. The Harness auto-generates a `subagent` built-in tool that the parent model can call to spawn any configured subagent type.
4
+
5
+ ## When to use subagents
6
+
7
+ Use subagents when:
8
+
9
+ - A task needs a different toolset or instruction set than the parent agent
10
+ - You want to isolate a subtask's messages from the parent thread
11
+ - The parent agent needs to delegate to a cheaper or faster model for specific work
12
+
13
+ ## Quickstart
14
+
15
+ Define subagent types in the Harness constructor:
16
+
17
+ ```typescript
18
+ import { Agent } from '@mastra/core/agent'
19
+ import { Harness } from '@mastra/core/harness'
20
+
21
+ const agent = new Agent({
22
+ name: 'assistant',
23
+ instructions: 'Use subagents when a focused task needs a narrower toolset.',
24
+ model: 'openai/gpt-5.5',
25
+ })
26
+
27
+ const harness = new Harness({
28
+ id: 'with-subagents',
29
+ agent,
30
+ modes: [{ id: 'default', name: 'Default', metadata: { default: true } }],
31
+ subagents: [
32
+ {
33
+ id: 'explore',
34
+ name: 'Explore',
35
+ description: 'Reads files and gathers context without making changes.',
36
+ instructions: 'You are a read-only exploration agent.',
37
+ allowedWorkspaceTools: ['read_file', 'list_directory', 'grep_search'],
38
+ defaultModelId: 'anthropic/claude-haiku-4-5',
39
+ maxSteps: 30,
40
+ },
41
+ {
42
+ id: 'execute',
43
+ name: 'Execute',
44
+ description: 'Makes changes to files and runs commands.',
45
+ instructions: 'You are an execution agent.',
46
+ allowedWorkspaceTools: ['read_file', 'write_file', 'execute_command'],
47
+ defaultModelId: 'anthropic/claude-sonnet-4-6',
48
+ },
49
+ ],
50
+ })
51
+ ```
52
+
53
+ The parent agent can then call the auto-generated `subagent` tool with a `task` description and optional `agentType`.
54
+
55
+ For the full list of subagent configuration options, see [`Harness` reference](https://mastra.ai/reference/harness/harness-class).
56
+
57
+ ## Forked subagents
58
+
59
+ By default, a subagent runs with a fresh context and doesn't see the parent conversation. **Forked subagents** run on a clone of the parent thread and reuse the parent agent's configuration. This preserves prompt-cache prefixes and gives the subagent full conversation context.
60
+
61
+ Enable forked mode per-type:
62
+
63
+ ```typescript
64
+ const collaborator = {
65
+ id: 'collaborator',
66
+ name: 'Collaborator',
67
+ description: 'Continues the conversation in a fork.',
68
+ instructions: '...', // Ignored in forked mode
69
+ forked: true,
70
+ }
71
+ ```
72
+
73
+ Or per-invocation via the `subagent` tool's `forked` input parameter.
74
+
75
+ ### Forked mode semantics
76
+
77
+ - Memory must be configured on the Harness (forking clones the thread)
78
+ - The parent agent's instructions, tools, model, and `maxSteps` apply
79
+ - The subagent definition's own `instructions`, `tools`, and `defaultModelId` are ignored
80
+ - Fork threads are tagged with `metadata.forkedSubagent === true` and hidden from `session.thread.list()` by default
81
+ - Recursive forks are blocked at runtime to prevent infinite nesting
82
+
83
+ ### When to prefer non-forked mode
84
+
85
+ Use non-forked subagents when the child needs a strictly smaller toolset, a different system prompt, or a cheaper model. Pass any required context explicitly in the `task` description.
86
+
87
+ ## Subagent model management
88
+
89
+ Set a global or per-type subagent model at runtime:
90
+
91
+ ```typescript
92
+ // Global subagent model
93
+ await harness.setSubagentModelId({ modelId: 'anthropic/claude-sonnet-4-6' })
94
+
95
+ // Per-type override
96
+ await harness.setSubagentModelId({
97
+ modelId: 'anthropic/claude-haiku-4-5',
98
+ agentType: 'explore',
99
+ })
100
+
101
+ // Read current model
102
+ const modelId = harness.getSubagentModelId({ agentType: 'explore' })
103
+ ```
104
+
105
+ ## Related
106
+
107
+ - [Harness overview](https://mastra.ai/docs/harness/overview)
108
+ - [Tool approvals and permissions](https://mastra.ai/docs/harness/tool-approvals)
109
+ - [API reference](https://mastra.ai/reference/harness/harness-class)
@@ -0,0 +1,134 @@
1
+ # Threads and state
2
+
3
+ The Harness manages conversation threads and shared state that persist across mode switches, model changes, and sessions. Threads hold messages while state holds structured data that agents and the UI can read and write.
4
+
5
+ Thread _lifecycle_ transitions (create, switch, clone, delete) live on the Harness because they coordinate the shared thread lock and emit events. The active thread binding and thread/message _reads_ live on the [`Session`](https://mastra.ai/docs/harness/session) as `harness.session.thread`.
6
+
7
+ ## When to use threads and state
8
+
9
+ Use threads when your application needs persistent conversation history, multi-session continuity, or when users switch between threads. Use state for structured values (model preferences, feature flags, UI settings) that agents and the UI layer share.
10
+
11
+ ## Threads
12
+
13
+ ### Creating and selecting threads
14
+
15
+ On startup, call `selectOrCreateThread()` to resume the most recent thread or create a new one:
16
+
17
+ ```typescript
18
+ await harness.init()
19
+ const thread = await harness.selectOrCreateThread()
20
+ ```
21
+
22
+ Create a thread explicitly with a title:
23
+
24
+ ```typescript
25
+ const thread = await harness.createThread({ title: 'New conversation' })
26
+ ```
27
+
28
+ ### Switching threads
29
+
30
+ Switch to an existing thread. The harness aborts any in-progress generation, acquires a lock on the new thread, and emits a `thread_changed` event:
31
+
32
+ ```typescript
33
+ await harness.switchThread({ threadId: 'thread-abc123' })
34
+ ```
35
+
36
+ ### Listing threads
37
+
38
+ List threads for the current resource. Forked subagent threads are hidden by default:
39
+
40
+ ```typescript
41
+ const threads = await harness.session.thread.list()
42
+
43
+ // Include all resources
44
+ const allThreads = await harness.session.thread.list({ allResources: true })
45
+ ```
46
+
47
+ ### Cloning threads
48
+
49
+ Clone a thread to create a branch of the conversation. The harness copies all messages and switches to the clone:
50
+
51
+ ```typescript
52
+ const cloned = await harness.cloneThread({ title: 'Alternative approach' })
53
+ ```
54
+
55
+ ### Thread locking
56
+
57
+ Pass a `threadLock` to the Harness constructor to prevent concurrent access from multiple processes. The lock is acquired before any thread operation and released on switch or delete:
58
+
59
+ ```typescript
60
+ const harness = new Harness({
61
+ id: 'my-agent',
62
+ threadLock: {
63
+ acquire: async threadId => {
64
+ /* acquire lock or throw */
65
+ },
66
+ release: async threadId => {
67
+ /* release lock */
68
+ },
69
+ },
70
+ })
71
+ ```
72
+
73
+ ## State
74
+
75
+ ### Defining a state schema
76
+
77
+ Pass a `stateSchema` (Standard JSON Schema) to validate state and extract defaults:
78
+
79
+ ```typescript
80
+ import { Agent } from '@mastra/core/agent'
81
+ import { Harness } from '@mastra/core/harness'
82
+ import { z } from 'zod'
83
+
84
+ const agent = new Agent({
85
+ name: 'assistant',
86
+ instructions: 'Help the user manage a stateful session.',
87
+ model: 'openai/gpt-5.5',
88
+ })
89
+
90
+ const harness = new Harness({
91
+ id: 'stateful-agent',
92
+ agent,
93
+ modes: [{ id: 'default', name: 'Default', metadata: { default: true } }],
94
+ stateSchema: z.object({
95
+ currentModelId: z.string().optional(),
96
+ theme: z.enum(['light', 'dark']).default('dark'),
97
+ }),
98
+ })
99
+ ```
100
+
101
+ ### Reading and writing state
102
+
103
+ State is owned by the [`Session`](https://mastra.ai/docs/harness/session) as `harness.session.state`:
104
+
105
+ ```typescript
106
+ // Read the current state snapshot
107
+ const state = harness.session.state.get()
108
+
109
+ // Update state — validates against schema and emits state_changed
110
+ await harness.session.state.set({ theme: 'light' })
111
+ ```
112
+
113
+ State changes emit a `state_changed` event with the new state and the set of changed keys.
114
+
115
+ ## Resource IDs
116
+
117
+ Threads are scoped to a resource ID. The resource ID defaults to the harness `id` but can be set explicitly to group threads by project, user, or workspace:
118
+
119
+ ```typescript
120
+ const harness = new Harness({
121
+ id: 'my-agent',
122
+ resourceId: 'project-xyz',
123
+ })
124
+
125
+ // Change resource ID at runtime (clears the current thread)
126
+ harness.setResourceId({ resourceId: 'project-abc' })
127
+ ```
128
+
129
+ ## Related
130
+
131
+ - [Harness overview](https://mastra.ai/docs/harness/overview)
132
+ - [Session](https://mastra.ai/docs/harness/session)
133
+ - [Modes](https://mastra.ai/docs/harness/modes)
134
+ - [API reference](https://mastra.ai/reference/harness/harness-class)