Embedding

Six ways to integrate noumen into your application — in-process, HTTP/SSE, middleware, WebSocket, framework-specific patterns, and headless CLI.

noumen is designed as a library first. Whether you're building a VS Code extension, a web app with an AI coding assistant, or a CI pipeline, you can embed the full agent loop directly into your application.

In-process (Node.js / Bun)

The simplest integration: import Agent, create a thread, and iterate over events.

import { Agent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createAnthropic } from "@ai-sdk/anthropic";

const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });

const code = new Agent({
  provider: new AiSdkProvider({
    model: anthropic("claude-opus-4.6"),
    providerFamily: "anthropic",
    cacheConfig: { enabled: true },
  }),
  sandbox: LocalSandbox({ cwd: "/my/project" }),
  options: {
    permissions: { mode: "bypassPermissions" },
    autoCompact: true,
    costTracking: { enabled: true },
  },
});

await code.init();

const thread = code.createThread();

for await (const event of thread.run("Add error handling to server.ts")) {
  switch (event.type) {
    case "text_delta":
      process.stdout.write(event.text);
      break;
    case "tool_use_start":
      console.log(`\n[tool] ${event.toolName}`);
      break;
    case "tool_result":
      console.log(`[done] ${event.toolName}`);
      break;
    case "turn_complete":
      console.log(`\n[cost] ${event.usage.total_tokens} tokens`);
      break;
  }
}

await code.close();

One-shot with run() and execute()

For simple tasks you can skip createThread() entirely. run() streams events, execute() runs to completion and returns a result.

import { LocalAgent } from "noumen/local";

const agent = LocalAgent({ provider: "anthropic", cwd: "." });

// Streaming — same events as thread.run()
for await (const event of agent.run("Add a health-check endpoint")) {
  if (event.type === "text_delta") process.stdout.write(event.text);
}

// Execute — runs to completion, optional callbacks along the way
const result = await agent.execute("Fix the auth bug", {
  onText: (text) => process.stdout.write(text),
  onToolUse: (name) => console.log(`Using ${name}`),
});
console.log(`Done — ${result.toolCalls} tool calls, ${result.usage.total_tokens} tokens`);

LocalAgent (on noumen/local) is a thin factory over new Agent({ ..., sandbox: LocalSandbox({ cwd }) }). For raw host access use UnsandboxedAgent from noumen/unsandboxed. For remote sandboxes construct Agent directly with the sandbox of your choice — see Virtual Infrastructure.

Using presets

For quicker setup, use a preset that configures sensible defaults:

import { codingAgent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY! });

const code = codingAgent({
  provider: new AiSdkProvider({ model: openai.chat("gpt-5") }),
  cwd: "/my/project",
  sandbox: LocalSandbox({ cwd: "/my/project" }),
});

await code.init();
const thread = code.createThread();

for await (const event of thread.run("Refactor the auth module")) {
  if (event.type === "text_delta") process.stdout.write(event.text);
}

await code.close();

Presets require an explicit sandbox — import one from its subpath (noumen/local, noumen/unsandboxed, noumen/docker, …) and pass it in. This keeps the root barrel free of host-fs imports for apps that only use a remote sandbox.

Three presets are available:

PresetPermissionsFeatures
codingAgentdefaultSubagents, tasks, plan mode, auto-compact, retry, cost tracking, project context
planningAgentplanRead-only exploration, plan mode
reviewAgentplanRead-only with web search for documentation lookups

Health checks

Before running prompts, verify that all integrations are working. Pass an optional timeoutMs to control per-check timeout (default 10 000 ms):

const result = await code.diagnose();
console.log(result);
// {
//   overall: true,
//   provider: { ok: true, latencyMs: 342, model: "claude-sonnet-4" },
//   sandbox: {
//     fs: { ok: true, latencyMs: 2 },
//     computer: { ok: true, latencyMs: 45 },
//   },
//   mcp: { filesystem: { ok: true, latencyMs: 0, status: "connected", toolCount: 5 } },
//   lsp: { typescript: { ok: true, latencyMs: 0, state: "running" } },
//   timestamp: "2026-04-04T12:00:00.000Z",
// }

Each check includes:

  • ok — whether the subsystem is healthy
  • latencyMs — how long the check took (useful for identifying slow integrations)
  • error / warning — human-readable detail when something is wrong
  • overalltrue only when provider, filesystem, and shell all pass

Use result.overall as a gate before accepting user prompts in production.

HTTP / SSE server

Expose the agent over HTTP using the built-in server. Clients connect via Server-Sent Events for streaming.

import { Agent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createServer } from "noumen/server";
import { createAnthropic } from "@ai-sdk/anthropic";

const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });

const code = new Agent({
  provider: new AiSdkProvider({
    model: anthropic("claude-opus-4.6"),
    providerFamily: "anthropic",
  }),
  sandbox: LocalSandbox({ cwd: "/workspace" }),
  options: { permissions: { mode: "bypassPermissions" } },
});

await code.init();

const server = createServer(code, {
  port: 3001,
  auth: { type: "bearer", token: process.env.API_TOKEN! },
  cors: true,
});

await server.start();
console.log("Agent server running on http://localhost:3001");

Client connection

Connect from any HTTP client or use the built-in NoumenClient:

import { NoumenClient } from "noumen/client";

const client = new NoumenClient({
  baseUrl: "http://localhost:3001",
  token: process.env.API_TOKEN,
  transport: "sse",
});

for await (const event of client.run("Fix the failing test in auth.test.ts")) {
  if (event.type === "text_delta") process.stdout.write(event.text);
}

Middleware adapter

Mount the agent on an existing Express, Fastify, or Hono server using createRequestHandler():

import express from "express";
import { Agent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createRequestHandler } from "noumen/server";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY! });

const code = new Agent({
  provider: new AiSdkProvider({ model: openai.chat("gpt-5") }),
  sandbox: LocalSandbox({ cwd: "/workspace" }),
  options: { permissions: { mode: "bypassPermissions" } },
});
await code.init();

const app = express();
app.use("/agent", createRequestHandler(code, {
  auth: { type: "bearer", token: process.env.API_TOKEN! },
}));
app.listen(3000);

All REST endpoints from the Server API Reference are available under the mount path. WebSocket requires createServer() standalone mode.

WebSocket

For bidirectional communication (permissions, user input), use the WebSocket transport:

import { createServer } from "noumen/server";

const server = createServer(code, {
  port: 3001,
  ws: true,
  auth: { type: "bearer", token: process.env.API_TOKEN! },
});

await server.start();

The client can handle permission requests and user input interactively:

import { NoumenClient } from "noumen/client";

const client = new NoumenClient({
  baseUrl: "http://localhost:3001",
  token: process.env.API_TOKEN,
  transport: "ws",
});

for await (const event of client.run("Deploy to staging", {
  onPermissionRequest: async (req) => {
    const approved = await askUser(`Allow ${req.toolName}?`);
    return { allow: approved };
  },
  onUserInput: async (question) => {
    return await askUser(question);
  },
})) {
  if (event.type === "text_delta") process.stdout.write(event.text);
}

Framework examples

Next.js API route

Stream agent events to the browser via a Next.js route handler:

// app/api/agent/route.ts
import { Agent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY! });

const code = new Agent({
  provider: new AiSdkProvider({ model: openai.chat("gpt-5") }),
  sandbox: LocalSandbox({ cwd: process.env.WORKSPACE_DIR! }),
  options: { permissions: { mode: "bypassPermissions" } },
});

export async function POST(req: Request) {
  const { prompt } = await req.json();
  const thread = code.createThread();

  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder();
      for await (const event of thread.run(prompt)) {
        controller.enqueue(
          encoder.encode(`data: ${JSON.stringify(event)}\n\n`),
        );
      }
      controller.close();
    },
  });

  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
    },
  });
}

Electron main process

Run the agent in Electron's main process and communicate with the renderer via IPC:

// main.ts
import { app, ipcMain } from "electron";
import { codingAgent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createAnthropic } from "@ai-sdk/anthropic";

const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
const cwd = app.getPath("documents");
const code = codingAgent({
  provider: new AiSdkProvider({
    model: anthropic("claude-opus-4.6"),
    providerFamily: "anthropic",
    cacheConfig: { enabled: true },
  }),
  cwd,
  sandbox: LocalSandbox({ cwd }),
});

app.whenReady().then(async () => {
  await code.init();

  ipcMain.handle("agent:run", async (event, prompt: string) => {
    const thread = code.createThread();
    const events = [];
    for await (const e of thread.run(prompt)) {
      event.sender.send("agent:event", e);
      events.push(e);
    }
    return events;
  });
});

VS Code extension

Integrate the agent into a VS Code extension:

// extension.ts
import * as vscode from "vscode";
import { codingAgent, AiSdkProvider } from "noumen";
import { LocalSandbox } from "noumen/local";
import { createOpenAI } from "@ai-sdk/openai";

export function activate(context: vscode.ExtensionContext) {
  const cwd = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
  const openai = createOpenAI({
    apiKey: vscode.workspace.getConfiguration("noumen").get("apiKey")!,
  });
  const code = codingAgent({
    provider: new AiSdkProvider({ model: openai.chat("gpt-5") }),
    cwd,
    sandbox: LocalSandbox({ cwd }),
  });

  const disposable = vscode.commands.registerCommand(
    "noumen.run",
    async () => {
      const prompt = await vscode.window.showInputBox({
        prompt: "What should the agent do?",
      });
      if (!prompt) return;

      await code.init();
      const thread = code.createThread();

      const output = vscode.window.createOutputChannel("noumen");
      output.show();

      for await (const event of thread.run(prompt)) {
        if (event.type === "text_delta") output.append(event.text);
        if (event.type === "tool_use_start") {
          output.appendLine(`\n[${event.toolName}]`);
        }
      }

      await code.close();
    },
  );

  context.subscriptions.push(disposable);
}

Headless CLI

For subprocess integration from any language, run the agent in headless mode with bidirectional NDJSON over stdin/stdout:

noumen --headless -p anthropic -m claude-sonnet-4

Send commands as JSON lines on stdin, receive events as JSON lines on stdout. See the Server API Reference for the full protocol and examples in Node.js and Python.

Key stream events to handle

When embedding, you'll typically handle these core events:

EventWhenWhat to do
text_deltaModel streams textDisplay to user
tool_use_startTool call beginsShow activity indicator
tool_resultTool completesShow result summary
permission_requestAgent needs approvalPrompt user (WebSocket) or auto-approve
errorSomething went wrongDisplay error, retry, or abort
turn_completeRun finishedShow cost/token summary
session_resumedResuming a saved sessionConfirm to user

See Stream Events for the complete reference of all event types.