Yoke

Agents

Stateful code with model-powered loops.

An agent is defined through code and is stateful — agent instances keep state across messages. Yoke offers two levels of agents:

  • Low-level: AgentDefinition + AgentManager — you own everything.
  • High-level: Agent({ ... }) and Prompt({ ... }) — Yoke wraps the agent-owned tool loop, structured IO, and optional persistence.

AgentManager (low level)

Register a definition and the manager handles spawning instances and dispatching messages:

runtime.agent({
  name: "assistant",
  initialState: {},
  async run(ctx, input) {
    // ctx.inference, ctx.tools, ctx.agents are all available
    return { answer: input.text };
  },
});

The runtime (or an actor, or another agent) can spawn a new instance or send a message to an existing one:

ctx.agents.assistant("instance-1").sendMessage({ text: "hi" });

Agent (high-level primitive)

Agent({ name, model, instructions, tools, persist }) wraps the tool loop, error recovery, and optional durable thread/state:

import { Agent } from "yoke";

const weatherAgent = Agent({
  name: "weather-bot",
  model: "deepseek/deepseek-v4-flash-0731",
  instructions:
    "You answer weather questions for any city. Use the forecast toolchain.",
  tools: [forecastToolchain],
  persist: true, // durable thread + state (SQLite backend)
});

const runtime = Runtime({ inference: stack })
  .agent(weatherAgent)
  .actor({
    name: "inbox",
    topics: ["messages.incoming"],
    act: (env, ctx) =>
      ctx.agents["weather-bot"]("default").sendMessage(env.payload),
  });

The reply shape is { message, steps, usage, data? } — the final message, how many model calls the loop took, usage totals, and any extracted structured data.

Options

OptionWhat it does
nameLiteral agent name (typed on ctx.agents)
modelA model id or a Router
instructionsSystem instructions
toolsA tool, a toolchain, or a list
maxStepsCap on tool-loop iterations
persistPersist thread + state across restarts
inputSchemazod schema for structured input
messageBuilderConvert structured input into messages (spawn only)
outputSchemazod schema for structured output (extraction)

Structured input

A messageBuilder(payload) → Message[] converts structured input into the messages sent to the model — used only when spawning a new instance. Without a builder, the input is JSON-stringified into one message.

Structured output

The agent never receives the schema as a structured-output parameter. It runs its normal tool loop; the final text is fed to a single-turn extraction call (extractStructured) that converts it into the structured object carried in reply.data.

Prompt (single turn)

Prompt({ name, model, instructions, inputSchema?, outputSchema? }) is a single, no-tool-call, single-turn agent:

import { Prompt } from "yoke";

const summarizer = Prompt({
  name: "summarizer",
  model: "gpt-4o",
  instructions: "Summarize the input in under 100 words.",
  outputSchema: z.object({ summary: z.string() }),
});

const { text, data } = await ctx.agents.summarizer("default").sendMessage({
  text: longDocument,
});

Reply shape: { text, message, steps: 1, data? }.

Agents call other agents

Agents compose naturally — either via ctx.agents or by turning an agent into a tool:

import { createToolFromAgent } from "yoke";

const research = Agent({ name: "researcher", /* ... */ });

const editor = Agent({
  name: "editor",
  model: "gpt-4o",
  instructions: "Edit the text handed to you.",
  tools: [createToolFromAgent(research, { description: "Do web research" })],
});

The agent-owned tool loop

Model calls are stateless — messages in, assistant message out. The SDK never executes tools and never loops. The agent owns the loop: declare tools in chat({ tools }), receive the tool calls in the response, execute them yourself via ctx.tools / ctx.callTool, append toolResultMessage(...), and call again.

inferenceLoop codifies this — and feeds tool errors back to the model so it can correct and retry. See inference for details.

On this page