> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# createHarness()

> **createHarness**\<`State`, `TOOLS`, `OutputDefinition`>(`spec`): [`Harness`](/docs/agent-sdk/harness-sdk)\<`TOOLS`, `OutputDefinition`>

Construct a portable AI SDK v6 turn engine over an explicit ports object.

**Type Parameters**

| Type Parameter | Default type |
| - | - |
|  `State` *extends* `Record`\<`string`, `unknown`> | - |
|  `TOOLS` *extends* `ToolSet` | - |
|  `OutputDefinition` *extends* `HarnessOutputDefinition` \| `undefined` | `undefined` |

**Parameters**

| Parameter | Type |
| - | - |
| `spec` | [`HarnessSpec`](/docs/agent-sdk/harness-sdk/create-harness)\<`State`, `TOOLS`, `OutputDefinition`> |

**Returns**
[`Harness`](/docs/agent-sdk/harness-sdk)\<`TOOLS`, `OutputDefinition`>

## spec.ports

> **ports**: [`AgentHarnessPorts`](/docs/agent-sdk/harness-sdk/ports)\<`State`>

Persistent host capabilities supplied through the versioned ports seam.

## spec.model

> **model**: `LanguageModel`

AI SDK v6 language model.

Any AI SDK provider supplies the model. A failed call to it surfaces as [`HarnessProviderError`](/docs/agent-sdk/harness-sdk/harness/turn#harnessprovidererror), which keeps only allow-listed diagnostics.

## spec.models

> `optional` **models?**: `Readonly`\<`Record`\<`string`, `LanguageModel`>>

Host-owned models selectable only by a named runtime configuration key.

The [runtime configuration](#runtime-configuration) selects one for a turn by its name.

## spec.tools

> **tools**: `TOOLS`

The one explicit, flat tool set available to the model.

Tools are keyed by the name the model calls them by. `createHarness()` throws a `TypeError` unless every tool follows these rules.

* It has an `execute` function.
* It is an ordinary function tool, not a dynamic or provider tool.
* It sets `needsApproval` only to `true`.
* It uses no deferred results, no `toModelOutput` and none of the `onInput` hooks.

A tool with `needsApproval: true` runs only inside a persistent run. A direct `turn()` whose tools include one throws `TypeError("Harness approval-capable tools require durable runs")`. Such a turn starts with [`accept()`](/docs/agent-sdk/harness-sdk/harness/accept) and executes through [`run()`](/docs/agent-sdk/harness-sdk/harness/run).

## spec.activeTools

> `optional` **activeTools?**: readonly (keyof `TOOLS` & `string`)\[]

Optional static subset of the injected tool set.

When it is given, only the named tools are offered to the model. The [runtime configuration](#runtime-configuration) can offer another subset for a turn.

## spec.authorization

> `optional` **authorization?**: `Readonly`\<\{ `approvalTtlMs?`: `number`; `toolActions`: `Readonly`\<`Record`\<`string`, `string`>>; }>

Private, host-owned Service Account action mapping. Every supplied tool is
mapped exactly once when approval-capable tools are present; model/tool
metadata is never treated as an authorization authority.

Both fields are required when any tool sets `needsApproval`. `createHarness()` throws a `TypeError` when a rule is broken.

* `toolActions` maps each tool in `spec.tools` to an action, and names no other tool.
* An action is one or more runs of letters and digits joined by dots, such as `orders.refund`, at most 512 bytes.
* `approvalTtlMs` is an approval's lifetime from its request, in milliseconds, from 300000 (5 minutes) to 86400000 (24 hours).

[`approvals`](/docs/agent-sdk/harness-sdk/harness/approvals) decides each held call.

## spec.instructions

> `optional` **instructions?**: `string`

Optional static instructions supplied to every model step.

The text is the model's system prompt on every step. The harness assembles nothing into it, so its content is decided before the harness is built.

* [`assemblePrompt()`](/docs/agent-sdk/harness-sdk/plug-ins/workspace#assembleprompt) on a virtual workspace builds it from a base text and skills.
* The [runtime configuration](#runtime-configuration) replaces it for a turn.

## spec.output

> `optional` **output?**: `OutputDefinition`

Optional host-owned structured output mode.

`output` is an AI SDK output definition, such as `Output.object({ schema })` from `ai`. The model's final text is parsed through it, and the parsed value is returned as `output` in the [`HarnessTurnResult`](/docs/agent-sdk/harness-sdk/harness/turn#harnessturnresult), beside `text`. A harness with `output` or `outputs` cannot take [`begin()`](/docs/agent-sdk/harness-sdk/harness/begin) turns.

## spec.outputs

> `optional` **outputs?**: `Readonly`\<`Record`\<`string`, `OutputDefinition`>>

Host-owned output modes selectable only by a named runtime configuration key.

The [runtime configuration](#runtime-configuration) selects one for a turn by its name.

## spec.limits

> **limits**: `Readonly`\<\{ `steps`: `number`; }>

Required bounded turn limits.

`steps` is a positive integer that bounds the model steps of one turn. When the last allowed step still calls a tool, the turn throws [`HarnessStepLimitError`](/docs/agent-sdk/harness-sdk/harness/turn#harnesssteplimiterror). The [runtime configuration](#runtime-configuration) can replace it for a turn.

## spec.middleware

> `optional` **middleware?**: `Readonly`\<\{ `modelStep?`: readonly [`HarnessMiddleware`](/docs/agent-sdk/harness-sdk/plug-ins/middleware)\<[`HarnessModelStepMiddlewareContext`](/docs/agent-sdk/harness-sdk/plug-ins/middleware#harnessmodelstepmiddlewarecontext)\<`TOOLS`>, `unknown`>\[]; `tool?`: readonly [`HarnessMiddleware`](/docs/agent-sdk/harness-sdk/plug-ins/middleware)\<[`HarnessToolMiddlewareContext`](/docs/agent-sdk/harness-sdk/plug-ins/middleware#harnesstoolmiddlewarecontext)\<`TOOLS`>, `unknown`>\[]; `turn?`: readonly [`HarnessMiddleware`](/docs/agent-sdk/harness-sdk/plug-ins/middleware)\<[`HarnessTurnMiddlewareContext`](/docs/agent-sdk/harness-sdk/plug-ins/middleware#harnessturnmiddlewarecontext)\<`TOOLS`, `OutputDefinition`>, [`HarnessTurnResult`](/docs/agent-sdk/harness-sdk/harness/turn#harnessturnresult)\<`HarnessParsedOutput`\<`OutputDefinition`>>>\[]; }>

Ordered transport-neutral lifecycle middleware.

Each list wraps one phase of a turn.

* `turn` wraps the whole turn.
* `modelStep` wraps each call to the model, and requires a model object rather than a model id.
* `tool` wraps each tool execution.

A list runs in its given order, its first entry outermost. An entry that does not call `next` ends the phase with its own result. A harness with `turn` or `modelStep` middleware cannot take [`begin()`](/docs/agent-sdk/harness-sdk/harness/begin) turns. [`HarnessMiddleware`](/docs/agent-sdk/harness-sdk/plug-ins/middleware) documents the contexts.

## spec.durability

> `optional` **durability?**: `Readonly`\<\{ `checkpoints?`: [`HarnessCheckpoint`](/docs/agent-sdk/harness-sdk/persistence/run-ledger#harnesscheckpoint); `onToolCallStart?`: `GenerateTextOnToolCallStartCallback`\<`TOOLS`>; `toolStepVersion?`: `string`; }>

Deterministic persistence seams used by recovery tests.

A host leaves `durability` unset. Its seams exist for recovery tests.

## Runtime Configuration

At the start of every turn, the harness reads one key of the instance's state, `__telnyx_agent_harness`, and resolves the turn's configuration from it.

```ts theme={null}
await this.setState({
  __telnyx_agent_harness: { config: { model: "fast", limits: { steps: 4 } } },
});
```

* `model` and `output` name an entry of `spec.models` and `spec.outputs`.
* `instructions` replaces the static text.
* `tools` names the tools offered, from `spec.tools`, in place of `spec.activeTools`.
* `limits.steps` replaces the step limit.

Every field is optional, and a field left out or `null` takes its static value. A value that does not validate, such as one with an unknown name or key, is ignored whole, and the last valid configuration stands.
