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.
The public SDK is intentionally narrow: Node-only, async-iterator-first, and built around host-managed session state.
Create a reusable client, then spawn isolated sessions for each conversation or workflow branch.
createAriadne(...)
client.createSession(...)
session.run(...)
Quick start
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
Register application-specific tools with `defineTool(...)` and `createToolRegistry(...)` while keeping aricode's built-ins available.
defineTool(...)
createToolRegistry([...])
Custom tools
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.
Install aricode once. The same package ships the CLI binaries and the SDK runtime.
npm install aricode
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(),
});
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);
}
}
The async iterator is the primary runtime interface in v1. aricode emits structured events instead of requiring callback orchestration.
Every event includes sessionId, timestamp, and turn (the current turn number or null).
| Event type | Key fields | When emitted |
|---|---|---|
message.delta | delta: string | Each streamed text chunk from the model |
message.completed | message: string, final: boolean | Full message at end of a text block. final=true if no tools will run. |
tool.call | id, name, args | Before a tool executes |
tool.result | name, result, error? | After a tool finishes |
observation | kind, text | Tool emits a human-visible observation (e.g., "read src/app.ts") |
turn.completed | toolCalls: number, final: boolean | End of an agent turn. final=true when the run is complete. |
status | status, detail? | Phase changes (thinking, compacting, indexing) |
usage | usage | Token count after each model call |
approval.required | command, denial? | Agent wants to run a blocked command |
approval.resolved | command, decision | After the approval handler returns |
warning | message | Non-fatal issue |
error | message | Fatal error (run terminates) |
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,
})) {
// ...
}
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"
},
});
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,
});
| Field | Type | Purpose |
|---|---|---|
sideEffects | "none" | "read" | "write" | Whether the tool modifies external state |
permissionTier | "readonly" | "mutating" | "privileged" | Safety tier — determines if approval is needed |
concurrentSafe | boolean | Whether the tool can run in parallel with others |
category | string | Grouping label (e.g., "plugin", "search") |
idempotent | boolean | Whether calling it twice with the same args has the same effect |
requiresApproval | boolean | Force approval dialog even for non-privileged tools |
timeoutMs | number | Execution timeout in milliseconds (0 = no limit) |
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.
InMemorySessionStore ships by default. Sessions live in memory and are lost on process exit unless you snapshot and persist them yourself.
session.snapshot() returns a serializable AriadneSessionState object. Save it to disk, a database, or anywhere. session.restore(state) rehydrates a session from a snapshot.
WorkspaceSessionStore persists sessions to the .aricode/ directory inside the workspace. This is what the CLI uses internally.
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>;
}