---
title: Letta Code SDK | Letta Docs
description: Build stateful agents using Letta Code as a library
---

The [Letta Code SDK](https://github.com/letta-ai/letta-code-sdk/) provides a programmatic interface to [Letta Code](https://github.com/letta-ai/letta-code). Use the Letta Code SDK to build your own applications that build on Letta Code’s advanced coding agent harness.

Already familiar with the Claude Agent SDK? Jump to our [migration guide](/letta-code-sdk/migration/index.md).

## Installation

Terminal window

```
npm install @letta-ai/letta-code-sdk
```

## Quick start

### One-shot prompt

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

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`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) syntax (TypeScript 5.2+) automatically closes the session. For manual cleanup, use `session.close()`.

### Managing conversations

**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);
}
```

## Concepts

- **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)`

## Configuration

### Agent creation options

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..." },
  ],
});
```

### Runtime session options

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: "sonnet",


  // 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"],


  // Permission handling
  permissionMode: "standard", // "standard" | "acceptEdits" | "memory" | "unrestricted"


  // 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" };
  },
});
```

`allowedTools` / `disallowedTools` map to Letta Code’s permission flags. They control what is allowed to execute, but do not remove tools from the model’s tool list.

`skillSources` values are `"bundled" | "global" | "agent" | "project"`. Passing an empty list disables all skills.

`sleeptime.trigger: "compaction-event"` and `sleeptime.behavior: "auto-launch"` require MemFS enabled for the agent.

### Interactive tools in non-interactive sessions

When running without a UI (for example, server-side SDK sessions), tools that require runtime user input should be handled in `canUseTool` or blocked with `disallowedTools`.

For bot-style deployments, you can either:

1. Provide `canUseTool` to handle permission requests and user-input tools.
2. Block user-input tools explicitly via `disallowedTools` (for example `["AskUserQuestion"]`).

### System prompts

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:**

- `default` - Letta-tuned system prompt
- `letta-claude` - Full coding assistant with memory (Claude-optimized)
- `letta-codex` - Optimized for OpenAI Codex models
- `letta-gemini` - Optimized for Google Gemini models
- `claude`, `codex`, `gemini` - Minimal prompts without Letta features

### Cleanup

Sessions can be closed manually or automatically using [`await using`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) (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();
}
```

## API reference

### Core functions

| Function                            | Description                                                                                         |
| ----------------------------------- | --------------------------------------------------------------------------------------------------- |
| `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)                                               |

### Session interface

```
interface Session {
  send(message: string): Promise<void>;
  stream(): AsyncGenerator<SDKMessage>;
  close(): void;


  readonly agentId: string;
  readonly conversationId: string | null;
  readonly sessionId: string;
}
```

### CreateAgentOptions

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;
}
```

### CreateSessionOptions

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?: "standard" | "acceptEdits" | "memory" | "unrestricted";
  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;
}
```

## Example apps

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

[Channels ](/letta-code/channels/index.md)Connect your agent to Telegram, Slack, Discord, and WhatsApp for messaging from anywhere.

[Letta OSS UI ](https://github.com/letta-ai/letta-oss-ui)Like Claude Cowork, but fully open source and using stateful agents.
