@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.
- package/.docs/docs/harness/modes.md +116 -0
- package/.docs/docs/harness/overview.md +129 -0
- package/.docs/docs/harness/session.md +136 -0
- package/.docs/docs/harness/subagents.md +109 -0
- package/.docs/docs/harness/threads-and-state.md +134 -0
- package/.docs/docs/harness/tool-approvals.md +145 -0
- package/.docs/reference/harness/harness-class.md +124 -186
- package/.docs/reference/harness/session.md +429 -0
- package/.docs/reference/index.md +1 -0
- package/CHANGELOG.md +7 -0
- package/package.json +4 -4
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# Harness class
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
|
|
5
|
-
> **Alpha:** The `Harness` class is in alpha stage and subject to change. It won't follow semantic versioning guarantees until it graduates from experimental status. Use with caution and expect breaking changes in minor versions.
|
|
6
|
-
>
|
|
7
|
-
> [Mastra Code](https://code.mastra.ai/) is the flagship implementation of the `Harness` class, showcasing how it can be used to build a powerful terminal-based coding agent with multi-model support, persistent conversations, and built-in tools.
|
|
3
|
+
> **Alpha:** The `Harness` feature is in alpha stage and subject to breaking changes in minor versions until it graduates from its alpha status.
|
|
8
4
|
|
|
9
5
|
The `Harness` class orchestrates multiple agent modes, shared state, memory, and storage. It provides a control layer that a TUI or other UI can drive to manage threads, switch models and modes, send messages, handle tool approvals, and track events.
|
|
10
6
|
|
|
7
|
+
Per-conversation state — identity, the active thread, mode and model selection, run state, grants, and the display snapshot — lives on the [`Session`](https://mastra.ai/reference/harness/session), accessed through `harness.session`.
|
|
8
|
+
|
|
9
|
+
For a conceptual introduction, see the [Harness overview](https://mastra.ai/docs/harness/overview).
|
|
10
|
+
|
|
11
11
|
## Usage example
|
|
12
12
|
|
|
13
|
+
Import the `Harness` class and create a new instance with your agent, storage backend, and modes:
|
|
14
|
+
|
|
13
15
|
```typescript
|
|
14
16
|
import { Harness } from '@mastra/core/harness'
|
|
15
17
|
import { LibSQLStore } from '@mastra/libsql'
|
|
@@ -17,13 +19,19 @@ import { z } from 'zod'
|
|
|
17
19
|
|
|
18
20
|
const harness = new Harness({
|
|
19
21
|
id: 'my-coding-agent',
|
|
22
|
+
agent: myAgent,
|
|
20
23
|
storage: new LibSQLStore({ url: 'file:./data.db' }),
|
|
21
24
|
stateSchema: z.object({
|
|
22
25
|
currentModelId: z.string().optional(),
|
|
23
26
|
}),
|
|
24
27
|
modes: [
|
|
25
|
-
{
|
|
26
|
-
|
|
28
|
+
{
|
|
29
|
+
id: 'plan',
|
|
30
|
+
name: 'Plan',
|
|
31
|
+
metadata: { default: true },
|
|
32
|
+
instructions: 'Reason about changes before making them.',
|
|
33
|
+
},
|
|
34
|
+
{ id: 'build', name: 'Build', transitionsTo: 'plan' },
|
|
27
35
|
],
|
|
28
36
|
})
|
|
29
37
|
|
|
@@ -46,30 +54,48 @@ await harness.sendMessage({ content: 'Hello!' })
|
|
|
46
54
|
|
|
47
55
|
**storage** (`MastraCompositeStore`): Storage backend for persistence (threads, messages, state).
|
|
48
56
|
|
|
49
|
-
**stateSchema** (`StandardJSONSchemaV1`): Standard JSON Schema defining the shape of
|
|
57
|
+
**stateSchema** (`StandardJSONSchemaV1`): Standard JSON Schema defining the shape of application state, accessed at runtime via session.state. Used for validation and extracting defaults.
|
|
50
58
|
|
|
51
59
|
**initialState** (`Partial<z.infer<TState>>`): Initial state values. Must conform to the schema if provided.
|
|
52
60
|
|
|
53
61
|
**memory** (`MastraMemory`): Memory configuration shared across all modes. Propagated to mode agents that don't have their own memory.
|
|
54
62
|
|
|
55
|
-
**modes** (`HarnessMode[]`): Available agent modes. At least one mode is required.
|
|
63
|
+
**modes** (`HarnessMode[]`): Available agent modes. At least one mode is required. When a top-level \`agent\` is provided, each mode layers its own instructions and tool overrides on top of the shared agent.
|
|
56
64
|
|
|
57
65
|
**modes.id** (`string`): Unique identifier for this mode (e.g., \`"plan"\`, \`"build"\`).
|
|
58
66
|
|
|
59
67
|
**modes.name** (`string`): Human-readable name for display.
|
|
60
68
|
|
|
61
|
-
**modes.default** (`boolean`): Whether this is the default mode when the harness starts.
|
|
69
|
+
**modes.default** (`boolean`): Whether this is the default mode when the harness starts. Deprecated in favor of \`metadata.default\` or the top-level \`defaultModeId\`.
|
|
62
70
|
|
|
63
71
|
**modes.defaultModelId** (`string`): Default model ID for this mode (e.g., \`"anthropic/claude-sonnet-4-20250514"\`). Used when no per-mode model has been explicitly selected.
|
|
64
72
|
|
|
65
|
-
**modes.
|
|
73
|
+
**modes.description** (`string`): Description surfaced in mode pickers and Studio UI.
|
|
74
|
+
|
|
75
|
+
**modes.instructions** (`string`): Additional instructions layered above the backing agent's own instructions for this mode.
|
|
76
|
+
|
|
77
|
+
**modes.transitionsTo** (`string`): Target mode ID to switch to on \`submit\_plan\` approval. Must reference another mode's \`id\`.
|
|
78
|
+
|
|
79
|
+
**modes.metadata** (`Record<string, unknown>`): Arbitrary metadata. \`metadata.default === true\` marks the default mode when \`defaultModeId\` is unset.
|
|
80
|
+
|
|
81
|
+
**modes.tools** (`ToolsInput`): Replaces the backing agent's tools for this mode. Mutually exclusive with \`additionalTools\`.
|
|
66
82
|
|
|
67
|
-
**modes.
|
|
83
|
+
**modes.additionalTools** (`ToolsInput`): Tools layered on top of the backing agent's tools. Mutually exclusive with \`tools\`.
|
|
84
|
+
|
|
85
|
+
**modes.agent** (`Agent`): The agent for this mode. Deprecated in favor of the top-level \`agent\` config with mode-level overrides.
|
|
86
|
+
|
|
87
|
+
**agent** (`Agent`): Shared backing agent that each mode forks and decorates. When provided, modes layer instructions and tool overrides on top of this agent instead of providing their own.
|
|
88
|
+
|
|
89
|
+
**defaultModeId** (`string`): Default mode to enter when a thread has no persisted mode. Takes precedence over \`metadata.default\` on individual modes.
|
|
90
|
+
|
|
91
|
+
**instructions** (`string`): Base instructions shared across all modes. Appended to the backing agent's instructions.
|
|
68
92
|
|
|
69
93
|
**tools** (`ToolsInput | ((ctx) => ToolsInput)`): Tools available to all agents across all modes. It can be a static tools object or a dynamic function that receives the request context.
|
|
70
94
|
|
|
71
95
|
**workspace** (`Workspace | WorkspaceConfig | ((ctx) => Workspace)`): Workspace configuration. Accepts a pre-constructed Workspace, a WorkspaceConfig for the harness to construct internally, or a dynamic factory function.
|
|
72
96
|
|
|
97
|
+
**browser** (`MastraBrowser | ((ctx) => MastraBrowser)`): Browser automation configuration. Propagated to mode agents that don't have their own browser configured.
|
|
98
|
+
|
|
73
99
|
**subagents** (`HarnessSubagent[]`): Subagent definitions. When provided, the harness creates a built-in \`subagent\` tool that parent agents can call to spawn focused subagents.
|
|
74
100
|
|
|
75
101
|
**subagents.id** (`string`): Unique identifier for this subagent type (e.g., \`"explore"\`, \`"execute"\`).
|
|
@@ -108,14 +134,26 @@ await harness.sendMessage({ content: 'Hello!' })
|
|
|
108
134
|
|
|
109
135
|
**modelUseCountProvider** (`ModelUseCountProvider`): Provides per-model use counts for sorting and display in \`listAvailableModels()\`.
|
|
110
136
|
|
|
137
|
+
**modelUseCountTracker** (`ModelUseCountTracker`): Callback invoked when a model is selected via \`switchModel()\`. Use to track and persist model usage for ranking.
|
|
138
|
+
|
|
139
|
+
**customModelCatalogProvider** (`CustomModelCatalogProvider`): Catalog hook for additional models (e.g., user-defined custom providers). Returned entries are merged into \`listAvailableModels()\`.
|
|
140
|
+
|
|
141
|
+
**gateways** (`MastraModelGatewayInterface[]`): Model gateways registered on the internal Mastra instance. Apps that need gateway-backed model resolution should also provide \`resolveModel\`.
|
|
142
|
+
|
|
111
143
|
**toolCategoryResolver** (`(toolName: string) => ToolCategory | null`): Maps tool names to permission categories (\`'read'\`, \`'edit'\`, \`'execute'\`, \`'mcp'\`, \`'other'\`). Used by the permission system to resolve category-level policies.
|
|
112
144
|
|
|
145
|
+
**pubsub** (`PubSub`): PubSub instance used by the internal Mastra instance and mode agents for event coordination.
|
|
146
|
+
|
|
113
147
|
**threadLock** (`{ acquire, release }`): Thread locking callbacks to prevent concurrent access from multiple processes. \`acquire\` should throw if the lock is held.
|
|
114
148
|
|
|
149
|
+
**observability** (`ObservabilityEntrypoint`): Observability entrypoint for tracing, scoring, and feedback. When provided, the internal Mastra instance produces trace spans for agent runs.
|
|
150
|
+
|
|
115
151
|
## Properties
|
|
116
152
|
|
|
117
153
|
**id** (`string`): Harness identifier, set at construction.
|
|
118
154
|
|
|
155
|
+
**session** (`Session`): The per-conversation state for the active conversation: identity, the active thread binding and reads, mode and model selection, run and abort state, the live stream, tool suspensions, follow-ups, approvals, permission grants, token usage, and the display-state snapshot. See the Session reference for the full API.
|
|
156
|
+
|
|
119
157
|
## Methods
|
|
120
158
|
|
|
121
159
|
### Lifecycle
|
|
@@ -144,40 +182,44 @@ Stop all heartbeat handlers and clean up resources.
|
|
|
144
182
|
await harness.destroy()
|
|
145
183
|
```
|
|
146
184
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
#### `getState()`
|
|
185
|
+
#### `removeHeartbeat({ id })`
|
|
150
186
|
|
|
151
|
-
|
|
187
|
+
Remove a specific heartbeat handler by ID. Calls the handler's `shutdown()` callback if defined.
|
|
152
188
|
|
|
153
189
|
```typescript
|
|
154
|
-
|
|
190
|
+
await harness.removeHeartbeat({ id: 'gateway-sync' })
|
|
155
191
|
```
|
|
156
192
|
|
|
157
|
-
#### `
|
|
193
|
+
#### `stopHeartbeats()`
|
|
158
194
|
|
|
159
|
-
|
|
195
|
+
Stop and remove all heartbeat handlers.
|
|
160
196
|
|
|
161
197
|
```typescript
|
|
162
|
-
|
|
198
|
+
await harness.stopHeartbeats()
|
|
163
199
|
```
|
|
164
200
|
|
|
165
|
-
#### `
|
|
201
|
+
#### `getCurrentAgent()`
|
|
202
|
+
|
|
203
|
+
Return the fully-configured Agent for the current mode with runtime services (storage, memory, workspace, pubsub, telemetry) propagated.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const agent = harness.getCurrentAgent()
|
|
207
|
+
```
|
|
166
208
|
|
|
167
|
-
|
|
209
|
+
#### `getResolvedMemory()`
|
|
168
210
|
|
|
169
|
-
|
|
211
|
+
Return the resolved memory instance, or `null` if no memory is configured.
|
|
170
212
|
|
|
171
213
|
```typescript
|
|
172
|
-
harness.
|
|
214
|
+
const memory = await harness.getResolvedMemory()
|
|
173
215
|
```
|
|
174
216
|
|
|
175
|
-
#### `
|
|
217
|
+
#### `getMastra()`
|
|
176
218
|
|
|
177
|
-
|
|
219
|
+
Return the internal `Mastra` instance, or `undefined` before `init()`. Useful for scorer registration, observability access, and eval tooling.
|
|
178
220
|
|
|
179
221
|
```typescript
|
|
180
|
-
|
|
222
|
+
const mastra = harness.getMastra()
|
|
181
223
|
```
|
|
182
224
|
|
|
183
225
|
### Modes
|
|
@@ -190,21 +232,7 @@ Return all configured `HarnessMode` instances.
|
|
|
190
232
|
const modes = harness.listModes()
|
|
191
233
|
```
|
|
192
234
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Return the ID of the currently active mode.
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
const modeId = harness.getCurrentModeId()
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
#### `getCurrentMode()`
|
|
202
|
-
|
|
203
|
-
Return the `HarnessMode` object for the current mode.
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
const mode = harness.getCurrentMode()
|
|
207
|
-
```
|
|
235
|
+
To read the active mode, use [`session.mode.get()`](https://mastra.ai/reference/harness/session); to resolve it to a full `HarnessMode`, use [`session.mode.resolve()`](https://mastra.ai/reference/harness/session).
|
|
208
236
|
|
|
209
237
|
#### `switchMode({ modeId })`
|
|
210
238
|
|
|
@@ -216,13 +244,7 @@ await harness.switchMode({ modeId: 'build' })
|
|
|
216
244
|
|
|
217
245
|
### Models
|
|
218
246
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Return the ID of the currently selected model from state.
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
const modelId = harness.getCurrentModelId()
|
|
225
|
-
```
|
|
247
|
+
To read the active model ID, use [`session.model.get()`](https://mastra.ai/reference/harness/session); to check whether a model is selected, use [`session.model.hasSelection()`](https://mastra.ai/reference/harness/session).
|
|
226
248
|
|
|
227
249
|
#### `getModelName()`
|
|
228
250
|
|
|
@@ -240,16 +262,6 @@ Return the complete model ID string.
|
|
|
240
262
|
const fullId = harness.getFullModelId()
|
|
241
263
|
```
|
|
242
264
|
|
|
243
|
-
#### `hasModelSelected()`
|
|
244
|
-
|
|
245
|
-
Check if a model ID is currently selected.
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
if (harness.hasModelSelected()) {
|
|
249
|
-
// Ready to send messages
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
265
|
#### `switchModel({ modelId, scope?, modeId? })`
|
|
254
266
|
|
|
255
267
|
Switch the active model. When `scope` is `'thread'`, the model ID is persisted to thread metadata so it's restored when switching back. Emits a `model_changed` event.
|
|
@@ -282,13 +294,7 @@ const models = await harness.listAvailableModels()
|
|
|
282
294
|
|
|
283
295
|
### Threads
|
|
284
296
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
Return the ID of the currently active thread.
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
const threadId = harness.getCurrentThreadId()
|
|
291
|
-
```
|
|
297
|
+
The harness owns thread lifecycle transitions — creating, switching, cloning, renaming, and deleting threads — because they coordinate the shared thread lock and emit events. The active thread binding and thread/message reads live on [`session.thread`](https://mastra.ai/reference/harness/session).
|
|
292
298
|
|
|
293
299
|
#### `createThread({ title? })`
|
|
294
300
|
|
|
@@ -306,22 +312,7 @@ Switch to a different thread. Aborts any in-progress operations, acquires a lock
|
|
|
306
312
|
await harness.switchThread({ threadId: 'thread-abc123' })
|
|
307
313
|
```
|
|
308
314
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
List threads from storage. By default, only threads for the current resource are returned, and transient [forked subagent](#forked-subagents) threads are hidden so they don’t appear in user-facing thread pickers / startup flows.
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
// List threads for current resource (forks hidden)
|
|
315
|
-
const threads = await harness.listThreads()
|
|
316
|
-
|
|
317
|
-
// List all threads across resources (forks still hidden)
|
|
318
|
-
const allThreads = await harness.listThreads({ allResources: true })
|
|
319
|
-
|
|
320
|
-
// Include forked subagent fork threads (debug / admin tooling only)
|
|
321
|
-
const everything = await harness.listThreads({ includeForkedSubagents: true })
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Fork threads are tagged with `metadata.forkedSubagent === true` (and `metadata.parentThreadId`) by the harness. Set `includeForkedSubagents: true` to opt back into seeing them — e.g. for a debug panel.
|
|
315
|
+
To list threads from storage, use [`session.thread.list()`](https://mastra.ai/reference/harness/session). By default it returns only threads for the current resource and hides transient [forked subagent](#forked-subagents) threads; pass `includeForkedSubagents: true` to opt back into seeing them — e.g. for a debug panel.
|
|
325
316
|
|
|
326
317
|
#### `renameThread({ title })`
|
|
327
318
|
|
|
@@ -348,20 +339,22 @@ const cloned = await harness.cloneThread({
|
|
|
348
339
|
|
|
349
340
|
See [`Memory.cloneThread()`](https://mastra.ai/reference/memory/cloneThread) for details on what gets cloned.
|
|
350
341
|
|
|
351
|
-
|
|
342
|
+
To read the current resource ID, use [`session.identity.getResourceId()`](https://mastra.ai/reference/harness/session).
|
|
352
343
|
|
|
353
|
-
|
|
344
|
+
#### `setResourceId({ resourceId })`
|
|
345
|
+
|
|
346
|
+
Set the resource ID and clear the current thread.
|
|
354
347
|
|
|
355
348
|
```typescript
|
|
356
|
-
|
|
349
|
+
harness.setResourceId({ resourceId: 'project-xyz' })
|
|
357
350
|
```
|
|
358
351
|
|
|
359
|
-
#### `
|
|
352
|
+
#### `getKnownResourceIds()`
|
|
360
353
|
|
|
361
|
-
|
|
354
|
+
Return the distinct resource IDs that have threads in storage.
|
|
362
355
|
|
|
363
356
|
```typescript
|
|
364
|
-
harness.
|
|
357
|
+
const resourceIds = await harness.getKnownResourceIds()
|
|
365
358
|
```
|
|
366
359
|
|
|
367
360
|
#### `getSession()`
|
|
@@ -383,52 +376,11 @@ Send a message to the current agent. Creates a thread if none exists, builds a `
|
|
|
383
376
|
await harness.sendMessage({ content: 'Explain the authentication flow' })
|
|
384
377
|
```
|
|
385
378
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
Retrieve messages for the current thread.
|
|
389
|
-
|
|
390
|
-
```typescript
|
|
391
|
-
const messages = await harness.listMessages()
|
|
392
|
-
|
|
393
|
-
// Limit to the last 50 messages
|
|
394
|
-
const recent = await harness.listMessages({ limit: 50 })
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
#### `listMessagesForThread({ threadId, limit? })`
|
|
398
|
-
|
|
399
|
-
Retrieve messages for a specific thread.
|
|
400
|
-
|
|
401
|
-
```typescript
|
|
402
|
-
const messages = await harness.listMessagesForThread({ threadId: 'thread-abc123' })
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
#### `getFirstUserMessageForThread({ threadId })`
|
|
406
|
-
|
|
407
|
-
Retrieve the first user message for a given thread.
|
|
408
|
-
|
|
409
|
-
```typescript
|
|
410
|
-
const firstMsg = await harness.getFirstUserMessageForThread({ threadId: 'thread-abc123' })
|
|
411
|
-
```
|
|
379
|
+
Reading messages is owned by [`session.thread`](https://mastra.ai/reference/harness/session): use `listActiveMessages()` for the active thread, `listMessages({ threadId })` for a specific thread, and `firstUserMessage({ threadId })` / `firstUserMessages({ threadIds })` for thread previews.
|
|
412
380
|
|
|
413
381
|
### Memory
|
|
414
382
|
|
|
415
|
-
The `memory` property
|
|
416
|
-
|
|
417
|
-
#### `memory.createThread({ title? })`
|
|
418
|
-
|
|
419
|
-
Create a new thread. Same as `harness.createThread()`.
|
|
420
|
-
|
|
421
|
-
#### `memory.switchThread({ threadId })`
|
|
422
|
-
|
|
423
|
-
Switch to a different thread. Same as `harness.switchThread()`.
|
|
424
|
-
|
|
425
|
-
#### `memory.listThreads(options?)`
|
|
426
|
-
|
|
427
|
-
List threads from storage. Same as `harness.listThreads()`.
|
|
428
|
-
|
|
429
|
-
#### `memory.renameThread({ title })`
|
|
430
|
-
|
|
431
|
-
Update the title of the current thread. Same as `harness.renameThread()`.
|
|
383
|
+
The `memory` property bundles thread management operations into a single namespace. `memory.createThread`, `memory.switchThread`, and `memory.renameThread` delegate to the corresponding Harness lifecycle methods documented above; `memory.listThreads` delegates to [`session.thread.list()`](https://mastra.ai/reference/harness/session).
|
|
432
384
|
|
|
433
385
|
#### `memory.deleteThread({ threadId })`
|
|
434
386
|
|
|
@@ -466,14 +418,7 @@ harness.followUp({ content: 'Now apply those changes' })
|
|
|
466
418
|
|
|
467
419
|
### Tool approvals
|
|
468
420
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
Respond to a pending tool approval request. Called when a `tool_approval_required` event is received.
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
harness.respondToToolApproval({ decision: 'approve' })
|
|
475
|
-
harness.respondToToolApproval({ decision: 'decline' })
|
|
476
|
-
```
|
|
421
|
+
Responding to a pending tool approval is owned by the session — see [`session.respondToToolApproval()`](https://mastra.ai/reference/harness/session). The harness owns the permission _policy_ that decides when approval is required, documented under [Permissions](#permissions) below.
|
|
477
422
|
|
|
478
423
|
### Tool suspensions and plans
|
|
479
424
|
|
|
@@ -524,30 +469,7 @@ harness.respondToToolSuspension({
|
|
|
524
469
|
|
|
525
470
|
### Permissions
|
|
526
471
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
Grant a tool category for the current session. Tools in this category are auto-approved without prompting.
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
harness.grantSessionCategory({ category: 'edit' })
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
#### `grantSessionTool({ toolName })`
|
|
536
|
-
|
|
537
|
-
Grant a specific tool for the current session.
|
|
538
|
-
|
|
539
|
-
```typescript
|
|
540
|
-
harness.grantSessionTool({ toolName: 'mastra_workspace_execute_command' })
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
#### `getSessionGrants()`
|
|
544
|
-
|
|
545
|
-
Return currently granted session categories and tools.
|
|
546
|
-
|
|
547
|
-
```typescript
|
|
548
|
-
const grants = harness.getSessionGrants()
|
|
549
|
-
// { categories: Set<string>, tools: Set<string> }
|
|
550
|
-
```
|
|
472
|
+
The harness owns the permission _policy_ — the rules that decide when a tool requires approval. Session-scoped _grants_ (which tools and categories are auto-approved for the current conversation) live on the session: see [`session.grantCategory()`](https://mastra.ai/reference/harness/session), [`session.grantTool()`](https://mastra.ai/reference/harness/session), and [`session.getGrants()`](https://mastra.ai/reference/harness/session).
|
|
551
473
|
|
|
552
474
|
#### `setPermissionForCategory({ category, policy })`
|
|
553
475
|
|
|
@@ -754,7 +676,7 @@ The model can also pass `forked: true` (or `forked: false`) per-invocation in th
|
|
|
754
676
|
- **Memory required.** Forked mode calls `memory.cloneThread` to create the fork, so the harness must have `memory` configured and an active parent thread. Calls without those return a structured error rather than throwing.
|
|
755
677
|
- **Parent agent reused.** The fork runs through the parent agent's `stream(...)` call. The parent's instructions, tools, model, `maxSteps`, and `stopWhen` apply. The subagent definition's `instructions`, `tools`, `allowedHarnessTools`, `allowedWorkspaceTools`, `defaultModelId`, `maxSteps`, and `stopWhen` are ignored in forked mode — this is what preserves the prompt-cache prefix.
|
|
756
678
|
- **Toolsets inherited, recursive forks blocked at runtime.** Forks inherit the parent's toolsets verbatim (`ask_user`, `submit_plan`, user-configured harness tools, _including the `subagent` tool itself_) so the LLM request prefix — system prompt + tool list + tool schemas + tool descriptions — stays byte-identical to the parent's. This is what preserves the prompt cache. The `subagent` entry is kept on the model side but its `execute` is replaced inside the fork with a stub that returns a non-error "tool unavailable inside a forked subagent" message: nested forks are blocked at the runtime layer without perturbing the cached prefix.
|
|
757
|
-
- **Fork threads are tagged.** Each fork thread is created with `metadata.forkedSubagent === true` and `metadata.parentThreadId === <parent>`. By default, [`
|
|
679
|
+
- **Fork threads are tagged.** Each fork thread is created with `metadata.forkedSubagent === true` and `metadata.parentThreadId === <parent>`. By default, [`session.thread.list()`](https://mastra.ai/reference/harness/session) hides these so they don't show up in user-facing thread pickers / startup flows. Pass `includeForkedSubagents: true` to see them in admin / debug tooling.
|
|
758
680
|
- **Save-queue flushed before clone.** The agent stream batches message saves through a debounced `SaveQueueManager`, so the parent's latest user / assistant turn may not be on disk yet when the subagent tool call fires. The fork tool flushes pending saves first via the `flushMessages` callback on `AgentToolExecutionContext` before cloning, so the fork actually carries the latest turn. Flush failures are non-fatal — the clone still runs.
|
|
759
681
|
- **Parent thread untouched.** All subagent activity (messages, OM writes) lands on the fork. The parent thread is never appended to during a forked subagent run.
|
|
760
682
|
|
|
@@ -764,34 +686,25 @@ Forked mode trades isolation for context inheritance. If the subagent should run
|
|
|
764
686
|
|
|
765
687
|
### Events
|
|
766
688
|
|
|
767
|
-
#### `
|
|
689
|
+
#### `subscribe(listener)`
|
|
768
690
|
|
|
769
|
-
Register
|
|
691
|
+
Register an event listener. Returns an unsubscribe function.
|
|
770
692
|
|
|
771
|
-
|
|
772
|
-
const unsubscribe = harness.subscribeDisplayState(displayState => {
|
|
773
|
-
render(displayState)
|
|
774
|
-
})
|
|
693
|
+
Use this method for all consumers — UI, Server-Sent Events (SSE), terminal UI (TUI), bridge rendering, audit logs, debugging, analytics, and deterministic replay. For display rendering, watch for the `display_state_changed` event and read the latest snapshot from [`session.displayState.get()`](https://mastra.ai/reference/harness/session). 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.
|
|
775
694
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
695
|
+
```typescript
|
|
696
|
+
// Render from the coalesced display-state snapshot:
|
|
697
|
+
const unsubscribe = harness.subscribe(event => {
|
|
698
|
+
if (event.type === 'display_state_changed') {
|
|
699
|
+
render(harness.session.displayState.get())
|
|
700
|
+
}
|
|
780
701
|
})
|
|
781
702
|
|
|
782
|
-
//
|
|
783
|
-
|
|
703
|
+
// Render an initial frame before the next harness event:
|
|
704
|
+
render(harness.session.displayState.get())
|
|
784
705
|
```
|
|
785
706
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
`subscribeDisplayState()` doesn't call the listener immediately. Call [`getDisplayState`](#getdisplaystate) first if the UI needs an initial render before the next harness event.
|
|
789
|
-
|
|
790
|
-
#### `subscribe(listener)`
|
|
791
|
-
|
|
792
|
-
Register an event listener. Returns an unsubscribe function.
|
|
793
|
-
|
|
794
|
-
Use this method for audit logs, debugging, analytics, deterministic replay, or consumers that need every raw event. For display rendering, prefer [`subscribeDisplayState`](#subscribedisplaystatelistener-options) so high-frequency events such as `message_update`, `tool_update`, and `tool_input_delta` are coalesced into the latest display state snapshot.
|
|
707
|
+
To handle raw events directly, switch on `event.type`:
|
|
795
708
|
|
|
796
709
|
```typescript
|
|
797
710
|
const unsubscribe = harness.subscribe(event => {
|
|
@@ -840,15 +753,28 @@ The harness emits events through registered listeners. The following table lists
|
|
|
840
753
|
| `usage_update` | Token usage was updated. |
|
|
841
754
|
| `error` | An error occurred. |
|
|
842
755
|
| `info` | An informational message was emitted. |
|
|
756
|
+
| `system_reminder` | A system reminder was injected into the conversation. |
|
|
757
|
+
| `state_signal` | A state signal was emitted (state-driven prompt injection). |
|
|
758
|
+
| `reactive_signal` | A reactive signal fired in response to a state change. |
|
|
759
|
+
| `notification` | A notification was delivered to the thread inbox. |
|
|
760
|
+
| `notification_summary` | A summary of pending notifications was emitted. |
|
|
843
761
|
| `follow_up_queued` | A follow-up message was queued. |
|
|
844
762
|
| `workspace_status_changed` | The workspace status changed. |
|
|
845
763
|
| `workspace_ready` | The workspace finished initializing. |
|
|
846
764
|
| `workspace_error` | The workspace encountered an error. |
|
|
847
765
|
| `om_status` | Observational Memory status update. |
|
|
766
|
+
| `om_activation` | Observational Memory was activated for the thread. |
|
|
767
|
+
| `om_model_changed` | An Observational Memory role model changed (`observer` or `reflector`). |
|
|
848
768
|
| `om_observation_start` | An observation started. |
|
|
849
769
|
| `om_observation_end` | An observation completed. |
|
|
770
|
+
| `om_observation_failed` | An observation failed. |
|
|
850
771
|
| `om_reflection_start` | A reflection started. |
|
|
851
772
|
| `om_reflection_end` | A reflection completed. |
|
|
773
|
+
| `om_reflection_failed` | A reflection failed. |
|
|
774
|
+
| `om_buffering_start` | Observational Memory started buffering messages. |
|
|
775
|
+
| `om_buffering_end` | Observational Memory finished buffering messages. |
|
|
776
|
+
| `om_buffering_failed` | Observational Memory buffering failed. |
|
|
777
|
+
| `om_thread_title_updated` | Observational Memory updated the thread title. |
|
|
852
778
|
| `subagent_start` | A subagent started processing. |
|
|
853
779
|
| `subagent_text_delta` | A subagent emitted a text delta. |
|
|
854
780
|
| `subagent_tool_start` | A subagent started a tool call. |
|
|
@@ -856,6 +782,11 @@ The harness emits events through registered listeners. The following table lists
|
|
|
856
782
|
| `subagent_end` | A subagent finished processing. |
|
|
857
783
|
| `subagent_model_changed` | A subagent's model changed. |
|
|
858
784
|
| `task_updated` | A task list was updated. |
|
|
785
|
+
| `goal_evaluation` | A goal evaluation completed (when native agent goals are configured). |
|
|
786
|
+
| `shell_output` | A tool emitted shell output (stdout or stderr). |
|
|
787
|
+
| `display_state_changed` | The canonical `HarnessDisplayState` snapshot changed. Read it from [`session.displayState.get()`](https://mastra.ai/reference/harness/session). |
|
|
788
|
+
|
|
789
|
+
The harness also emits low-level streaming content chunks — `text`, `thinking`, `tool_call`, `tool_result`, `image`, and `file`. These are the raw pieces that get assembled into messages; most UIs render from `message_update` (or read the [`session.displayState`](https://mastra.ai/reference/harness/session) snapshot) rather than subscribing to them directly.
|
|
859
790
|
|
|
860
791
|
## Built-in tools
|
|
861
792
|
|
|
@@ -889,4 +820,11 @@ harness.subscribe(event => {
|
|
|
889
820
|
}
|
|
890
821
|
}
|
|
891
822
|
})
|
|
892
|
-
```
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
## Related
|
|
826
|
+
|
|
827
|
+
- [Session class](https://mastra.ai/reference/harness/session)
|
|
828
|
+
- [Harness overview](https://mastra.ai/docs/harness/overview)
|
|
829
|
+
- [Threads and state](https://mastra.ai/docs/harness/threads-and-state)
|
|
830
|
+
- [Tool approvals](https://mastra.ai/docs/harness/tool-approvals)
|