> ## 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.

# The Harness

```text {13,14,15} theme={null}
support-agent/
├── agent.json
├── instructions.md
└── .telnyx/                        written by deploy
    ├── index.ts
    ├── telnyx.toml
    ├── telnyx-env.d.ts
    └── package.json

@telnyx/acp/                      the package
├── managed/
│   ├── index.ts
│   ├── agent.ts                                       ◀
│   ├── model.ts                                       ◀
│   ├── memory.ts                                      ◀
│   ├── tools.ts
│   ├── mcp.ts
│   ├── voice.ts
│   ├── direct-turn.ts
│   └── filler.ts
├── channels/
│   ├── index.ts
│   ├── phone.ts
│   ├── email.ts
│   ├── web.ts
│   └── wire.ts
├── synth/
│   ├── index.ts
│   ├── code-entry.ts
│   └── vendor.ts
└── spec/
    ├── schema.ts
    ├── model.ts
    ├── phone.ts
    ├── project.ts
    └── io.ts
```

The harness is the apparatus the configuration is plugged into, the model, the tools, the instructions and the step limit, and every run of the turn taking is pulled through it. It is the harness SDK's [`createHarness()`](/docs/agent-sdk/harness-sdk/create-harness), built once per activation by `harness(card)` in `managed/agent.ts`:

```ts theme={null}
// managed/agent.ts
harness(card) {
  return (this.harnessInstance ??= this.buildHarness(card, this.spec.approvals ?? {}));
}

buildHarness(card, approvals) {
  const { tools, activeTools, authorization } = applyApprovals({ ...specTools(this.spec, this.toolEnv()), ...(this.host.tools ?? {}) }, approvals, this.host.approvalTtlMs);
  const model = this.host.model ?? chatLanguageModel((this.host.completions ?? defaultCompletions)(this.env, this.spec), this.spec.model);
  return createHarness({
    ports: this.buildPorts(),
    model: observeModelErrors(model, (message) => { this.modelError = message; }),
    tools, ...(activeTools ? { activeTools } : {}), ...(authorization ? { authorization } : {}),
    instructions: this.spec.memory ? withCard(this.spec.instructions, card) : this.spec.instructions,
    limits: { steps: this.spec.limits?.steps ?? DEFAULT_STEPS },
  });
}
```

`callHarness(card)` builds a second harness for calls when a tool is marked `ask`: a turn on a call is not a durable run, and nobody can approve while the caller waits, so every `ask` becomes `deny` for that harness and the agent says so once in the logs, `{"voiceTools":"left out on calls","tools":[…]}`. Without any `ask`, calls use the very same instance.

The class fills seven fields of the spec.

* [`ports`](/docs/agent-sdk/harness-sdk/create-harness#spec-ports) is `buildPorts()`, the instance's own runtime ports.
* [`model`](/docs/agent-sdk/harness-sdk/create-harness#spec-model) is the host's `model`, or else `chatLanguageModel` for `spec.model`. `observeModelErrors` wraps it so that a failed run can record the model's own error message.
* [`tools`](/docs/agent-sdk/harness-sdk/create-harness#spec-tools) is the tool set as `applyApprovals` leaves it: the tools `specTools` builds from `agent.json` ([Tools, Approvals and Limits](/docs/agent-sdk/inside/configuration/tools)), with the host's merged on top.
* [`activeTools`](/docs/agent-sdk/harness-sdk/create-harness#spec-activetools) is passed only when a tool is denied.
* [`authorization`](/docs/agent-sdk/harness-sdk/create-harness#spec-authorization) is passed only when a tool is marked `ask`.
* [`instructions`](/docs/agent-sdk/harness-sdk/create-harness#spec-instructions) is `spec.instructions`, with the memory summary appended when memory is on.
* [`limits`](/docs/agent-sdk/harness-sdk/create-harness#spec-limits) is `{ steps }`, from `spec.limits.steps` or `DEFAULT_STEPS`.

The host, the second argument of `defineManagedAgent(spec, host)`, supplies the tools and can replace the model. [Extending the Managed Agent](/docs/agent-sdk/extending#the-host) lists its fields.

The ports are built over the instance itself:

```ts theme={null}
// managed/agent.ts
constructor(ctx, env, spec, host = {}) {
  super(ctx, env);
  this.spec = spec;
  this.host = host;
  if (host.authorization) setAgentHarnessAuthorizationProvider(this, host.authorization);
}
// …
buildPorts() {
  return createRuntimeAgentHarnessPorts(this, {
    ...(this.host.environmentKeys ? { environmentKeys: this.host.environmentKeys } : {}),
  });
}
```

`super(ctx, env)` is the runtime SDK's [`constructor()`](/docs/agent-sdk/interface/constructor), and `createRuntimeAgentHarnessPorts(this, { environmentKeys })` is [`createRuntimeAgentHarnessPorts()`](/docs/agent-sdk/harness-sdk/adapters/runtime).

* `this` is the instance. The harness reaches its storage and scheduler only through the [ports](/docs/agent-sdk/harness-sdk/ports).
* [`environmentKeys`](/docs/agent-sdk/harness-sdk/adapters/runtime#runtimeagentharnessportsoptions) is the host's. It names the bindings that cross into the ports, and none cross without it.
* `setAgentHarnessAuthorizationProvider`, from `@telnyx/edge-runtime/internal`, registers the host's `authorization` as the instance's [authorization port](/docs/agent-sdk/harness-sdk/ports/authorization#on-the-agent-runtime). Approvals fail closed without it.

The managed classes attach none of the harness SDK's plug-ins. A class of the user's own can add each of them.

* [`createHarnessWithScheduling()`](/docs/agent-sdk/harness-sdk/plug-ins/scheduling) adds tools with which the model schedules its own follow-ups.
* [`createVirtualWorkspace()`](/docs/agent-sdk/harness-sdk/plug-ins/workspace) adds a per-instance text store with skills, which the model reads and writes through tools.
* [`HarnessMiddleware`](/docs/agent-sdk/harness-sdk/plug-ins/middleware) wraps a turn, a model step or a tool call.
