Skip to content
4 min read · 871 words

Class: TurnRunner

Executes a single agent turn through paired input and output middleware pipelines.

Remarks

Construction validates config eagerly and throws @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG if it does not satisfy the schema — fail-fast so misconfiguration surfaces before any turn runs.

Each call to TurnRunner.run threads a @nhtio/adk!TurnContext through the input pipeline, invokes the model, then threads the result through the output pipeline. Middleware on each side can read and mutate the context for pre- and post-processing (e.g. message normalisation, tool call dispatch, response filtering).

Two event buses:

  • Functional bus (on / off / once): message, thought, toolCall — pipeline-affecting events that middleware raises throughout turn execution.
  • Observability bus (observe / unobserve / observeOnce): turnStart, turnEnd, turnGateOpen, turnGateClosed, error — instrumentation-only events that monitor execution without participating in it.

Streaming content is surfaced via message and thought events; tool call lifecycle via toolCall; non-fatal pipeline errors via the observability error event; gate lifecycle via turnGateOpen and turnGateClosed — all throughout execution.

Example

ts
const runner = new TurnRunner({
  fetchMemoriesCallback: async (ctx) => memoryStore.query(ctx),
  fetchMessagesCallback: async (ctx) => messageStore.history(ctx),
  fetchThoughtsCallback: async (ctx) => thoughtStore.history(ctx),
  fetchToolCallsCallback: async (ctx) => toolCallStore.history(ctx),
});
// Functional bus — pipeline events
runner.on("message", (chunk) => process.stdout.write(chunk.aDelta));
// Observability bus — instrumentation
runner.observe("error", (err) => console.error(err.toString()));
runner.observe("turnStart", ({ turnId }) =>
  console.log("turn started", turnId),
);
runner.observe("turnGateOpen", (gate) => {
  if (gate.reason === "tool_approval") {
    gate.resolve(true); // approve immediately for this example
  }
});
await runner.run({
  turnAbortController: new AbortController(),
  systemPrompt: "You are a helpful assistant.",
  standingInstructions: [],
});

Constructors

Constructor

ts
new TurnRunner(config: TurnRunnerConfig): TurnRunner;

Parameters

ParameterTypeDescription
configTurnRunnerConfigConstruction-time configuration validated against turnRunnerConfigSchema.

Returns

TurnRunner

Throws

@nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG when config does not satisfy the schema.

Methods

observe()

ts
observe<K>(event: TurnObservabilityEvent<K>, listener: TurnObservabilityEventListener<K>): this;

Registers a persistent observability listener for event.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnObservabilityEvent<K>The event to observe.
listenerTurnObservabilityEventListener<K>The function to call on each emission.

Returns

this

this for chaining.

Remarks

Use the observability bus (observe / unobserve / observeOnce) for instrumentation: turn lifecycle, gate lifecycle, and non-fatal errors. Use the functional bus (on / off / once) for pipeline-affecting events: message, thought, toolCall.


observeOnce()

ts
observeOnce<K>(event: TurnObservabilityEvent<K>, listener: TurnObservabilityEventListener<K>): this;

Registers a one-time observability listener for event that is automatically removed after the first emission.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnObservabilityEvent<K>The event to observe once.
listenerTurnObservabilityEventListener<K>The function to call on the next emission.

Returns

this

this for chaining.


off()

ts
off<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this;

Removes a previously registered functional listener for event.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnEvent<K>The event to stop listening to.
listenerTurnEventListener<K>The listener function to remove.

Returns

this

this for chaining.


on()

ts
on<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this;

Registers a persistent functional listener for event.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnEvent<K>The event to listen to.
listenerTurnEventListener<K>The function to call on each emission.

Returns

this

this for chaining.


once()

ts
once<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this;

Registers a one-time functional listener for event that is automatically removed after the first emission.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnEvent<K>The event to listen to.
listenerTurnEventListener<K>The function to call on the next emission.

Returns

this

this for chaining.


run()

ts
run(context: RawTurnContext): Promise<void>;

Executes a single agent turn against the provided raw context.

Parameters

ParameterTypeDescription
contextRawTurnContextRaw input validated and wrapped into a @nhtio/adk!TurnContext before execution.

Returns

Promise<void>

Remarks

Returns Promise<void> intentionally — all meaningful output surfaces via events, not return values. Register listeners before calling run: observability events (turnStart, turnEnd) bracket execution; functional events (message, thought, toolCall) fire throughout; observability error carries non-fatal pipeline failures; turnGateOpen and turnGateClosed fire when middleware suspends via ctx.waitFor(). Awaiting this method only tells you the pipeline has finished, not what it produced.

Constructs a validated @nhtio/adk!TurnContext from context (throwing @nhtio/adk!E_INVALID_TURN_CONTEXT on failure), then runs the input middleware pipeline. Abort signals are silently swallowed.

Throws

@nhtio/adk!E_INVALID_TURN_CONTEXT when context does not satisfy the schema.


unobserve()

ts
unobserve<K>(event: TurnObservabilityEvent<K>, listener: TurnObservabilityEventListener<K>): this;

Removes a previously registered observability listener for event.

Type Parameters

Type Parameter
K

Parameters

ParameterTypeDescription
eventTurnObservabilityEvent<K>The event to stop observing.
listenerTurnObservabilityEventListener<K>The listener function to remove.

Returns

this

this for chaining.


isTurnRunner()

ts
static isTurnRunner(value: unknown): value is TurnRunner;

Returns true if value is a TurnRunner instance.

Parameters

ParameterTypeDescription
valueunknownThe value to test.

Returns

value is TurnRunner

true when value is a TurnRunner instance.

Remarks

Uses @nhtio/adk!isInstanceOf for cross-realm safety.