SDK

The SDK provides programmatic access to Elyra's agent capabilities. Embed Elyra in other applications, build custom interfaces, or integrate with automated workflows.

Overview

Custom UI
Build a custom UI (web, desktop, mobile) on top of Elyra's agent.
App Integration
Integrate agent capabilities into existing applications.
Automation
Create automated pipelines with agent reasoning.
Sub-agents
Build custom tools that spawn sub-agents.
Testing
Test agent behavior programmatically.
RPC Alternative
Use from other languages via --mode rpc.

Installation

The SDK is included in the main package. No separate installation needed.

npm install @elyracode/coding-agent

Quick Start

import {
  AuthStorage,
  createAgentSession,
  ModelRegistry,
  SessionManager,
} from "@elyracode/coding-agent";

// Set up credential storage and model registry
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);

const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  authStorage,
  modelRegistry,
});

session.subscribe((event) => {
  if (
    event.type === "message_update" &&
    event.assistantMessageEvent.type === "text_delta"
  ) {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("What files are in the current directory?");

createAgentSession()

The main factory function for a single AgentSession. Uses a ResourceLoader to supply extensions, skills, prompt templates, themes, and context files. If you do not provide one, it uses DefaultResourceLoader with standard discovery.

import { createAgentSession } from "@elyracode/coding-agent";

// Minimal: defaults with DefaultResourceLoader
const { session } = await createAgentSession();

// Custom: override specific options
const { session } = await createAgentSession({
  model: myModel,
  tools: [readTool, bashTool],
  sessionManager: SessionManager.inMemory(),
});

AgentSession

The session manages agent lifecycle, message history, model state, compaction, and event streaming.

interface AgentSession {
  // Send a prompt and wait for completion
  prompt(text: string, options?: PromptOptions): Promise<void>;

  // Queue messages during streaming
  steer(text: string): Promise<void>;
  followUp(text: string): Promise<void>;

  // Subscribe to events (returns unsubscribe function)
  subscribe(listener: (event: AgentSessionEvent) => void): () => void;

  // Session info
  sessionFile: string | undefined;
  sessionId: string;

  // Model control
  setModel(model: Model): Promise<void>;
  setThinkingLevel(level: ThinkingLevel): void;
  cycleModel(): Promise<ModelCycleResult | undefined>;
  cycleThinkingLevel(): ThinkingLevel | undefined;

  // State access
  agent: Agent;
  model: Model | undefined;
  thinkingLevel: ThinkingLevel;
  messages: AgentMessage[];
  isStreaming: boolean;

  // In-place tree navigation
  navigateTree(targetId: string, options?: NavigateTreeOptions):
    Promise<{ editorText?: string; cancelled: boolean }>;

  // Compaction
  compact(customInstructions?: string): Promise<CompactionResult>;
  abortCompaction(): void;

  // Abort current operation
  abort(): Promise<void>;

  // Cleanup
  dispose(): void;
}

Session replacement APIs (new-session, resume, fork, import) live on AgentSessionRuntime, not on AgentSession.

AgentSessionRuntime

Use the runtime API when you need to replace the active session and rebuild cwd-bound runtime state. This is the same layer used by the built-in interactive, print, and RPC modes.

import {
  type CreateAgentSessionRuntimeFactory,
  createAgentSessionFromServices,
  createAgentSessionRuntime,
  createAgentSessionServices,
  getAgentDir,
  SessionManager,
} from "@elyracode/coding-agent";

const createRuntime: CreateAgentSessionRuntimeFactory = async ({
  cwd,
  sessionManager,
  sessionStartEvent,
}) => {
  const services = await createAgentSessionServices({ cwd });
  return {
    ...(await createAgentSessionFromServices({
      services,
      sessionManager,
      sessionStartEvent,
    })),
    services,
    diagnostics: services.diagnostics,
  };
};

const runtime = await createAgentSessionRuntime(createRuntime, {
  cwd: process.cwd(),
  agentDir: getAgentDir(),
  sessionManager: SessionManager.create(process.cwd()),
});

AgentSessionRuntime owns replacement of the active session across: newSession(), switchSession(), fork(), clone flows, and importFromJsonl().

Important: runtime.session changes after replacement operations. Event subscriptions are attached to a specific AgentSession, so re-subscribe after replacement.

Prompting and Message Queueing

interface PromptOptions {
  expandPromptTemplates?: boolean;
  images?: ImageContent[];
  streamingBehavior?: "steer" | "followUp";
  source?: InputSource;
  preflightResult?: (success: boolean) => void;
}

preflightResult is called once per prompt() invocation: true when accepted, false when rejected. Use steer() and followUp() to queue messages while the agent is streaming.

Events

Subscribe with session.subscribe(listener). The listener receives AgentSessionEvent objects with a type field for dispatch. Key event types:

Event Type Description
message_start New assistant message started.
message_update Streaming delta with assistantMessageEvent (text_delta, tool_call, usage, etc.).
message_end Assistant message completed with final usage/cost.
tool_start Tool execution started.
tool_end Tool execution completed.
agent_end Agent finished all work.
error An error occurred.

Model

import { getModel } from "@elyracode/ai";

const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const opus = getModel("anthropic", "claude-opus-4-5");

const { session } = await createAgentSession({
  model: opus,
  thinkingLevel: "high",
  authStorage,
  modelRegistry,
});

Use scopedModels for Ctrl+P cycling:

const { session } = await createAgentSession({
  scopedModels: [
    { model: getModel("anthropic", "claude-sonnet-4-5"), thinkingLevel: "high" },
    { model: getModel("openai", "gpt-4o"), thinkingLevel: "off" },
  ],
});

API Keys and OAuth

const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);

const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  authStorage,
  modelRegistry,
});

Runtime API key override (not persisted):

authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);

Tools

Built-in tools use process.cwd(). Use tool factories for a custom cwd:

import {
  readTool,
  bashTool,
  editTool,
  writeTool,
  createReadTool,
  createBashTool,
} from "@elyracode/coding-agent";

// Default cwd
const { session } = await createAgentSession({
  tools: [readTool, bashTool, editTool, writeTool],
});

// Custom cwd
const { session } = await createAgentSession({
  tools: [createReadTool(myCwd), createBashTool(myCwd)],
});

Built-in tools: readTool, bashTool, editTool, writeTool, grepTool, findTool, lsTool. Convenience bundles: codingTools (all), readOnlyTools (read + grep + find + ls).

Custom Tools

import { Type } from "typebox";
import { defineTool } from "@elyracode/coding-agent";

const statusTool = defineTool({
  name: "status",
  label: "Status",
  description: "Get system status",
  parameters: Type.Object({}),
  execute: async () => ({
    content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
    details: {},
  }),
});

const { session } = await createAgentSession({
  customTools: [statusTool],
});

Extensions

Load extensions via ResourceLoader:

const loader = new DefaultResourceLoader({
  cwd: process.cwd(),
  agentDir: getAgentDir(),
  additionalExtensionPaths: ["/path/to/my-extension.ts"],
  extensionFactories: [
    async (elyra) => {
      elyra.on("session_start", async (_e, ctx) => {
        ctx.ui.notify("Inline extension loaded", "info");
      });
    },
  ],
});
await loader.reload();

const { session } = await createAgentSession({
  resourceLoader: loader,
});

Session Management

import { SessionManager } from "@elyracode/coding-agent";

// In-memory (no persistence)
const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
});

// Persistent (default location)
const { session } = await createAgentSession({
  sessionManager: SessionManager.create(process.cwd()),
});

// Continue most recent
const { session, modelFallbackMessage } = await createAgentSession({
  sessionManager: SessionManager.continue(process.cwd()),
});

// Open specific session
const { session } = await createAgentSession({
  sessionManager: SessionManager.open(sessionFilePath),
});

The SessionManager API exposes session listing, branch navigation, labels, and tree structure:

const entries = sm.getEntries();
const branch = sm.getBranch();
const tree = sm.getTree();
const leaf = sm.getLeafId();
const label = sm.getLabel(entryId);

Settings Management

import { SettingsManager } from "@elyracode/coding-agent";

// In-memory with overrides
const settingsManager = SettingsManager.inMemory({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 2 },
});

const { session } = await createAgentSession({
  settingsManager,
});

InteractiveMode

Full TUI interactive mode with editor, chat history, and all built-in commands:

import {
  type CreateAgentSessionRuntimeFactory,
  createAgentSessionFromServices,
  createAgentSessionRuntime,
  createAgentSessionServices,
  getAgentDir,
  InteractiveMode,
  SessionManager,
} from "@elyracode/coding-agent";

const createRuntime: CreateAgentSessionRuntimeFactory = async ({
  cwd, sessionManager, sessionStartEvent,
}) => {
  const services = await createAgentSessionServices({ cwd });
  return {
    ...(await createAgentSessionFromServices({
      services, sessionManager, sessionStartEvent,
    })),
    services,
    diagnostics: services.diagnostics,
  };
};

const runtime = await createAgentSessionRuntime(createRuntime, {
  cwd: process.cwd(),
  agentDir: getAgentDir(),
  sessionManager: SessionManager.create(process.cwd()),
});

const mode = new InteractiveMode(runtime, {
  migratedProviders: [],
  modelFallbackMessage: undefined,
  initialMessage: "Hello",
  initialImages: [],
  initialMessages: [],
});

await mode.run();

Single-shot mode: send prompts, output result, exit:

import { runPrintMode } from "@elyracode/coding-agent";

// ... create runtime as above ...

await runPrintMode(runtime, {
  mode: "text",
  initialMessage: "Hello",
  initialImages: [],
  messages: ["Follow up"],
});

runRpcMode

JSON-RPC mode for subprocess integration:

import { runRpcMode } from "@elyracode/coding-agent";

// ... create runtime as above ...

await runRpcMode(runtime);

RPC Mode Alternative

For subprocess-based integration without building with the SDK, use the CLI directly:

elyra --mode rpc --no-session
Use SDK when Use RPC when
You want type safety You're integrating from another language
You're in the same Node.js process You want process isolation
You need direct access to agent state You're building a language-agnostic client
You want to customize tools/extensions You want minimal dependencies

Exports

Category Exports
Factory createAgentSession, createAgentSessionRuntime, AgentSessionRuntime
Auth and Models AuthStorage, ModelRegistry
Resource Loading DefaultResourceLoader, ResourceLoader (type), createEventBus
Helpers defineTool
Session SessionManager, SettingsManager
Built-in Tools codingTools, readOnlyTools, readTool, bashTool, editTool, writeTool, grepTool, findTool, lsTool
Tool Factories createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool
Types CreateAgentSessionOptions, CreateAgentSessionResult, ExtensionFactory, ExtensionAPI, ToolDefinition, Skill, PromptTemplate, Tool

Complete Example

import { getModel } from "@elyracode/ai";
import { Type } from "typebox";
import {
  AuthStorage,
  bashTool,
  createAgentSession,
  DefaultResourceLoader,
  defineTool,
  ModelRegistry,
  readTool,
  SessionManager,
  SettingsManager,
} from "@elyracode/coding-agent";

const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);

const statusTool = defineTool({
  name: "status",
  label: "Status",
  description: "Get system status",
  parameters: Type.Object({}),
  execute: async () => ({
    content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
    details: {},
  }),
});

const model = getModel("anthropic", "claude-opus-4-5");
if (!model) throw new Error("Model not found");

const settingsManager = SettingsManager.inMemory({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 2 },
});

const loader = new DefaultResourceLoader({
  cwd: process.cwd(),
  agentDir: "/custom/agent",
  settingsManager,
  systemPromptOverride: () => "You are a minimal assistant. Be concise.",
});
await loader.reload();

const { session } = await createAgentSession({
  cwd: process.cwd(),
  agentDir: "/custom/agent",
  model,
  thinkingLevel: "off",
  authStorage,
  modelRegistry,
  tools: [readTool, bashTool],
  customTools: [statusTool],
  resourceLoader: loader,
  sessionManager: SessionManager.inMemory(),
  settingsManager,
});

session.subscribe((event) => {
  if (
    event.type === "message_update" &&
    event.assistantMessageEvent.type === "text_delta"
  ) {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("Get status and list files.");