Types and errors
The whole surface on one page.
Every call
Five entry points. Everything else is a method on what you already hold.
// GETTING IN
new AgentNava(options?) -> Workspace // no request
ws.get() -> Workspace
ws.agents.create(fields) -> Agent
ws.agents.list() -> Agent[]
ws.projects.create(fields) -> Project
ws.projects.list() -> Project[]
ws.agent(agentId) -> Agent // no request
ws.conversation(conversationId) -> Conversation // no request
ws.project(projectId) -> Project // no request
// AN AGENT
agent.start({ version?, parameters? }) -> Conversation
agent.update(fields) -> Agent // publishes a version
agent.get({ version? }) -> Agent
agent.versions() -> Agent[]
agent.delete() -> void
agent.conversations() -> Conversation[]
agent.setWorkflow(workflow) -> Agent // publishes a version
agent.removeWorkflow(name) -> Agent // publishes a version
agent.setTriggers(triggers) -> Agent // publishes a version
agent.knowledge.put(path, bytes) -> FileEntry
agent.knowledge.list(dir?) -> FileEntry[]
agent.knowledge.remove(path) -> void
agent.bind({ secrets?, connections? }) -> Agent
agent.connections() -> ConnectionStatus[]
agent.authorize(provider) -> { url }
agent.disconnect(provider) -> void
// A CONVERSATION, FROM AN AGENT OR A PROJECT
conversation.ask(text, { attachments? }) -> Turn
conversation.messages() -> Message[]
conversation.end() -> void
conversation.bind({ secrets?, connections? }) -> Conversation
conversation.authorize(provider) -> { url }
conversation.files(dir?) -> FileEntry[]
conversation.readFile(path) -> string
conversation.readFileBytes(path) -> Uint8Array
conversation.writeFile(path, bytes) -> FileEntry
conversation.removeFile(path) -> void
conversation.fileUrl(path, { download?, ttl? }) -> string
// A PROJECT
project.start({ parameters? }) -> Conversation
project.update(fields) -> Project
project.get() -> Project
project.addAgent(agentId) -> Project
project.removeAgent(agentId) -> Project
project.archive() -> Project
project.delete() -> void
project.conversations() -> Conversation[]
project.files(dir?) -> FileEntry[]
project.readFile(path) -> string
project.fileUrl(path, { download?, ttl? }) -> string
// HELPERS
loadWorkflows(dir) -> Workflow[]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. A returned object carries methods, so store the id and use ws.agent(id) to come back in.
Types
type Workspace = { id: string; name: string; credits: number; createdAt: string };
type Agent = {
id: string;
name: string;
instructions: string;
greeting?: string;
grade: 'standard' | 'premium';
filesystem: 'drive' | 'none';
tools: string[];
workflows: Workflow[];
connections: Connection[];
secrets: Secret[];
parameters: Parameter[];
triggers: Trigger[];
mcpServers: McpServer[];
httpTools: HttpTool[];
version: number;
createdAt: string;
updatedAt: string;
};
type Workflow = { name: string; when: string; content: string };
type Trigger =
| { kind: 'chat' }
| { kind: 'schedule'; cron: string; timezone?: string; message: string }
| { kind: 'interval'; seconds: number; maxRuns?: number; message: string }
| { kind: 'webhook'; message?: string; url?: string };
type Connection = { provider: string; scope: Scope; required?: boolean };
type Secret = { name: string; scope: Scope; required?: boolean };
type Scope = 'fixed' | 'conversation';
type Parameter = {
name: string;
type: 'string' | 'number' | 'date' | 'choice';
required?: boolean;
default?: string;
choices?: string[];
};
type McpServer = {
name: string;
url: string;
transport?: 'http' | 'sse';
headers?: Record<string, string>;
timeoutMs?: number;
};
type HttpTool = {
name: string;
description: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
url: string;
headers?: Record<string, string>;
body?: string;
params?: Record<string, HttpToolParam>;
secrets?: string[];
timeoutMs?: number;
};
type ConnectionStatus = {
provider: string;
declared: boolean;
required: boolean;
scope?: Scope;
connected: boolean;
suppliedBy?: 'conversation' | 'agent' | 'workspace';
accountLabel?: string;
error?: string;
};
type AgentConversation = {
id: string; agentId: string; version: number;
startedAt: string; title?: string;
ready: boolean; requires: Requirement[];
};
type ProjectConversation = {
id: string; projectId: string;
startedAt: string; title?: string;
ready: boolean; requires: Requirement[];
};
type Project = {
id: string;
name: string;
goal: string | null;
managerInstructions: string | null;
grade: 'standard' | 'premium';
status: 'active' | 'archived';
agents: Member[];
createdAt: string;
updatedAt: string;
};
type Member = { agentId: string; name: string; addedAt: string };
type Requirement = { kind: 'secret' | 'connection'; name: string; required: boolean };
type Turn = Promise<string> & AsyncIterable<TurnEvent>;
type TurnEvent =
| { type: 'start'; turnId: string }
| { type: 'progress'; steps: Step[] }
| { type: 'tool'; name: string; state: 'start' | 'end'; ok?: boolean }
| { type: 'text'; text: string }
| { type: 'file'; path: string }
| { type: 'done'; reply: string }
| { type: 'error'; message: string; retryable: boolean };
type Step = {
id: string;
label: string;
kind: 'read' | 'write' | 'edit' | 'search' | 'shell'
| 'web' | 'data' | 'task' | 'plan' | 'other';
status: 'active' | 'done' | 'error';
};
type Message = { role: 'user' | 'assistant'; content: string; at: string };
type Attachment = { kind: 'image' | 'file'; name?: string; mimeType: string; data: string };
type FileEntry = {
path: string;
kind: 'file' | 'directory';
mimeType: string;
size: number;
modifiedAt: string;
};
type AgentNavaError = {
code: string;
message: string;
status: number;
retryable: boolean;
retryAfter?: number;
};There are two kinds of conversation and they are two types. You know which one you hold, because you know which call returned it.
Errors
Every failure throws AgentNavaError.
import { AgentNavaError } from '@cerebro-labs/agentnava-sdk';
try {
await conversation.ask('...');
} catch (err) {
if (err instanceof AgentNavaError) {
err.code; // 'agent_not_found'
err.status; // 404
err.retryable; // false
err.retryAfter; // seconds, on 429
}
}status | |
|---|---|
| 400 | Something in the request was wrong |
| 401 | Missing or invalid key |
| 404 | It does not exist, or it is not in your workspace |
| 409 | Conflicts with current state, such as reusing an agent id |
| 429 | Rate limited. retryAfter says how long |
| 5xx | Ours. retryable is true |
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.
Codes are stable. Messages are written for a person reading a log and will be reworded.
retryable is the only thing worth automating on: true for 5xx and 429, false for
everything else, because a 400 will fail identically forever.
When you iterate a turn instead of awaiting it, a failure arrives as an error
event and the loop ends.
Client options
new AgentNava();
new AgentNava({ apiKey, baseURL, timeout });
new AgentNava({ fetch: myFetch });apiKey | Defaults to AGENTNAVA_API_KEY |
baseURL | Defaults to https://api.agentnava.com |
timeout | Per request, in milliseconds. Not applied to a streaming turn |
fetch | Your own fetch, for custom transport or authentication |
Construction is synchronous and makes no request. The SDK does not retry, cache or queue on your behalf: a call maps to a request, and what comes back is what the API returned.