Embed aricode in Node

aricode now ships a real in-process Node SDK. Create sessions, stream structured events, register custom tools, and keep session state under your application's control.

SDK shape

The public SDK is intentionally narrow: Node-only, async-iterator-first, and built around host-managed session state.

Client + Session runtime

Create a reusable client, then spawn isolated sessions for each conversation or workflow branch.

createAriadne(...)
client.createSession(...)
session.run(...)
Quick start

Structured Events streaming

Consume `message.delta`, `tool.call`, `approval.required`, `usage`, and `turn.completed` over a single async iterator.

for await (const event of session.run(task)) {
  ...
}
Event model

Public Tooling API extensible

Register application-specific tools with `defineTool(...)` and `createToolRegistry(...)` while keeping aricode's built-ins available.

defineTool(...)
createToolRegistry([...])
Custom tools

Create a client and stream a run

The package root now exports the SDK directly. New integrations should use the aricode package and the createAricode(...) factory. Legacy createAriadne(...) aliases remain available for compatibility.

npm install aricode

Install aricode once. The same package ships the CLI binaries and the SDK runtime.

npm install aricode
ts createAricode()

Provide model and backend config once at client construction, then create one or more isolated sessions.

import { createAricode } from "aricode";

const client = createAricode({
  model: "qwen2.5-coder:32b",
  baseUrl: "http://localhost:11434/v1",
});

const session = client.createSession({
  cwd: process.cwd(),
});
ts session.run()

Runs stream structured events with `for await`, which works equally well in CLIs, servers, and desktop apps.

for await (const event of session.run("Read package.json and summarize the scripts.")) {
  if (event.type === "message.delta") {
    process.stdout.write(event.delta);
  }

  if (event.type === "tool.call") {
    console.log("tool", event.name, event.args);
  }
}

Event stream contract

The async iterator is the primary runtime interface in v1. aricode emits structured events instead of requiring callback orchestration.

type AriadneEvent

Every event includes sessionId, timestamp, and turn (the current turn number or null).

Event typeKey fieldsWhen emitted
message.deltadelta: stringEach streamed text chunk from the model
message.completedmessage: string, final: booleanFull message at end of a text block. final=true if no tools will run.
tool.callid, name, argsBefore a tool executes
tool.resultname, result, error?After a tool finishes
observationkind, textTool emits a human-visible observation (e.g., "read src/app.ts")
turn.completedtoolCalls: number, final: booleanEnd of an agent turn. final=true when the run is complete.
statusstatus, detail?Phase changes (thinking, compacting, indexing)
usageusageToken count after each model call
approval.requiredcommand, denial?Agent wants to run a blocked command
approval.resolvedcommand, decisionAfter the approval handler returns
warningmessageNon-fatal issue
errormessageFatal error (run terminates)
ts Cancellation

Pass an AbortSignal to cancel a run in progress. This aborts the current model API call and any running tool.

const controller = new AbortController();

// Cancel after 30 seconds
setTimeout(() => controller.abort(), 30_000);

for await (const event of session.run("...", {
  signal: controller.signal,
})) {
  // ...
}
ts Approval handler

When the agent wants to run a shell command that's blocked by the safety policy, the approval handler is called. Return "allow", "always", or "deny".

const client = createAricode({
  model: "qwen2.5-coder:32b",
  baseUrl: "http://localhost:11434/v1",
  approvalHandler: async ({ command, denial }) => {
    console.log(`Agent wants to run: ${command}`);
    return "allow"; // or "always" or "deny"
  },
});

Register application-specific capabilities

The public tool SDK is part of the first release. Define tools once, register them in a registry, and pass that registry into the client or session.

import { createAricode, createToolRegistry, defineTool } from "aricode";

const tools = createToolRegistry([
  defineTool({
    name: "lookup_ticket",
    schema: {
      name: "lookup_ticket",
      description: "Look up a ticket by id.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string" },
        },
        required: ["id"],
      },
    },
    async execute(args) {
      return { result: `ticket:${args.id}` };
    },
    metadata: {
      sideEffects: "none",
      permissionTier: "readonly",
      concurrentSafe: true,
      category: "plugin",
      kind: "tool",
      idempotent: true,
      cancellable: false,
      streaming: false,
      timeoutMs: 0,
      requiresApproval: false,
    },
  }),
]);

const client = createAricode({
  model: "qwen2.5-coder:32b",
  baseUrl: "http://localhost:11434/v1",
  toolRegistry: tools,
});

Tool metadata fields

FieldTypePurpose
sideEffects"none" | "read" | "write"Whether the tool modifies external state
permissionTier"readonly" | "mutating" | "privileged"Safety tier — determines if approval is needed
concurrentSafebooleanWhether the tool can run in parallel with others
categorystringGrouping label (e.g., "plugin", "search")
idempotentbooleanWhether calling it twice with the same args has the same effect
requiresApprovalbooleanForce approval dialog even for non-privileged tools
timeoutMsnumberExecution timeout in milliseconds (0 = no limit)

Host-owned persistence

The SDK defaults to in-memory host-managed state. Persist snapshots yourself, or use the included workspace adapter if you want aricode-style on-disk sessions.

Default store

InMemorySessionStore ships by default. Sessions live in memory and are lost on process exit unless you snapshot and persist them yourself.

Snapshots

session.snapshot() returns a serializable AriadneSessionState object. Save it to disk, a database, or anywhere. session.restore(state) rehydrates a session from a snapshot.

Workspace adapter

WorkspaceSessionStore persists sessions to the .aricode/ directory inside the workspace. This is what the CLI uses internally.

Custom SessionStore interface

Implement these methods to provide your own persistence backend:

interface SessionStore {
  // Load a session by ID. Return null if not found.
  load(sessionId: string): Promise<AriadneSessionState | null>;

  // Save session state. Called after each run completes.
  save(sessionId: string, state: AriadneSessionState): Promise<void>;

  // Optional: append individual events for real-time logging.
  appendEvent?(sessionId: string, event: AriadneEvent): Promise<void>;

  // Delete a session.
  clear(sessionId: string): Promise<void>;

  // Optional: generate a summary for the session.
  summarize?(sessionId: string, state: AriadneSessionState): Promise<string>;
}