Skip to content
Letta Code Letta Code Letta Docs
Sign up
Letta Code SDK

Letta Code SDK

Build stateful agents using Letta Code as a library

The Letta Code SDK provides a programmatic interface to Letta Code. Use the Letta Code SDK to build your own applications that build on Letta Code’s advanced coding agent harness.

Terminal window
npm install @letta-ai/letta-code-sdk

For simple queries, use prompt():

import { prompt } from "@letta-ai/letta-code-sdk";
const result = await prompt("What is 2 + 2?");
console.log(result.result);

Persistent agent with multi-turn conversations

Section titled “Persistent agent with multi-turn conversations”

Create an agent to persist memory across sessions and application restarts:

import { createAgent, resumeSession } from "@letta-ai/letta-code-sdk";
// Create agent with custom configuration
const agentId = await createAgent({
persona: "You are a Python expert specialized in data science.",
});
console.log("Agent ID:", agentId); // Save this for later!
// Resume session on agent's default conversation
await using session = resumeSession(agentId);
// Turn 1
await session.send("What's the best way to load a CSV?");
for await (const msg of session.stream()) {
if (msg.type === "assistant") console.log(msg.content);
}
// Turn 2 - agent remembers previous context
await session.send("Show me an example with pandas");
for await (const msg of session.stream()) {
if (msg.type === "assistant") console.log(msg.content);
}
// Later: Resume the same agent using the saved ID
await using laterSession = resumeSession(agentId);
await laterSession.send("What were we discussing?");
for await (const msg of laterSession.stream()) {
if (msg.type === "assistant") console.log(msg.content);
}

The await using syntax (TypeScript 5.2+) automatically closes the session. For manual cleanup, use session.close().

Start a new conversation on the same agent:

import { createSession } from "@letta-ai/letta-code-sdk";
// New conversation
await using session = createSession(agentId);
await session.send("Let's start a new topic");
for await (const msg of session.stream()) {
if (msg.type === "assistant") console.log(msg.content);
}

Resume a specific conversation by ID:

import { resumeSession } from "@letta-ai/letta-code-sdk";
// Resume a conversation you saved earlier
await using session = resumeSession("conv-abc123");
await session.send("Continuing where we left off");
for await (const msg of session.stream()) {
if (msg.type === "assistant") console.log(msg.content);
}
  • Agent (agentId): A persistent entity with memory.
  • Conversation (conversationId): A message thread within an agent.
  • Session: A single execution/connection.
  • Default conversation: Always exists after createAgent(); use resumeSession(agentId).
  • Default agent: Most recently used agent or the default Memo agent from Letta Code; use createSession() or prompt(message)

Configure agents at creation time with memory blocks, system prompts, and initial context:

import { createAgent } from "@letta-ai/letta-code-sdk";
const agentId = await createAgent({
// Memory blocks define the agent's persistent context
memory: [
{ label: "persona", value: "You are a Python expert specialized in data science." },
{ label: "rules", value: "Always include type hints and docstrings." },
],
// Or use preset memory blocks
memory: ["persona", "human"], // Valid presets: persona, human
// Convenience shortcuts for common presets
persona: "You are a helpful coding assistant.",
human: "User prefers TypeScript and functional programming.",
});

Valid memory presets:

  • persona - Agent’s identity and behavior
  • human - Information about the user

Custom memory blocks:

const agentId = await createAgent({
memory: [
{ label: "project", value: "Building a REST API with Express and PostgreSQL..." },
{ label: "context", value: "Migrating from MongoDB to PostgreSQL..." },
],
});

Configure behavior at session creation or resume time:

import { createAgent, resumeSession } from "@letta-ai/letta-code-sdk";
const agentId = await createAgent();
await using session = resumeSession(agentId, {
// Update the agent's model (persists)
model: "claude-sonnet-4-5",
// Working directory for file operations
cwd: "/path/to/project",
// MemFS toggle for this run
// true -> --memfs, false -> --no-memfs, undefined -> leave unchanged
memfs: true,
// Skills/reminder controls
skillSources: ["project", "global"], // [] disables all skills
systemInfoReminder: false, // disable first-turn system info reminder
// Reflection settings (equivalent to /sleeptime)
sleeptime: {
trigger: "step-count", // "off" | "step-count" | "compaction-event"
behavior: "reminder", // "reminder" | "auto-launch"
stepCount: 8,
},
// Restrict available tools
allowedTools: ["Read", "Grep", "Glob"],
disallowedTools: ["AskUserQuestion", "EnterPlanMode", "ExitPlanMode"],
// Permission handling
permissionMode: "default", // "default" | "acceptEdits" | "plan" | "bypassPermissions"
// Custom permission callback
canUseTool: async (toolName, toolInput) => {
if (
toolName === "Bash" &&
typeof toolInput.command === "string" &&
toolInput.command.includes("rm")
) {
return { behavior: "deny", message: "Destructive commands not allowed" };
}
return { behavior: "allow" };
},
});

Interactive tools in non-interactive sessions

Section titled “Interactive tools in non-interactive sessions”

When running without a UI (for example, server-side SDK sessions):

  • EnterPlanMode can be auto-allowed by default.
  • AskUserQuestion and ExitPlanMode require runtime user input and are denied unless you provide a canUseTool callback.

For bot-style deployments, you can either:

  1. Provide canUseTool to handle these permission requests.
  2. Block the tools explicitly via disallowedTools (for example ["AskUserQuestion", "EnterPlanMode", "ExitPlanMode"]).

System prompts can be configured at agent creation time, or updated later when creating/resuming a session.

At agent creation (createAgent) you can use a custom string or a preset (optionally with append):

// Custom system prompt
const agentId = await createAgent({
systemPrompt: "You are a Python expert. Always use type hints.",
});
// Preset (with optional append)
const agentId2 = await createAgent({
systemPrompt: {
type: "preset",
preset: "letta-claude",
append: "Always respond in Spanish.",
},
});

At session time (createSession / resumeSession) you can only set a preset (no custom strings / no append):

await using session = resumeSession(agentId, {
systemPrompt: "letta-claude",
});

Note: Updating model or systemPrompt via createSession / resumeSession updates the agent in Letta Code (it persists), not a temporary per-session override.

Available presets:

  • letta-claude / default - Full coding assistant with memory
  • letta-codex - Optimized for OpenAI Codex models
  • letta-gemini - Optimized for Google Gemini models
  • claude, codex, gemini - Minimal prompts without Letta features

Sessions can be closed manually or automatically using await using (TypeScript 5.2+).

Automatic cleanup:

await using session = resumeSession(agentId);
// Session closes automatically when the block exits

Manual cleanup:

const session = resumeSession(agentId);
try {
// ... use session ...
} finally {
session.close();
}
FunctionDescription
createAgent(options?)Create a new persistent agent
createSession(agentId?, options?)Start a new conversation (uses default agent if no ID provided)
resumeSession(id, options?)Resume session (pass agent-xxx for default conversation, or conv-xxx for specific conversation)
prompt(message, agentId?)One-shot query (uses default agent if no ID provided)
interface Session {
send(message: string): Promise<void>;
stream(): AsyncGenerator<SDKMessage>;
close(): void;
readonly agentId: string;
readonly conversationId: string | null;
readonly sessionId: string;
}

Options for createAgent() - these configure the persistent agent:

interface CreateAgentOptions {
model?: string;
embedding?: string;
// Memory configuration (choose one approach)
memory?: Array<string | { label: string; value: string }>;
// Convenience shortcuts for common presets
persona?: string;
human?: string;
// System prompt (custom string or preset)
systemPrompt?: string | SystemPromptPreset | { type: "preset"; preset: SystemPromptPreset; append?: string };
// Runtime/harness controls available at creation
memfs?: boolean;
skillSources?: SkillSource[];
systemInfoReminder?: boolean;
sleeptime?: SleeptimeOptions;
}

Options for createSession() and resumeSession() - these configure runtime behavior:

interface CreateSessionOptions {
// Model configuration
model?: string;
// System prompt preset (updates the agent)
systemPrompt?: SystemPromptPreset;
// Tool restrictions
allowedTools?: string[];
disallowedTools?: string[];
permissionMode?: "default" | "acceptEdits" | "plan" | "bypassPermissions";
canUseTool?: (toolName: string, toolInput: object) => Promise<CanUseToolResponse>;
// File system
cwd?: string;
// Runtime/harness controls
memfs?: boolean; // true => --memfs, false => --no-memfs
skillSources?: SkillSource[]; // [] => --no-skills
systemInfoReminder?: boolean; // false => --no-system-info-reminder
sleeptime?: SleeptimeOptions;
}

Supporting types:

type SkillSource = "bundled" | "global" | "agent" | "project";
interface SleeptimeOptions {
trigger?: "off" | "step-count" | "compaction-event";
behavior?: "reminder" | "auto-launch";
stepCount?: number;
}

Just directly into code examples with demo app built on the Letta Code SDK: