Developer documentation

Everything, on one page.

Node ≥ 20 · TypeScript-first · zero runtime dependencies. npm install @nexusaiframework/runtime

Install

npm install @nexusaiframework/runtime   # the runtime, zero deps

# or work from source, to run the tests and the offline demo:
git clone https://github.com/BULMKT/NexusAIAgentFramework
cd NexusAIAgentFramework
npm install     # dev deps only, the runtime itself has zero
npm test        # 171 tests
npm run demo    # full lifecycle offline, no API keys

Quickstart

import { NexusRuntime, FileStorage, AnthropicProvider,
         defineAgent, defineTool } from '@nexusaiframework/runtime';

const sendEmail = defineTool({
  name: 'send_email',
  description: 'Send an email',
  parameters: { type: 'object', properties: { to: { type: 'string' } }, required: ['to'] },
  requiresApproval: true,
  async execute({ to }) { /* … */ return `sent to ${to}`; },
});

const runtime = new NexusRuntime({ storage: new FileStorage('.nexus') });
runtime.register(defineAgent({
  name: 'assistant',
  provider: new AnthropicProvider(),   // or OpenAIProvider / OllamaProvider
  model: 'claude-sonnet-4-5',
  systemPrompt: 'You are a helpful operations assistant.',
  tools: [sendEmail],
  maxTurns: 10,
  budget: { maxUsd: 0.50, maxTotalTokens: 100_000 },
}));

const run = await runtime.run('assistant', 'Email the team a status update.');
if (run.status === 'waiting_approval') {
  const done = await runtime.approve(run.id);   // or from another process, later
  console.log(done.result);
}

Tools & approvals

A tool is a name, description, JSON-Schema parameters, and an execute function. Set requiresApproval: true and the run pauses in waiting_approval before that tool ever executes. Safe sibling calls in the same turn run immediately; their results are buffered and never re-executed after the pause resolves. Denials become error tool-results the model sees and reacts to. Beyond approve and deny, a reviewer can approve with edited arguments, approve(runId, callId, { edited }), so the tool runs with corrected input, or answer the call directly, respond(runId, callId, text), so the tool never runs at all. Every variant is journaled with who decided.

Approval policies

Automate the easy decisions; escalate the rest. A throwing policy fails safe to 'ask'.

approvalPolicy: ({ toolName, arguments: a }) =>
  toolName === 'issue_refund' && Number(a.amountUsd) < 50
    ? 'approve'          // journaled: decidedBy 'policy'
    : 'ask',             // pause for a human, decidedBy 'human'

Budgets

budget.maxUsd and budget.maxTotalTokens are enforced before each LLM call. At the cap the run halts as halted_budget, journaled, notifiable, resumable after you raise the limit. Costs use a built-in pricing table (override via new NexusRuntime({ pricing })); unknown models cost $0 but token caps still apply.

Run lifecycle & resume

running → waiting_approval → running → completed
running → halted_budget | halted_max_turns   (resumable)
running → failed | cancelled                 (terminal)

await runtime.resume(runId);   // crashed process? new deploy? same run continues
await runtime.cancel(runId);

Tool execution is at-least-once on crash resume: if the process dies after a tool ran but before its result persisted, the tool re-executes. Results already recorded are never re-executed.

Durable MCP

import { McpClient, mcpTools } from '@nexusaiframework/runtime';

const client = await McpClient.connect({
  command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '/data'],
  // or: url: 'https://mcp.example.com/mcp' (Streamable HTTP, SSE supported)
});
const tools = await mcpTools(client, {
  requiresApproval: ['write_file'],   // or true, or (name) => boolean
  namespace: 'fs',                     // tools become fs__read_file, …
  include: ['read_file', 'write_file'],
});

The reverse also ships: nexus mcp exposes Nexus itself as an MCP server over stdio, so Claude, Cursor, or any MCP client can drive durable runs, run, approve, deny, respond, resume, and list, as MCP tools.

$ nexus mcp        # run/approve/deny/respond/resume/list over stdio

Replay

import { replayRun } from '@nexusaiframework/runtime';

// exact replay, offline, $0.00, verifies determinism
const report = await replayRun(run);
report.identical;      // true

// regression replay, recorded tools + live candidate model/prompt
const reg = await replayRun(run, { provider: candidate, systemPrompt: v2 });
reg.divergences;       // each tool call where behavior departed

Storage

MemoryStoragetests & throwaway scripts, not durable
FileStorage('.nexus')JSON snapshot + JSONL journal, atomic writes, the zero-dep default
SqliteStoragesingle SQLite file via built-in node:sqlite (Node 22+), import from '@nexusaiframework/runtime/sqlite'
PostgresStoragemulti-node deployments; bring-your-own pg client so it stays zero-dep, import from '@nexusaiframework/runtime/postgres'

Four adapters ship in the box, memory, file, SQLite, and Postgres, over one Storage interface of five methods. Redis and others are straightforward contributions.

Events, Slack, dashboard

runtime.on('approval:required', ({ run, approvals }) => { /* … */ });
// also: run:start, llm:call, llm:token (streamed), llm:response,
//       tool:start, tool:end, retry, run:complete, run:failed,
//       run:halted, notify:error

import { attachWebhook } from '@nexusaiframework/runtime';
attachWebhook(runtime, { url: process.env.SLACK_WEBHOOK_URL!, format: 'slack' });
attachWebhook(runtime, { url: 'https://ops.internal/hooks/nexus' });  // JSON

nexus serve starts the self-hosted dashboard (default 127.0.0.1:3838): live run list, transcripts, journals, approve/edit/deny/respond buttons, a stale-approval age badge, plus a JSON API at /api/runs. Pass --token (or NEXUS_DASHBOARD_TOKEN) to gate the API behind a shared secret; it is a gate, not a substitute for putting it behind your SSO/proxy.

OpenTelemetry & cost attribution

One call attaches an OTLP exporter. Nexus emits GenAI-convention spans, invoke_agent, chat, execute_tool, with token usage, zero dependencies, prompt content off by default. Runs land in Datadog, Grafana, Langfuse, or any OTLP backend, no vendor SDK.

import { attachOtel } from '@nexusaiframework/runtime';
attachOtel(runtime, { endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT });

Tag any run and the tag flows through the record, journal, webhooks, and every span; then filter by it. This is how you attribute an LLM bill to the customer that generated it.

await runtime.run('support', input, { tags: { customerId: 'acme-42' } });
const theirs = await runtime.listRuns({ tags: { customerId: 'acme-42' } });

Providers also stream tokens as an llm:token event, and runtime.cancel(runId) aborts a live model call in flight, not just at the next checkpoint.

Operating in production

The runtime-level controls a security review checks for. The perimeter (SSO, SIEM, secrets, egress) stays yours; these are the parts Nexus owns.

// Kill switch, one command for on-call:
await runtime.cancelAll();                 # every non-terminal run, cross-process
runtime.disableAgent('support', 'incident');  # refuse new runs

// Page someone when approvals blow their SLA:
import { watchStaleApprovals } from '@nexusaiframework/runtime';
watchStaleApprovals(runtime, { olderThanMs: 30*60_000,
  onStale: (run) => pagerduty.trigger(run.id, run.waitingMs) });

// Scrub PII from the audit journal before it ships to your SIEM.
// The live record keeps real content so the run can resume.
new NexusRuntime({ storage, redactStep: (type, detail) => redactPii(detail) });

CLI reference

nexus run <agent> <input…>      # start a run and follow it
nexus runs --status waiting_approval
nexus show <runId> --steps      # full audit journal
nexus approve <runId> [callId]  # --edit '{"amountUsd":50}' to fix args
nexus deny <runId> [callId] --reason "…"
nexus respond <runId> [callId] --text "…"   # answer, tool never runs
nexus resume <runId>
nexus replay <runId>            # offline re-execution + diff
nexus runs --stale 30m          # approvals past their SLA
nexus cancel-all --agent support   # kill switch
nexus serve --token $TOKEN      # dashboard + JSON API (gated)
nexus mcp                       # expose Nexus as an MCP server (stdio)
nexus agents
# config: nexus.config.mjs default-exporting a NexusRuntime; --json everywhere

API surface

new NexusRuntime(opts){ storage?, pricing?, retry?, redactStep? }
runtime.register(agent)agent: name, provider, model, systemPrompt?, tools?, maxTurns?, budget?, approvalPolicy?
runtime.run(name, input)drives to terminal or paused state; returns the RunRecord, inspect status
approve / deny / respond / resume / cancelby runId (+ toolCallId); approve(…, { edited }) fixes arguments, respond(…, text) answers a call without running it, cancel aborts in-flight
run(name, input, { tags })tags ride the record, journal, webhooks, and OTel spans
getRun / listRuns / getStepsread the record and journal; listRuns({ status, tags }) filters
providersAnthropicProvider · OpenAIProvider · OllamaProvider · MockProvider (scriptable, for tests); token streaming via llm:token
replayRun / createReplayProvider / createReplayToolsdeterministic + regression replay
McpClient.connect / mcpTools · nexus mcpwrap MCP servers (stdio + Streamable HTTP), or serve Nexus as one
cancelAll / disableAgent / enableAgentthe kill switch: stop every non-terminal run, refuse new runs of an agent
watchStaleApprovals / staleApprovalspage someone when an approval waits past its SLA
attachOtel / attachWebhook / createDashboardOTLP tracing · Slack/JSON notifications · self-hosted UI ({ token } to gate)
validateRun / validateStep / validateJournalzero-dep OAR conformance checks (oar/0.2)
storageMemoryStorage · FileStorage · SqliteStorage · PostgresStorage

The persisted format is the Open Agent Run spec (CC0, now oar/0.2) with hosted JSON Schemas and two independent conformance implementations: the zero-dep validate* functions above and a stdlib-only Python reader (python -m oar validate|timeline). Full source, tests, and examples: GitHub.