Yoke.sh
Agents

Tools

Agent tools — defineTool, defineToolchain, Zod validation, parallel execution, and inference message outputs.

Tools extend the processor actor with pluggable capabilities the LLM can invoke. They are agent capabilities inside your product, not APIs you expose to end users.

A coding harness is the usual example: your server owns the workspace. defineTool for read / write / shell lives in the internal package so the agent can work. Users never call those tools — they talk to your session API, which drives Yoke. See Embed Yoke in your product.

Types

type ToolOutput = InferenceMessage | InferenceMessage[];

type Tool = {
  name: string;
  description: string;
  parameters?: z.ZodType;
  execute: (input: z.infer<typeof parameters>, ctx: ToolRunContext) => Promise<ToolOutput>;
};

type Toolchain = {
  id: string;
  description?: string;
  tools: Tool[];
};

Tool inputs are validated with Zod before execute runs. Outputs must be inference messages — not arbitrary JSON.

Output helpers

import { toolTextMessage, toolInferenceMessage, normalizeToolOutput } from "@yoke/sdk";

// Single text message
await toolTextMessage("Results: ...");

// From message parts
await toolInferenceMessage([{ type: "text", text: "...", format: "plain" }]);

// Normalize to array
normalizeToolOutput(output); // InferenceMessage[]

tool_result parts store the inference message array as structured content for the next infer round.

Registration

Static tools

import {
  defineAgentStack,
  defineTool,
  defineToolchain,
  toolTextMessage,
  z,
} from "@yoke/sdk";

const echoToolchain = defineToolchain("utilities", [
  defineTool({
    name: "echo",
    description: "Echo text back to the model",
    parameters: z.object({ text: z.string() }),
    execute: async (input) =>
      toolTextMessage(JSON.stringify({ echoed: input.text })),
  }),
]);

const agentStack = defineAgentStack({
  tools: [echoToolchain],
});

defineProcessorActor({
  configSchema: processorConfigSchema,
  stateSchema: processorStateSchema,
  tools: agentStack.tools,
});

Lazy resolver

defineAgentStack({
  tools: async (ctx) => ({
    tools: [],
    toolchains: ctx.config.model.startsWith("gpt")
      ? [searchToolchain]
      : [],
  }),
});

Called when building the provider payload each infer round.

Processor loop

  1. Resolve tools (static or lazy) → convert Zod to JSON Schema → send to provider
  2. If response has tool_call parts:
    • Post tool calls to monologue (agent author)
    • Validate inputs, execute all tools in parallel
    • Post tool_result parts to monologue (system author)
    • Run inference again
  3. Repeat until no tool calls (max 8 rounds)
  4. Post final type: "reply" to monologue for egress

Example echo tool

const echoToolchain = defineToolchain("utilities", [
  defineTool({
    name: "echo",
    description: "Echo text back to the model",
    parameters: z.object({ text: z.string() }),
    execute: async (input) =>
      toolTextMessage(JSON.stringify({ echoed: input.text })),
  }),
]);

Used with createLocalToolLoopProvider and model local-tool-loop for E2E demos without API keys.

Providers

ProviderTools support
createOpenAiProviderJSON Schema tools
createAnthropicProviderJSON Schema tools
createFixtureProviderecho tool for tests
createLocalToolLoopProviderLocal tool loop for demos

ToolRunContext

execute receives a context with conversation and config access for tools that need to read state or post side-effect messages. Keep tool handlers focused — heavy work should append events for actor processing.

On this page