npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@workkit/agent

v0.4.0

Published

Composable agent loop primitives — typed tools (Standard Schema), handoffs, streaming, hooks. Provider-agnostic via @workkit/ai-gateway.

Readme

@workkit/agent

Composable agent loop primitives for Cloudflare Workers. Typed tools (Standard Schema), multi-turn loops with mandatory budget enforcement, agent-to-agent handoffs, typed streaming events, and lifecycle hooks. Provider-agnostic via @workkit/ai-gateway.

Install

bun add @workkit/agent @workkit/ai-gateway zod

Quick 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("tool", event.call.name, "→", event.result);
}

API

tool({ name, description, input, output?, handler, timeoutMs? })

Standard Schema validates input before the handler runs. If output is supplied, the handler's return value is validated. Per-tool timeout default 30s. Tool name must match /^[a-zA-Z_][a-zA-Z0-9_-]*$/.

defineAgent({ name, model, provider, instructions?, tools?, stopWhen?, hooks? })

  • providerGateway from @workkit/ai-gateway.
  • stopWhen.maxSteps — default 10. Mandatory cap; loop never runs forever.
  • stopWhen.maxTokens — default unset; checked against cumulative usage.totalTokens after 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? }){ text, messages, usage, stopReason }

stopReason is one of "stop" | "max_steps" | "max_tokens" | "abort" | "error".

agent.stream({ messages, context? })AsyncIterable<AgentEvent>

Typed discriminated union — step-start, text-delta, tool-start, tool-end, handoff, step-complete, error, done.

handoff(targetAgent, { when?, description? })

Returns a synthetic tool the model can call to switch agents. Cycle detection rejects re-entries past HANDOFF_HOP_LIMIT (default 3).

Security defaults

  • Tool input validated via Standard Schema before the handler runs. Bad input → ToolValidationError surfaces to the loop and is delivered to the model as a tool-error message.
  • Tool handler errors do not silently swallow. They surface as tool-error messages and as onError hook calls. The hook decides whether to abort.
  • Per-tool timeout (default 30s) bounds latency; an unresponsive tool can't stall the loop.
  • Abort signal propagates to the provider call and short-circuits before the next step.
  • Handoff cycles capped at 3 re-entries; raises HandoffCycleError.
  • Tool name collisions rejected at defineAgent time including against handoff target tools.
  • Logger redaction — the loop emits text-delta events containing model output. Wire your own logger to scrub PII before storage.

Out of scope (v1 — follow-up issues)

  • @modelcontextprotocol/sdk MCP client integration.
  • bindToAgent Durable Object helper.
  • Scratchpad compaction strategies.
  • maxCostUSD budget (depends on per-provider pricing surface unification).

Versioning

Follows the workkit Constitution — single src/index.ts export, Standard Schema only, no cross-package imports outside declared peer deps. Changesets accompany every public API change.