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

@volund-ia/sdk

v0.4.0

Published

Cliente TypeScript para rodar agentes do Volund OS com streaming em tempo real.

Readme

@volund-ia/sdk

Cliente TypeScript para rodar agentes do Volund OS pelo seu próprio código e receber, em tempo real (streaming), tudo que o agente faz — raciocínio, chamadas de ferramenta e a resposta token a token.

npm install @volund-ia/sdk

Requer Node ≥ 18 (usa o fetch nativo). Funciona também em Deno, Bun, Workers e no browser (parser SSE 100% web-standard).

Quickstart

import { VolundOS } from "@volund-ia/sdk";

const volund = new VolundOS({ apiKey: process.env.VOLUND_API_KEY! });

const run = await volund.agents.run({
  agentId: "agt_123",
  input: "Pesquise os 3 maiores concorrentes da empresa X e resuma.",
});

// Passo a passo conforme acontece:
for await (const event of run.stream()) {
  if (event.type === "assistant_text_delta") process.stdout.write(event.delta);
  if (event.type === "tool_call") console.log("→ usou:", event.tool_name);
}

// Ou só o resultado final:
const run2 = await volund.agents.run({ agentId: "agt_123", input: "Oi" });
const { output, usage } = await run2.result();

Continuar uma conversa (mesma thread):

const next = await volund.agents.continue({ runId: run.id, input: "E o 4º?" });

A DX

Espelha o Cursor SDK (Agent.create() → agent.send() → run.stream()): new VolundOS() → agents.run() → run.stream() / run.result() / run.cancel().

Eventos (VolundEvent)

Stream tipado por união discriminada — faça narrowing por event.type:

| type | Campos | | ----------------------- | ------------------------------------------------- | | run_started | protocol, run_id, agent_id | | thinking_delta | delta (raciocínio, streaming) | | assistant_text_delta | delta (resposta, streaming) | | tool_call | tool_call_id, tool_name, input | | tool_result | tool_call_id, output, is_error? | | awaiting_input | request_id, kind: "vault" \| "approval" (HITL — fecha o stream) | | run_finished | status, output, usage, error? |

O contrato é snake_case no fio (consistente com a API v1 e o ecossistema Anthropic/Cursor) e versionado por SCHEMA_VERSION (protocol no run_started).

Erros

Todos herdam de VolundError (tem .code e .status). Roteie por instanceof:

| Classe | Quando | | --------------------------- | --------------------------------------- | | VolundAuthError | 401 — chave ausente/inválida | | VolundForbiddenError | 403 — sem acesso ao agente | | VolundNotFoundError | 404 — agente/run inexistente | | VolundRunBusyError | 409 — já há run ativo na thread | | VolundRunFailedError | run.result() quando o run falha | | VolundAwaitingInputError | run.result() quando pausa p/ vault ou approval |

Aprovações (HITL)

Se o agente pausar esperando aprovação de uma ferramenta, o stream emite awaiting_input com kind: "approval". Decida por código e o run retoma:

for await (const ev of run.stream()) {
  if (ev.type === "awaiting_input" && ev.kind === "approval") {
    await volund.approvals.approve(ev.request_id);        // ou .reject(id, { note })
  }
}

volund.approvals: approve(id), reject(id, { note? }), decide(id, "approve" | "reject", { note? }).

Perguntas do agente

Quando o agente abre um card de pergunta, o stream emite question_asked e a tool do outro lado fica bloqueada esperando. Responda e o mesmo turno segue:

for await (const ev of run.stream()) {
  if (ev.type === "question_asked") {
    // desenhe o card com ev.questions e colete a escolha
    await volund.questions.answer(ev.request_id, { "Qual sprint?": "Sprint 4" });
  }
}

volund.questions: answer(id, answers), skip(id).

Repare que aqui não é awaiting_input: aquele é uma pausa que encerra o stream, enquanto question_asked mantém o for await vivo — é por isso que dá para responder sem sair do laço. Sem resposta, a tool desiste em ~10 minutos e o agente encerra o turno dizendo que aguarda; o card continua respondível.

As chaves de answers são os textos das perguntas, como vieram em ev.questions.

Notas

  • 0.4.0: novo evento question_asked e novo volund.questions (answer/skip). Aditivo no wire; se você faz exhaustive switch em ev.type, adicione um case "question_asked". Também novo o código de erro question_not_found.
  • 0.3.0: AwaitingInputEvent.kind agora inclui "approval" (além de "vault"). Aditivo no wire; se você faz exhaustive switch em ev.kind, adicione um case "approval".
  • stream() é consumível uma única vez (é um stream de rede). Não combine stream() e result() no mesmo Run.
  • run.cancel() aborta a conexão — o servidor encerra a sandbox.
  • execution: "local" (rodar no cwd do dev, estilo Cursor) chega na V2; o tipo já existe, mas a V1 só roda na nuvem.

Testar contra um preview da Vercel (modo intermediário)

Antes do endpoint de produção, dá pra apontar o SDK pro deployment de preview do PR:

VOLUND_API_KEY=vos_live_... \
VOLUND_AGENT_ID=agt_... \
VOLUND_BASE_URL=https://seu-preview.vercel.app \
npm run example

Se o preview estiver com Deployment Protection ligada, passe o token de Protection Bypass for Automation — ele vira um header via defaultHeaders:

VERCEL_BYPASS=<secret> ...demais envs... npm run example
new VolundOS({
  apiKey,
  baseUrl: "https://seu-preview.vercel.app",
  defaultHeaders: { "x-vercel-protection-bypass": process.env.VERCEL_BYPASS! },
});

Timeouts e runs longos

O SDK tem dois timeouts, e nenhum limita a duração total do run:

  • timeoutMs (default 60s) — só a fase pré-stream: tempo máximo até a resposta (headers) chegar. Assim que o stream começa, é desarmado.
  • idleTimeoutMs (default desligado) — durante o stream: aborta se nenhum dado (evento ou heartbeat) chegar nesse intervalo. Serve para detectar conexões travadas sem matar runs longos saudáveis — o servidor manda heartbeat : ping (~15s) que reseta o ocioso.
  • Duração total do run NÃO é limitada pelo SDK — depende do servidor/plataforma (a rota usa maxDuration; confirme o teto do seu plano de deploy).
new VolundOS({ apiKey, timeoutMs: 30_000, idleTimeoutMs: 120_000 });

Desenvolvimento

npm install
npm test              # testes do parser SSE (vitest)
npm run typecheck
npm run build         # tsdown → ESM + CJS + .d.ts
npm run check:protocol  # garante o contrato em sincronia com o volund-os

O contrato de eventos é vendorado de volund-os em src/protocol/events.ts — ver src/protocol/README.md. Atualize só via npm run sync:protocol.

Consumir um commit que ainda não foi publicado

O release sai por tag (v* dispara o workflow). Entre o merge e a publicação, um consumidor pode apontar direto para o repositório ou para um tarball local:

npm install volund-ia/os-sdk            # ou volund-ia/os-sdk#v0.4.0
npm pack && npm install ./volund-ia-sdk-0.4.0.tgz

O prepare builda no install, então o dist/ não precisa estar versionado. O nome do tarball achata o escopo: @volund-ia/sdk vira volund-ia-sdk-<versão>.

Licença

MIT