> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/dispatcher-and-batch-policies.md).

# Dispatcher and batch policies

`ConvaiActionDispatcher` is the runtime execution layer of the action system. It listens for command batches from Convai, resolves each action and target against the current session's configuration, and calls the bound executor components one step at a time. Two policies control what happens when new batches arrive during execution and when a step fails. A speech gate can also hold the first action of a fresh batch until the character starts speaking, and the dispatcher can optionally cancel its own work when the player starts talking.

### Component overview

| Attribute       | Value                                                            |
| --------------- | ---------------------------------------------------------------- |
| **Menu path**   | `Add Component → Convai → Convai Action Runner`                  |
| **Namespace**   | `Convai.Runtime.Actions`                                         |
| **Constraints** | `DisallowMultipleComponent`, `RequireComponent(ConvaiCharacter)` |

The dispatcher must be on the same `GameObject` as `ConvaiCharacter`. Only one dispatcher is allowed per character. In the Actions Editor's **Character Settings** tab, this component is labeled **Convai Action Runner** under **Actions Are Run By** — see [Configure character actions](/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/configuring-actions.md#where-action-behaviors-live).

### Inspector fields

| Field                         | Type                               | Default     | Description                                                                                                                                                                                                                                                                           |
| ----------------------------- | ---------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `_batchPolicy`                | `ConvaiActionBatchPolicy`          | `Queue`     | How incoming batches behave while another batch is executing                                                                                                                                                                                                                          |
| `_failurePolicy`              | `ConvaiActionBatchFailurePolicy`   | `StopBatch` | Whether a step failure aborts the remaining batch or allows it to continue                                                                                                                                                                                                            |
| `_speechGateTimeoutSeconds`   | `float`                            | `2`         | Maximum seconds the first action of a fresh batch waits for character speech before running anyway                                                                                                                                                                                    |
| `_defaultStepTimeoutSeconds`  | `float`                            | `60`        | Longest any action may run before it is reported as timed out, used only when the action's own `TimeoutSeconds` is `0`. An action's own timeout, when authored, always wins. `0` removes this safety net entirely.                                                                    |
| `_cancelOnUserSpeech`         | `bool`                             | `false`     | When enabled, the player starting to speak cancels the in-flight batch and clears the queue — the same effect as `ReplaceCurrent`, triggered by the player instead of a new backend batch                                                                                             |
| `_enablePerformanceReactions` | `bool`                             | `true`      | When enabled, notifies any `IActionPerformanceReactor` peers registered on the character's embodiment context (Gaze look-where-you-act, Body Language acknowledgment nod, Emotion outcome mood beat) of batch and step lifecycle. No-op with no embodiment context or reactor present |
| `_onBatchStarted`             | `UnityEvent`                       | —           | Fires when a batch begins executing                                                                                                                                                                                                                                                   |
| `_onStepStarted`              | `ConvaiActionInvocationUnityEvent` | —           | Fires at the start of each step                                                                                                                                                                                                                                                       |
| `_onStepSucceeded`            | `ConvaiActionInvocationUnityEvent` | —           | Fires when a step executor returns `Succeeded`                                                                                                                                                                                                                                        |
| `_onStepFailed`               | `ConvaiActionInvocationUnityEvent` | —           | Fires when a step fails for any reason                                                                                                                                                                                                                                                |
| `_onStepUnhandled`            | `ConvaiActionInvocationUnityEvent` | —           | Fires when an executor returns `Unhandled`                                                                                                                                                                                                                                            |
| `_onStepCompleted`            | `ConvaiActionStepReportUnityEvent` | —           | Fires once per step after the outcome event above, regardless of the result                                                                                                                                                                                                           |
| `_onBatchCompleted`           | `UnityEvent`                       | —           | Fires when all steps finish without being aborted                                                                                                                                                                                                                                     |
| `_onBatchAborted`             | `UnityEvent`                       | —           | Fires when the batch is cut short by the failure policy                                                                                                                                                                                                                               |

`ConvaiActionInvocationUnityEvent` is a serializable `UnityEvent<ConvaiActionInvocation>`. Wire it in the Inspector exactly like a standard `UnityEvent` — the event parameter carries the full invocation context (action name, target, character, batch and step index). `ConvaiActionStepReportUnityEvent` is a serializable `UnityEvent<ConvaiActionStepReport>`, exposed via the public `OnStepCompleted` property — use it when you want a single subscription point for every step outcome instead of wiring `OnStepSucceeded`/`OnStepFailed`/`OnStepUnhandled` separately.

### Read-only runtime state

| Property            | Type                             | Description                                                              |
| ------------------- | -------------------------------- | ------------------------------------------------------------------------ |
| `IsBusy`            | `bool`                           | Whether the dispatcher is running a batch right now.                     |
| `PendingBatchCount` | `int`                            | How many received batches are waiting behind the one currently running.  |
| `CurrentActionName` | `string`                         | Display name of the action running right now, or empty between steps.    |
| `BatchPolicy`       | `ConvaiActionBatchPolicy`        | The authored batch policy (read-only from code; set in the Inspector).   |
| `FailurePolicy`     | `ConvaiActionBatchFailurePolicy` | The authored failure policy (read-only from code; set in the Inspector). |

`CancelOnUserSpeech` and `EnablePerformanceReactions` are read/write C# mirrors of the two Inspector fields above — see [Barge-in: cancel on user speech](#barge-in-cancel-on-user-speech).

### Batch policy

Batch policy controls what happens when Convai returns a new action batch while the dispatcher is still executing a previous one.

| Policy           | Enum value | Behavior                                                                                                                                                                                |
| ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Queue`          | `0`        | New batches wait in a queue. The current batch finishes before the next starts. Suitable for most scenarios.                                                                            |
| `ReplaceCurrent` | `1`        | Cancels the currently executing step and clears any queued batches. The new batch starts immediately. Use for interrupt-driven scenarios (e.g., "Stop, come here instead").             |
| `DropIncoming`   | `2`        | Discards new batches until the current batch and all queued batches finish. Use when an in-progress sequence must not be interrupted (e.g., a safety demonstration that must complete). |

{% hint style="info" %}
`ReplaceCurrent` cancels the **currently running executor step** via the `CancellationToken` and clears all pending batches before starting the new one. Executors must respect the cancellation token for this to be instant — see [Write a custom action executor](/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/writing-custom-executors.md).
{% endhint %}

### Failure policy

Failure policy controls what happens when an executor returns a non-success result (`Failed`, `Unhandled`, `Canceled`, or `TimedOut`).

| Policy          | Enum value | Behavior                                                                                           |
| --------------- | ---------- | -------------------------------------------------------------------------------------------------- |
| `StopBatch`     | `0`        | Remaining steps in the batch are skipped. `OnBatchAborted` fires.                                  |
| `ContinueBatch` | `1`        | Execution continues with the next step regardless of failure. `OnBatchCompleted` fires at the end. |

Use `ContinueBatch` when actions are independent — a failed "Point At" should not prevent a following "Nod Or Shake Head." Use `StopBatch` (the default) for dependent sequences — a failed "Walk To" should prevent a following action that expects the character to have arrived.

### Gate the first action on character speech

Convai can mark an action so the dispatcher delays it until the character starts speaking. Two fields control this — one set by Convai on the command, one that can be authored locally on the matching action definition.

| Field                        | Location                 | Type    | Default | Description                                                                                                                             |
| ---------------------------- | ------------------------ | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `WaitForBotSpeech`           | `ConvaiActionCommand`    | `bool`  | `false` | Set by Convai on the backend command. `true` gates the first step of the batch.                                                         |
| `DelayAfterBotSpeechSeconds` | `ConvaiActionCommand`    | `float` | `0`     | Extra delay applied after the gate releases. Used only when the command's `WaitForBotSpeech` is `true`.                                 |
| `WaitForBotSpeech`           | `ConvaiActionDefinition` | `bool`  | `false` | Local override authored on the action definition. Also gates the first step when `true`.                                                |
| `DelayAfterBotSpeechSeconds` | `ConvaiActionDefinition` | `float` | `0`     | Extra delay applied after the gate releases. Used only when the command's `WaitForBotSpeech` is `false` and the definition's is `true`. |

The dispatcher checks these fields only on the first step of a batch (`stepIndex == 0`); later steps in the same batch never wait. Gating triggers when either the command's `WaitForBotSpeech` or the matched definition's `WaitForBotSpeech` is `true`. When neither is `true`, the step runs immediately with no gating.

### Speech gate timeout

`_speechGateTimeoutSeconds` caps how long a gated first step waits, in seconds. The default is `2`. This field has no public C# property — set it in the Inspector.

While the gate is open, the dispatcher listens for `ConvaiCharacter.OnSpeechStarted`, `ConvaiCharacter.OnSpeechStopped`, and `ConvaiCharacter.OnTurnCompleted`. The gate releases on whichever of these fires first, or once `_speechGateTimeoutSeconds` elapses, whichever comes first. `OnStepStarted` fires only after the gate releases.

### Barge-in: cancel on user speech

Enable `_cancelOnUserSpeech` to have the dispatcher cancel its in-flight batch and clear the queue the moment the player starts talking — the same effect as the `ReplaceCurrent` batch policy, but triggered by the player rather than by a new backend batch. Off by default, so existing scenes are unchanged until you opt in.

The signal comes from the character's embodiment context event hub (the same server-VAD-based `PlayerSpeakingStateChanged` signal Gaze and Body Language already react to). With no embodiment module added to the character, barge-in stays inert rather than throwing — the dispatcher only degrades, it does not fail.

```csharp
using Convai.Runtime.Actions;
using UnityEngine;

public sealed class BargeInFeedback : MonoBehaviour
{
    [SerializeField] private ConvaiActionDispatcher _dispatcher;

    private void OnEnable() => _dispatcher.OnCancelledByUserSpeech += HandleCancelled;
    private void OnDisable() => _dispatcher.OnCancelledByUserSpeech -= HandleCancelled;

    private void HandleCancelled(string interruptedAction) =>
        Debug.Log($"Barge-in cancelled: {interruptedAction}");
}
```

`OnCancelledByUserSpeech` carries the display name of the action that was interrupted, or an empty string when nothing had started executing yet.

### Lifecycle events

The dispatcher fires events at every meaningful stage of batch and step execution. Subscribe in the Inspector via UnityEvent fields, or subscribe in C# via the properties.

#### Event firing order

```
OnBatchStarted
  → OnStepStarted       (for each step)
  → OnStepSucceeded     (if executor returned Succeeded)
     or
  → OnStepFailed        (if executor returned Failed, Canceled, or TimedOut)
     or
  → OnStepUnhandled     (if executor returned Unhandled)
  → OnStepCompleted     (always fires after the outcome event above, every step)
OnBatchCompleted  (all steps finished, or ContinueBatch allowed failures through)
  or
OnBatchAborted    (StopBatch policy cut the batch short after a failure)
```

#### Subscribing in C\#

```csharp
using Convai.Runtime.Actions;
using UnityEngine;

public sealed class ActionFeedback : MonoBehaviour
{
    [SerializeField] private ConvaiActionDispatcher _dispatcher;

    private void OnEnable()
    {
        _dispatcher.OnBatchStarted.AddListener(HandleBatchStarted);
        _dispatcher.OnStepSucceeded.AddListener(HandleStepSucceeded);
        _dispatcher.OnStepFailed.AddListener(HandleStepFailed);
        _dispatcher.OnBatchCompleted.AddListener(HandleBatchCompleted);
        _dispatcher.OnBatchAborted.AddListener(HandleBatchAborted);
    }

    private void OnDisable()
    {
        _dispatcher.OnBatchStarted.RemoveListener(HandleBatchStarted);
        _dispatcher.OnStepSucceeded.RemoveListener(HandleStepSucceeded);
        _dispatcher.OnStepFailed.RemoveListener(HandleStepFailed);
        _dispatcher.OnBatchCompleted.RemoveListener(HandleBatchCompleted);
        _dispatcher.OnBatchAborted.RemoveListener(HandleBatchAborted);
    }

    private void HandleBatchStarted() => Debug.Log("Batch started");
    private void HandleStepSucceeded(ConvaiActionInvocation inv) =>
        Debug.Log($"Step succeeded: {inv.Command.Name}");
    private void HandleStepFailed(ConvaiActionInvocation inv) =>
        Debug.LogWarning($"Step failed: {inv.Command.Name}");
    private void HandleBatchCompleted() => Debug.Log("Batch completed");
    private void HandleBatchAborted() => Debug.LogWarning("Batch aborted");
}
```

### Manual batch injection

`EnqueueActions(IReadOnlyList<ConvaiActionCommand> actions)` submits a batch to the dispatcher programmatically, respecting the active batch and failure policies. Use this for scripted demonstration sequences, automated test runs, or NPC behaviors triggered by game events rather than player speech.

```csharp
using System.Collections.Generic;
using Convai.Runtime.Actions;
using Convai.Shared.Types;
using UnityEngine;

public sealed class DemoTrigger : MonoBehaviour
{
    [SerializeField] private ConvaiActionDispatcher _dispatcher;

    public void RunSafetyDemo()
    {
        _dispatcher.EnqueueActions(new List<ConvaiActionCommand>
        {
            new ConvaiActionCommand("Walk To", "Extinguisher"),
            new ConvaiActionCommand("Point At", "Extinguisher"),
            new ConvaiActionCommand("Walk To", "Exit")
        });
    }
}
```

The dispatcher executes these steps sequentially. If the `BatchPolicy` is `Queue`, this batch waits behind any batch already in progress. A manually injected batch is now read the same way a backend batch is — wire-text cleaning, parameter parsing, and target resolution all run — so an unresolvable action name or target produces the same `Actions`-category console explanation as a real conversation, instead of only surfacing later as a step failure. This path does not refuse a command the way the backend path does; the step's own preconditions (definition, executor, target requirement) remain the gate.

### Bypassing the dispatcher

If you want to react to raw action commands without the dispatcher's target resolution and execution pipeline, subscribe to `ConvaiCharacter.OnActionsReceived` directly:

```csharp
using System.Collections.Generic;
using Convai.Runtime.Components;
using Convai.Shared.Types;
using UnityEngine;

public sealed class ManualActionHandler : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _character;

    private void OnEnable() =>
        _character.OnActionsReceived += HandleActions;

    private void OnDisable() =>
        _character.OnActionsReceived -= HandleActions;

    private void HandleActions(IReadOnlyList<ConvaiActionCommand> commands)
    {
        foreach (ConvaiActionCommand cmd in commands)
            Debug.Log($"Action: {cmd.Name}, Target: {cmd.Target}");
    }
}
```

{% hint style="warning" %}
Bypassing the dispatcher means no automatic target resolution, no batch/failure policies, and no lifecycle events. This is appropriate for read-only observation or custom dispatch pipelines, but not for typical gameplay where the SDK should drive the behavior.
{% endhint %}

### Dispatcher lifecycle behavior

| Situation                                                                                                 | Dispatcher behavior                                                                                                       |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Dispatcher disabled                                                                                       | Active work is canceled; queue is cleared                                                                                 |
| Dispatcher destroyed                                                                                      | Same as disabled                                                                                                          |
| Empty batch received                                                                                      | Silently ignored — no events fire                                                                                         |
| Action name not in local definitions                                                                      | Step fails: `OnStepFailed` fires; `StopBatch` aborts the batch                                                            |
| Executor field not assigned                                                                               | Step fails: `OnStepFailed` fires                                                                                          |
| Target requirement not met                                                                                | Step fails: `OnStepFailed` fires                                                                                          |
| Executor returns `Unhandled`                                                                              | `OnStepUnhandled` fires; treated as failure for `StopBatch` policy                                                        |
| Matched action definition has `Enabled = false`                                                           | Step is declined: `OnStepUnhandled` fires with a message naming the action as disabled, without running the executor      |
| First step of a batch has `WaitForBotSpeech` set (on the command or the definition)                       | `OnStepStarted` is delayed until character speech starts, stops, a turn completes, or `_speechGateTimeoutSeconds` elapses |
| Player starts speaking with `_cancelOnUserSpeech` enabled                                                 | In-flight step is canceled and the queue is cleared; `OnCancelledByUserSpeech` fires                                      |
| An action step exceeds its timeout (its own `TimeoutSeconds`, or `_defaultStepTimeoutSeconds` when unset) | Step result is `TimedOut`; `OnStepFailed` fires                                                                           |

### Usage examples

#### Example 1 — Training checklist integration

**Scenario:** A corporate onboarding simulation. A checklist UI advances when the NPC completes each equipment demonstration.

Wire `OnBatchCompleted` in the Inspector to `TrainingChecklistManager.AdvanceStep()`. Each time the NPC finishes a full sequence, the checklist advances automatically.

```csharp
// TrainingChecklistManager.cs
public void AdvanceStep()
{
    _currentStep++;
    UpdateChecklistUI();
}
```

No additional code is required on the dispatcher side — wire the `OnBatchCompleted` UnityEvent in the Inspector.

#### Example 2 — Fallback dialogue on navigation failure

**Scenario:** When the NPC cannot reach a target (NavMesh path blocked), it should speak a fallback line rather than silently stopping.

Subscribe to `OnStepFailed` and inject a dynamic context update:

```csharp
private void HandleStepFailed(ConvaiActionInvocation invocation)
{
    if (invocation.Command.Name == "Walk To")
    {
        string targetName = invocation.Command.Target ?? "that location";
        // Inject into dynamic context so the NPC acknowledges the failure naturally
        _character.DynamicContext.AddEvent($"Unable to reach {targetName} — path was blocked.");
    }
}
```

### Next steps

{% content-ref url="/pages/yKmOPjUYIRCR4eqPYD0A" %}
[Write a custom action executor](/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/writing-custom-executors.md)
{% endcontent-ref %}

{% content-ref url="/pages/73HufYIPX0JRnnAsGDbk" %}
[Character actions scripting reference](/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/actions-scripting-reference.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.convai.com/api-docs/plugins-and-integrations/convai-unity-sdk/features/character-actions/dispatcher-and-batch-policies.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
