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

@cerebro-labs/agentnava-sdk

v0.26.0

Published

The AgentNava SDK. Configure an agent, start a conversation, and work with it.

Readme

@cerebro-labs/agentnava-sdk

Configure an agent, start a conversation, work with it.

npm install @cerebro-labs/agentnava-sdk

Node 18 or later, or Bun, or any runtime with fetch. Both import and require work, and TypeScript types ship with it.

import { AgentNava } from '@cerebro-labs/agentnava-sdk';

const ws = AgentNava.getWorkspace(); // reads AGENTNAVA_API_KEY

const agent = await ws.agents.create({
  name: 'Refund checker',
  instructions: 'Decide whether a late delivery qualifies for a refund.',
});

const conversation = await agent.start();
console.log(await conversation.ask('Order 4471 arrived nine days late.'));

The full reference is at https://docs.agentnava.com. (It used to point here at a file path inside our own repository, which nobody who installs this package has.) This README covers only the things that are easy to get wrong.

Connecting an account, end to end

authorize() gives you a URL for your user to visit. waitUntilReady() tells you when they are done, so you do not have to write a polling loop or work out which field to believe.

const { url } = await conversation.authorize('gmail');
showToUser(url);                      // popup, redirect, QR code, your choice
await conversation.waitUntilReady();  // resolves once nothing required is outstanding

How you show the URL is yours: this SDK authenticates with an API key, so it runs on your server, and the browser half is your application's.

An OPTIONAL requirement does not hold the wait up, for the same reason it does not make ready false: the conversation genuinely works without it.

On timeout it throws conversation_not_ready and the message names what is still missing, e.g. still waiting on: connection gmail. It is safe to call again; someone who has not finished signing in may just need longer.

await conversation.waitUntilReady({ timeoutMs: 120_000, intervalMs: 2_000, signal });

0.2.0 is a breaking change. Upgrade from 0.1.1, do not stay on it.

"session" is gone from everything you touch. It survives only in our storage, which you never see.

| 0.1.1 | 0.2.0 | |---|---| | GET /v1/sessions | GET /v1/conversations (the old path now 404s) | | ConnectionStatus.sessionId | ConnectionStatus.conversationId | | conversation.authorize() sent sessionId | sends conversationId | | scope: 'session' came back from the API | scope: 'conversation', the value the type always declared |

0.1.1 stops working once these reach production, because the API reads only the new field names. There is no compatibility shim: this package is private and pre-launch, so the old spellings were removed rather than deprecated.

If you are on 0.1.1 the change is mechanical: rename sessionId to conversationId wherever you read it or pass it. Nothing else about the shape moved.

Holding an object is free

ws.agent(id), ws.conversation(id) and ws.project(id) make no network call. You get something you can call methods on, and the first request happens when you call one.

Store the id, not the object. A returned object is a handle, not JSON.

Asking: one method, two shapes

const reply = await conversation.ask('...');          // just the answer
for await (const e of conversation.ask('...')) {      // as it happens
  if (e.type === 'text') append(e.text);
  if (e.type === 'done') save(e.reply);
}

The message is sent when you call ask, not when you await or iterate it. Calling ask twice asks twice.

You may await the same turn more than once; it is a promise, and you get the same answer. You may not iterate it twice, or mix the two, because the events are a live stream rather than a stored list and the second consumer would receive nothing at all. That throws instead.

The turn is not your request. It runs on our side, so a dropped connection does not cancel it. Read the answer back with conversation.messages() rather than asking again.

Changing an agent publishes a version

There is no separate save. agent.update(), setWorkflow, removeWorkflow and setTriggers each publish a new version.

Send only what changes. Everything you do not send is inherited from the current version. That is what makes changing one field safe, and it is not a convenience: a caller that rebuilds and resends a whole configuration drops whatever it forgot, which has caused four separate data-loss bugs in this codebase.

await agent.update({ instructions: 'Be brief.' });    // keeps everything else

A conversation pins its version

It pins the agent's version when it starts and keeps it. Publishing afterwards does not move a conversation that is already open.

Knowledge is the exception, and deliberately so: it is read by reference, so replacing a knowledge file changes what open conversations see on their next turn. A price list you could not correct without republishing the agent would be useless.

Two drives

| | The agent sees | Belongs to | You write | The agent writes | |---|---|---|---|---| | Knowledge | /knowledge | the agent | yes | no | | Conversation | /workspace | one conversation | yes | yes |

An application stages material by writing it in before asking about it:

await conversation.writeFile('/input/contract.pdf', bytes);
await conversation.ask('What are the payment terms?');

Anywhere under the root works. input/ is a convention here and nothing enforces it, because the agent and your application share one namespace.

Credentials have two tiers, and they do not substitute

fixed is set once on the agent and used by every conversation. conversation is supplied per conversation, so each user brings their own account.

An account connected on the agent does not satisfy a declaration scoped conversation, and a conversation cannot supply a fixed one. Check conversation.ready and conversation.requires to see what is outstanding.

Drafting tools from an API you already describe

importTools turns an OpenAPI document, a Postman collection or a working curl command into draft httpTools, so nobody hand-writes one declaration per endpoint.

import { importTools } from '@cerebro-labs/agentnava-sdk';

const draft = importTools({ openapi }, { include: (c) => !c.url.includes('/admin/') });
await ws.agents.create({ id: 'support', instructions, httpTools: draft.tools });

It runs entirely on your machine. Nothing about the source is sent to AgentNava, which matters because these files routinely contain a live token: any credential found is replaced with a {{secrets.NAME}} placeholder and listed in draft.secrets, with no value carried across. Each drafted tool also declares the secrets it uses in its own secrets field, which is what lets the runtime load them for the turn.

They are drafts. draft.warnings names what a human still has to decide, and the big one is description-missing: none of the three formats contains the sentence a model reads to pick a tool, and a bad description does not fail, it makes the agent call the wrong thing.

Errors

Every failure throws AgentNavaError.

catch (err) {
  if (err instanceof AgentNavaError) {
    err.code;       // 'agent_not_found', branch on THIS
    err.status;     // 404
    err.retryable;  // 5xx, 429, and network failures
    err.retryAfter; // seconds, on a 429
  }
}

Branch on code, not on message. Codes are stable; messages are written for a person reading a log.

retryable is the only thing worth automating on: a 400 will fail identically forever, and a 404 will not become found.

404 covers both "no such thing" and "not yours", so a caller probing ids cannot tell another customer's agent from one that never existed. There is no 403.

Configuration

AgentNava.getWorkspace();                                   // AGENTNAVA_API_KEY
AgentNava.getWorkspace({ apiKey, baseURL, timeout });
AgentNava.getWorkspace({ fetch: myFetch });                 // a proxy, or a test

Runs anywhere fetch exists: Node, Bun, Deno, Cloudflare Workers, the browser. loadWorkflows is the one exception; it reads a directory off disk and so needs Node or Bun.

Licence

Apache License 2.0. See LICENSE, and NOTICE for the attribution that section 4(d) asks you to carry along.

Free to use, modify and redistribute, including commercially, with an explicit patent grant. Section 6 grants no rights to the names: "AgentNava" and "Cerebro Labs" remain trademarks of Cerebro Labs, Inc.