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
--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().
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();
runPrintMode
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.");