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

oyadotai

v0.1.13

Published

A plan-don't-react framework for LLM agents. The model emits a typed dataflow plan once; the runtime executes it; the model never reads state it shouldn't.

Readme

oya

Agents that plan, don't react.

The model writes a typed plan once. The runtime runs it. Tool outputs never go back through the model - so your agent can't be prompt-injected through its tools, costs a fraction, and runs the same every time.

Drop-in for Mastra. TypeScript · Bun · MIT

Quickstart · The numbers · Why · Migrate from Mastra · Studio · White paper

The open-source core behind oya.ai - the hosted platform for plan-don't-react agents.


Quickstart

import { Agent, createTool } from "oyadotai";
import { anthropic } from "oyadotai/anthropic";
import { z } from "zod";

const getWeather = createTool({
  id: "get_weather",
  description: "Look up the weather for a city",
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => fetchWeather(city),
});

const agent = new Agent({
  model: anthropic("claude-haiku-4-5-20251001"),
  tools: { get_weather: getWeather },
});

const { text } = await agent.generate("How's the weather in NYC?");

That's the whole API - the same shape as Mastra. Types are inferred from your zod schema, and every value is OPAQUE to the model by default. You did less work and it's injection-safe.

The numbers

Same task, same tools, real Anthropic API, 3 trials on claude-opus-4-7:

| | Vercel AI SDK | Mastra | oya | |---|--:|--:|--:| | total tokens | 3,653 | 9,143 | 1,783 | | model round-trips | 4 | 4 | 2 | | latency | 20.0s | 20.2s | 6.3s | | same plan every run? | no | no | yes |

½ the tokens of the leanest loop, 5× fewer than Mastra, ~3× faster - and deterministic. Reproduce: bun run bench claude-opus-4-7. (The gap narrows on smaller/cheaper models and widens with bigger payloads - full methodology and numbers in benchmarks/.)

Why

Every other agent framework is a token loop (ReAct, LangGraph, AutoGen, Mastra, the Vercel AI SDK): the model picks a tool, sees the raw result, picks the next. Every URL, ID, and document flows back through the model. Three bugs follow - every time:

fetched:    https://example.io/q3-report.pdf
downloaded: https://example.com/q3-report.pdf     ← the model "fixed" the URL
expected:  fetch → validate → download
observed:  fetch → download → validate (skipped)  ← the model reordered the steps
$432 in tokens   ← re-reading every result through the model
 $51 in tokens   ← reading only what it needs to decide

Same root cause: the model read state it never needed to. oya makes that impossible. The model emits a typed dataflow plan; the runtime executes the DAG; each value is shown to the model only at the level the plan declares:

| level | the model sees | for | |---|---|---| | OPAQUE (default) | type + provenance - never the bytes | URLs, IDs, docs, payloads, secrets | | SUMMARY | a bounded projection ({count}) | facts to branch on | | TRANSPARENT | the full value | the user's message, the final answer |

An attacker can stuff a payload into any fetched page. The model never reads that handle, so indirect prompt injection through tool output has nowhere to land. You annotate none of this - it's OPAQUE by default.

Migrate from Mastra in 2 lines

If your app already uses @mastra/core, oya is close to a drop-in. createTool and Agent mirror the shapes you already write, so for most apps the entire migration is the import lines.

Step 1 - swap the imports:

- import { Agent } from "@mastra/core/agent";
- import { createTool } from "@mastra/core/tools";
- import { anthropic } from "@ai-sdk/anthropic";
+ import { Agent, createTool } from "oyadotai";
+ import { anthropic } from "oyadotai/anthropic";

Step 2 - there is no step 2. Your tool and agent definitions compile unchanged.

What stays identical

| Mastra | oya | Notes | |---|---|---| | createTool({ id, description, inputSchema, execute }) | same | Types are inferred from the zod inputSchema; execute's argument is typed for you - no casts. | | new Agent({ name, instructions, model, tools }) | same | tools is the same name → tool map. | | await agent.generate(prompt){ text } | same | text is the headline field, and token usage is reported for parity. | | agent.stream(prompt) | same call | Returns structured events (fullStream / textStream) instead of a raw token soup. | | anthropic(…) · openai(…) · google(…) | oyadotai/anthropic · …/openai · …/google | Same call shape, different import path. |

So the code you already have keeps working as-is:

import { Agent, createTool } from "oyadotai";
import { anthropic } from "oyadotai/anthropic";
import { z } from "zod";

const getWeather = createTool({
  id: "get_weather",
  description: "Look up the weather for a city",
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => fetchWeather(city),
});

const agent = new Agent({
  name: "WeatherBot",
  instructions: "Answer weather questions.",
  model: anthropic("claude-haiku-4-5-20251001"),
  tools: { get_weather: getWeather },
});

const { text } = await agent.generate("How's the weather in NYC?");

What changes underneath

Same surface, different engine. Mastra runs a token loop: the model calls a tool, reads the raw result, and decides the next call - re-reading every value back through the model. oya has the model emit one typed plan and executes that DAG directly. Values flow between tools by reference and are disclosed to the model only at their declared projection level (OPAQUE by default).

You didn't touch your code, but the migrated app now gets, for free:

  • ~½ the tokens of the leanest loop, 5× fewer than Mastra, ~3× faster (the numbers above) - intermediate state is piped tool-to-tool, never re-billed through the model.
  • Deterministic execution - one statically-checked order on every run, instead of a model-chosen sequence that can reorder or skip steps.
  • Injection-safe by construction - tool output the model never reads can't smuggle an injected instruction into its context.

Good to know before you migrate

  • Projection defaults to OPAQUE. Tool outputs are hidden from the model unless you mark them SUMMARY or TRANSPARENT. That's the safety win - but if a tool relied on the model reading its raw output to decide the next step, declare that output's projection. See Projection Types.
  • Providers import from oyadotai/* (oyadotai/anthropic, oyadotai/openai, oyadotai/google) rather than @ai-sdk/*.
  • oya is pre-1.0 and mirrors the core of the Mastra surface, not every helper. If something you depend on is missing, open an issue - Mastra parity gaps are high-priority.

Studio

Chat with your agents and watch each plan execute live - the DAG, the trace, and every value at its projection level (OPAQUE shows nothing, TRANSPARENT shows the value). In your project:

// oya.config.ts
export default { agents: { support } };
bunx oyadotai dev      # → oya Studio at localhost:4000

Use it anywhere

oya is a library, not a platform. Run an agent in a script, a Next.js route, a Bun server, a worker, the edge - await agent.generate(prompt). Stream it with agent.stream(prompt) (structured events, not a token soup) and render it with oya/react's usePlan / useChat, or serve SSE with oyadotai-server.

Built on this: oya.ai

This package is the open-source core that oya.ai runs on - the same plan-once runtime, projection types, and Studio, now hosted. Want the managed platform (deploy agents, schedules, skills, and the Studio without running any infrastructure) instead of wiring it up yourself? Start at oya.ai - everything you build against this library is the same engine that powers it.

Install

bun add oyadotai zod

Packages

| package | what | |---|---| | oyadotai | the runtime + Agent + createTool; oyadotai/anthropic · oyadotai/openai · oyadotai/google providers; oyadotai/react hooks; the oya dev studio | | oyadotai-server | toSSEResponse / toTextResponse for any Fetch server | | @oya/playground | the Next.js studio (make dev) | | @oya/benchmarks | the live comparison above |

Develop

make dev        # oya Studio
make test       # bun:test - checked against the Python reference runtime
make bench      # the comparison
make check      # typecheck + test, every package

Community & contributing

oya is a community project - contributions of every size are welcome.

White paper

oya is the TypeScript implementation of Plan, Don't React: Projection Types for LLM Agent Runtimes. Read the white paper:

License

MIT © Oya Labs, Inc.