Agents
Agents
@workkit/agent is a thin agent-loop primitive on top of @workkit/ai-gateway. Typed tools (Standard Schema), multi-turn loops with mandatory budgets, multi-agent handoffs, typed streaming events, lifecycle hooks. Provider-agnostic.
It’s not a framework — there’s no state graph, no DSL. Just composable primitives.
Install
bun add @workkit/agent @workkit/ai-gateway zodQuick start
import { z } from "zod";import { createGateway } from "@workkit/ai-gateway";import { defineAgent, tool, handoff } from "@workkit/agent";
const gateway = createGateway({ providers: { ai: { type: "workers-ai", binding: env.AI } }, defaultProvider: "ai",});
const getQuote = tool({ name: "get_quote", description: "Get the latest price for a stock", input: z.object({ symbol: z.string() }), output: z.object({ price: z.number() }), handler: async ({ symbol }) => ({ price: 100 }),});
const analyst = defineAgent({ name: "market-analyst", model: "@cf/meta/llama-3.1-8b-instruct", provider: gateway, instructions: "You are a market analyst. Use tools to answer.", tools: [getQuote], stopWhen: { maxSteps: 5, maxTokens: 50_000 },});
const result = await analyst.run({ messages: [{ role: "user", content: "What's NIFTY?" }],});console.log(result.text, result.stopReason, result.usage);Streaming
for await (const event of analyst.stream({ messages: [{ role: "user", content: "..." }],})) { if (event.type === "text-delta") process.stdout.write(event.delta); if (event.type === "tool-end") console.log(event.call.name, "→", event.result);}Event union: step-start | text-delta | tool-start | tool-end | handoff | step-complete | error | done.
Handoffs
import { handoff } from "@workkit/agent";
const fundamentalsAnalyst = defineAgent({ /* ... */ });const technicalAnalyst = defineAgent({ /* ... */ });
const desk = defineAgent({ name: "research-desk", model: "@cf/meta/llama-3.1-8b-instruct", provider: gateway, tools: [ handoff(fundamentalsAnalyst, { when: "valuation, earnings" }), handoff(technicalAnalyst, { when: "price action, levels" }), ],});handoff() returns a synthetic tool the model invokes to switch agents. Cycle detection rejects after HANDOFF_HOP_LIMIT (default 3) re-entries with HandoffCycleError.
API
tool({ name, description, input, output?, handler, timeoutMs? })
- Standard Schema validates
inputbefore the handler runs. - Optional
outputschema validated on handler return. - Per-tool timeout default 30s. Aborted via
AbortSignal. - Tool name must match
/^[a-zA-Z_][a-zA-Z0-9_-]*$/.
defineAgent({ name, model, provider, instructions?, tools?, stopWhen?, hooks? })
provider—Gatewayfrom@workkit/ai-gateway.stopWhen.maxSteps— default10. Mandatory cap; loop never runs forever.stopWhen.maxTokens— checked against cumulativeusage.totalTokensafter each step.hooks.beforeModel(ctx)— fires before each provider call.hooks.afterTool(call, result, ctx)— fires after each tool resolution.hooks.onError({ kind, toolName?, error }, ctx)— fires on tool/provider/hook errors. Return{ abort: true }to escalate; otherwise loop continues.
agent.run({ messages, context? })
Returns { text, messages, usage, stopReason }. stopReason ∈ stop | max_steps | max_tokens | abort | error.
agent.stream({ messages, context? })
Async iterator of typed AgentEvents.
handoff(targetAgent, { when?, description? })
Synthetic handoff tool. Pass the full agent (not just { name }) so collision detection can transit through target tools.
Security defaults
- Tool input validated before the handler runs. Bad input →
ToolValidationErrorbecomes a tool-error message to the model. - Tool handler errors do not silently swallow. They surface as tool-error messages and as
onErrorhook calls. - Per-tool timeout (default 30s) with abort propagation into handler bodies.
- Abort signal propagates to the provider call AND in-flight tool handlers.
- Handoff cycles capped at 3 re-entries; raises
HandoffCycleError. - Tool name collisions rejected at
defineAgenttime including against handoff target tools. - Provider failures rethrow by default.
onErrormust explicitly return{ abort: false }to recover withstopReason:'stop'.
Out of scope (v1 — follow-ups)
@modelcontextprotocol/sdkMCP client integration.bindToAgentDurable Object helper.- Scratchpad compaction strategies.
maxCostUSDbudget (depends on per-provider pricing surface unification).
See also
- AI Integration —
@workkit/ai-gatewayprimer. - Agent Memory — pair
@workkit/memoryfor fact recall and conversation budgeting. - Durable Workflows — reach for
@workkit/workflowwhen an agent loop needs durable multi-step orchestration. - MCP Servers — expose your agent’s tools as a Model Context Protocol server.
- Notifications — agents often produce notifications as side effects.