v0.1.0MIT0 dependencies

The agent loop that cannot hang.

A TypeScript agent SDK with zero runtime dependencies. Define an agent, add tools, run the loop, get a typed result — on Node, Bun, Deno, or the edge.

$pnpm add just-another-sdk
just-another-sdk — demo
$

Why another one

Because the boring parts are the ones that bite you in production.

Agent frameworks demo well. Then you ship one, and the interesting problems turn out to be the unglamorous ones: a model that loops until your bill spikes, a tool exception that takes down a request, an API key that ends up in a CI log, a dependency tree you cannot audit, and no way to see what your agent actually did.

just-another-sdk treats those as the product, not the appendix. Every one of them is a default, a type, or a test — not something you remember to add.

Zero dependencies

npm ls shows one package. Providers are plain fetch calls — no vendor SDK to keep in sync, nothing transitive to audit.

A loop that cannot hang

Every exit path sets a stopReason. A model that calls tools forever costs you maxTurns requests, not your afternoon.

Failures stay recoverable

A tool that throws becomes a tool result the model reads and works around. Your run finishes with an answer, not a stack trace.

Your validator, not ours

Zod, Valibot, ArkType — anything implementing Standard Schema. Handler input is typed from the schema; the JSON Schema is derived for you.

Secrets never reach your logs

An API key cannot appear in a thrown error, an emitted event, or a printed trace. There is a test suite asserting exactly that.

Observable by construction

Tracing, streaming, and progress UIs all consume one event stream, so you can see what your agent did without a debugger.

The whole API

Four concepts. No ceremony.

An Agent is immutable configuration, so one instance safely serves every concurrent request. Run state is separate. Tools are plain functions with a schema. That is the entire mental model.

Agent
name, instructions, model, tools
RunState
messages, turns, usage, steps
tool()
schema in, typed handler out
ModelProvider
one method: generate()
agent.ts
import { Agent, tool } from 'just-another-sdk'
import { openrouter } from 'just-another-sdk/providers'
import * as z from 'zod'

const agent = new Agent({
  name: 'travel-assistant',
  instructions: 'Use your tools rather than guessing.',
  model: openrouter('anthropic/claude-opus-5'),
  tools: [
    tool({
      name: 'get_weather',
      description: 'Get the current weather for a city.',
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => fetchWeather(city),
      //                  ^^^^ string — inferred, and validated
    }),
  ],
})

const result = await agent.run('Weather in Paris?')

result.output      // "It's 18°C and clear in Paris."
result.usage       // { inputTokens: 412, outputTokens: 63, … }
result.stopReason  // 'finish'

One key. Every model.

OpenRouter reaches hundreds of models through a single provider, and the same OpenAI-compatible transport covers OpenAI, Groq, Together, DeepSeek, xAI, Ollama, vLLM, and LM Studio. Writing your own means implementing one method.

openrouteropenaigroqtogetherdeepseekollamavllmlm studioanthropic — nextgemini — next

Running in about two minutes.

$pnpm add just-another-sdk
Read the docs