novo agents

Quickstart

Novo is the complete agent behind one API. One key and one create call get you an agent that can search the web, drive a browser, run code, and edit documents, streamed back into your product as it works. You don't pick a model or run agent infrastructure: pick a band, and Novo keeps it current. Every run returns a metered cost you can pass through.

1. Get an API key

Sign in to the console, open api keys, create one, and copy the secret. It's shown once, and it starts with novo_ag_. Set it on your server:

export NOVO_AGENTS_API_KEY=novo_ag_…

The SDK is server-only. Never ship the key to a browser bundle; constructing the client in browser code throws.

2. Install the SDK

npm install novoagents

The SDK has no peerDependencies. It speaks HTTP to https://api.novoagents.ai.

3. Hello world

Save this as hello.ts and run it with npx tsx hello.ts:

import { NovoAgents, isNovoTextDelta } from 'novoagents';

const novo = new NovoAgents(); // reads NOVO_AGENTS_API_KEY

const agent = await novo.agents.create({
  context: 'You are a helpful product assistant.',
});

const thread = await novo.threads.create({ agentId: agent.id });

const stream = await novo.threads.stream(thread.id, {
  input: 'Write a haiku about TypeScript.',
});

for await (const event of stream) {
  if (isNovoTextDelta(event)) process.stdout.write(event.delta);
}

That's a complete run. band defaulted to 'core' and delegation to subagents defaulted on. You configure capabilities like web, browser, or a workspace when your agent needs them.

4. The pattern you'll actually ship

One contract matters before you build on the loop above: a run is finished when the stream yields the data-novo-terminal chunk, not when the HTTP stream ends. A connection can drop or time out mid-run; the run keeps going on Novo's side, and EOF without a terminal chunk just means you stopped listening. streamToCompletion() handles this for you: it reconnects across drops until the terminal chunk arrives:

const stream = await novo.threads.stream(thread.id, {
  input: 'Summarize the last three releases of our changelog.',
  budgetCents: 500, // hard cap: the run stops before exceeding it
});

const result = await stream.streamToCompletion();
console.log(result.text);
console.log(`finished: ${result.usage.costCents.toFixed(2)}¢`);

usage.costCents is the final Novo-billed amount in fractional cents: the receipt you can attribute to your own customers. See pricing for how usage is metered.

stream.runId, stream.threadId, and stream.agentId are synchronous strings available the moment threads.stream(...) resolves.

Where next: pick your path

Wire it into your app

The SDK stays on your server; your browser talks to a thin route that proxies the stream:

// app/api/agent/route.ts
export const maxDuration = 300; // don't let the platform cut the stream

export async function POST(request: Request): Promise<Response> {
  const { input } = (await request.json()) as { input: string };
  const stream = await novo.threads.stream(threadId, { input });
  return stream.toResponse(); // SSE straight to the browser
}

Full walkthrough: Stream into a Next.js route. On the client, the headless @novoagents/react hooks (useNovoRun, useNovoInteractions) consume that route and give you messages, status, and pending approvals without hand-rolling the stream protocol.

Hand it to your coding agent

Building with Claude Code, Cursor, or another coding agent? Don't wire it by hand: copy the setup skill into your agent. It interviews you, picks the right capabilities, and integrates Novo end to end. The raw file lives at novoagents.ai/setup.md.

What just happened

  • agents.create(...) returns immediately. Agents are configuration, not state; one agent serves many threads.
  • threads.create(...) opens a conversation; threads.stream(...) starts a run that keeps running on Novo's infrastructure until it terminates or you cancel it.
  • One run can quietly fan out to delegated subagents when that produces a better answer. You'll see them as data-novo-task cards on the stream. See Subagents for how delegation works.
  • The stream is a discriminated union of typed chunks; isNovoTextDelta is one of the exported guards.

Next