Dispatch

Dispatch is Restormel's in-process model-execution Interaction Format: a structured request/response envelope for predictable AI interactions. Use it to standardise how your application communicates task intent, cost constraints, and routing context. It is an in-process contract, not a wire protocol — for spec-conformant cross-agent interop see A2A (Agent2Agent).

Formerly published as @restormel/aaif (the "AAIF" envelope); renamed to Dispatch. Old AAIF* exports remain as deprecated aliases until 1.0.

Status

Dispatch is advanced — the type contract is defined and exported from @restormel/dispatch. Dispatch runtime helpers are available via executeDispatchRequest(). Before install, verify availability with npm view @restormel/dispatch version.

Request

type DispatchRequest = {
  input: string
  task?: "chat" | "completion" | "embedding"
  constraints?: {
    maxCost?: number
    latency?: "low" | "balanced" | "high"
    tokens?: {
      inputTokensM?: number
      outputTokensM?: number
    }
  }
  user?: {
    id: string
    plan?: string
  }
  routing?: {
    model?: string
    provider?: string
  }
  routingContext?: { routeId?: string; workload?: string; stage?: string; /* … */ }
  routingPlan?: { stepChain?: unknown[]; routingAttempts?: unknown[] }
  integrationStack?: {
    schemaVersion: "1"
    templateId?: string
    components: { id: string; role?: string }[]
  }
}

Response

type DispatchResponse = {
  output: string
  provider: string
  model: string
  cost: number
  routing: {
    reason: string
  }
}

Package

Types, runtime guards, and the runtime helper are exported from @restormel/dispatch:

import type { DispatchRequest, DispatchResponse } from "@restormel/dispatch";
import { isDispatchRequest, isDispatchResponse } from "@restormel/dispatch";

Use cases

  • Agent orchestration — standardise how agents request model completions with cost and latency constraints.
  • Routing transparency — responses include the routing reason so callers understand why a provider/model was chosen.
  • Cost control — the maxCost constraint caps estimated spend per request.

Integration stack (optional)

Optional integrationStack on DispatchRequest declares which third-party products appear in the host environment (for example Neon, Vercel, OpenRouter). It is orthogonal to routingContext (resolve hints) and does not change provider selection. Use it for logs, MCP agents, and analytics. Valid components[].id values are exported as INTEGRATION_COMPONENT_IDS from @restormel/dispatch; isDispatchRequest() validates the object when present. The Dashboard Overview → Connect your stack wizard copies JSON that passes this guard.

Human index of vendors: Integration catalog.

Routing and resolve

Dispatch adds optional routingContext on DispatchRequest so hosts can mirror resolve hints (for example workload, stage, attemptNumber) next to the legacy routing object. Dispatch does not call the dashboard resolve HTTP API; for a full stepChain from Keys, use @restormel/keys resolve() (Gateway key) then execute the model in your worker.

Canonical semantics: Routing contract (mirrors docs/architecture/keys-routing-contract.md). Agents: MCP docs.canonical_resolve topic keys_routing_contract or suite tool routing.capabilities.

When to choose Dispatch vs MCP vs CLI

  • Dispatch — your app/service wants a typed contract and runtime helper for routing + cost estimation.
  • MCP — an agent or IDE wants to call Restormel tool surface (routing/cost/validation) via stdio.
  • CLI — you need local, developer-friendly inspection and debugging without embedding into runtime code.

Runtime helper

The Dispatch runtime helper resolves provider/model and estimates cost via @restormel/keys. Hosts can optionally provide the final model output via the generate callback.

import { executeDispatchRequest } from "@restormel/dispatch";
import { createKeys, openaiProvider } from "@restormel/keys";

const keys = createKeys(
  { routing: { defaultProvider: "openai" }, keys: [{ id: "k1", provider: "openai" }] },
  { providers: [openaiProvider] }
);

const res = await executeDispatchRequest(
  { input: "Summarise…", task: "chat", routing: { model: "gpt-4o-mini" } },
  keys,
  { generate: async (ctx) => "output(cost=" + ctx.cost + ")" }
);

Next steps