# TurnOrchestrator (formerly OrchestratorClient) (/docs/orchestrator-client)



`TurnOrchestrator` is the per-turn handle. When a turn begins,
the runtime constructs one, threads it through the stream body
and the substrate's tool / model / prompt resolution paths, and
disposes of it at turn end. The instance holds **what's in
scope for this one turn** — the channel config, the message
history, the per-turn context, the tools available, the resolved
model, the composed prompt.

It's the turn-scoped sibling of [`SessionRuntime`](/docs/session-runtime)
inside the [runtime-lifecycle cluster](/docs/session-lifecycle#the-runtime-lifecycle-cluster) —
`SessionRuntime` hosts the session arc; `TurnOrchestrator` hosts
each turn arc inside it.

Both `TurnOrchestrator` and the deprecated `OrchestratorClient`
alias export from the `@pleach/core/runtime` barrel — the canonical
import path for hosts that need a typed handle on the per-turn
surface — for example, when a custom strategy needs to read
`client.history` or query `client.model`.

```typescript
import type { TurnOrchestrator } from "@pleach/core/runtime";
```

### Renamed from `OrchestratorClient` [#renamed-from-orchestratorclient]

The class was renamed under D-PO-3. `OrchestratorClient` survives
as a deprecated re-export from the same `@pleach/core/runtime`
barrel so existing consumers keep compiling:

```typescript
// Still resolves; deprecated; removed at @pleach/core@2.0.0.
import { OrchestratorClient } from "@pleach/core/runtime";
```

Throughout the rest of this page we use `client` as the variable
name to match the field naming on `SessionRuntime` and the
plugin-hook context objects.

<SourceMeta subpath="@pleach/core/runtime" source="{ label: &#x22;src/runtime/turnOrchestrator.ts&#x22;, href: &#x22;https://github.com/pleachhq/core/tree/main/src/runtime/turnOrchestrator.ts&#x22; }" />

## The six facets [#the-six-facets]

`TurnOrchestrator` exposes a facet surface that mirrors the
`SessionRuntime` facet pattern — domain-grouped accessors instead
of a flat method dump. Six user-facing facets plus one
`_internal` namespace that consumer code must not touch.

| Facet                    | What it carries                                             |
| ------------------------ | ----------------------------------------------------------- |
| `client.config`          | Turn config — channel, model overrides, abort signal        |
| `client.history`         | The message history this turn sees, post-trim               |
| `client.context`         | Per-turn context object — variables, scratchpad             |
| `client.tools`           | Tools registered and available this turn                    |
| `client.model`           | Resolved model + family info — provider, family, model id   |
| `client.prompts`         | Prompt building, graph-config snapshot, system-notice drain |
| `client._internal.graph` | INTERNAL graph-internal composite — substrate-only          |

`client.prompts` covers the surface that used to live as flat
methods on the class. Use `client.prompts.buildSystemPrompt(...)`
instead of the deprecated `client.buildSystemPrompt(...)`. Same
shape applies to `client.prompts.getGraphConfig()` and
`client.prompts.drainPendingSystemNotices()`. The flat methods
remain callable with `@deprecated` JSDoc and are removed at
`@pleach/core@2.0.0`.

`client._internal.graph` absorbs the seven `*Public` / `*ForGraph`
suffixed methods the graph nodes call into the client with
(`executeTool`, `applyPreModelTransforms`, `runPostModelHooks`,
`getAuthToken`, `getThinkingConfig`, `getFallbackConfig`,
`recordToolOutcomes`). The leading underscore is the contract —
the field is intentionally not part of the user-facing surface
and is callable only from graph nodes inside the substrate.

For the full facet inventory across `SessionRuntime` and
`TurnOrchestrator` see [Facets](/docs/facets). The
`audit:orchestrator-facet-coverage` CI gate asserts every new
public field on the client lands under a facet (or under
`_internal`). The migration is complete — today's baseline shows
zero remaining call sites on the deprecated flat methods, and the
gate runs strict (FAIL on any novel direct call). See
[Facets § CI gates](/docs/facets#ci-gates-enforcing-facet-coverage).

## `_internal.graph` — do not use [#_internalgraph--do-not-use]

`TurnOrchestrator._internal.graph` is a substrate-only composite
the graph nodes call into for bookkeeping (`executeTool`,
`applyPreModelTransforms`, `recordToolOutcomes`, and the
configuration accessors named above). The leading underscore is
the contract: the field can change shape without a deprecation
cycle. Consumer code that reads `_internal.graph` will break the
next time the snapshot shape changes.

The supported surface for graph inspection is `runtime.graph.*` on
the [`SessionRuntime`](/docs/session-runtime), not `_internal.graph`
on the client. See [Facets § What's internal — don't
use](/docs/facets#whats-internal--dont-use) for the broader rule.

## Where the client comes from [#where-the-client-comes-from]

Hosts don't construct `TurnOrchestrator` directly. The runtime
allocates one per turn during `executeMessage` and disposes it at
turn end. A host receives the client in three places:

* **Inside a custom strategy.** Strategy slots that the per-turn
  body consumes (see [Runtime
  strategies](/docs/runtime-strategies)) take the client as part
  of their typed input bundle when the substrate needs to thread
  per-turn state through to the host.
* **Inside a plugin hook.** Plugin hooks that participate in the
  turn — `contributePrompts`, `contributeRuntimeAwarePrompts`,
  `contributeStreamObservers` — receive a context object that
  carries the relevant client facets.
* **Inside a custom turn body.** Hosts that swap the
  `streamSingleTurn` body for their own strategy receive the
  client as an explicit argument. See [streamSingleTurn](/docs/stream-single-turn)
  for the body contract.

The client itself is not part of the language-agnostic wire
contract — it's a TypeScript-side convenience. A Go implementation
threads equivalent per-turn state through its own types. See
[Language-agnostic contract § What's NOT in the
contract](/docs/language-agnostic-contract#whats-not-in-the-contract)
for the broader split.

## Coexistence with the graph path [#coexistence-with-the-graph-path]

The substrate runs two execution paths side by side: the
imperative `TurnOrchestrator` path (the per-turn body calls
seams directly through the client) and the declarative graph path
(the [`CompiledGraph`](/docs/graph) walks the four-stage lattice).
Both write to the same `AuditableCall` ledger; both honor the same
family-lock and singleton synthesize seam. The choice is a host
decision, not a runtime fork.

A host picks the imperative path when its turn shape is
linear — one tool loop, one synthesis, no plan stage. The
declarative path is the choice when the turn needs the lattice's
enrichment slots (intent detection, plan generation, quality
scoring) or when multiple plugins contribute graph nodes.

## Minimal consumer usage [#minimal-consumer-usage]

Most hosts never reach for `TurnOrchestrator` directly. The
runtime threads it through the substrate; plugin hooks and
strategies receive what they need via typed input bundles. A host
that **does** need the typed handle — typically for a custom
strategy implementing a `streamSingleTurn` swap — imports the
class for the type, not the constructor:

```typescript
import type { TurnOrchestrator } from "@pleach/core/runtime";

function myCustomTurnBody(client: TurnOrchestrator) {
  const config = client.config.get();                 // OrchestratorConfig snapshot
  const selection = client.model.getLastSelection();  // ModelSelection | null
  const tools = client.tools.getResolved();           // ToolDefinition[]
  // ... drive the seams using the typed facets
}
```

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="Facets" href="/docs/facets" description="The full facet inventory and the three audit gates that enforce coverage." />

  <Card title="streamSingleTurn" href="/docs/stream-single-turn" description="The canonical per-turn body the substrate threads the client through." />

  <Card title="SessionRuntime" href="/docs/session-runtime" description="The runtime that owns TurnOrchestrator — construction, sessions, executeMessage." />

  <Card title="Graph" href="/docs/graph" description="The declarative path that runs side-by-side with the imperative client." />
</Cards>
