Creating agents
Create agents with memory, configure skills and dreaming, and understand agent and conversation IDs
Create a persistent agent with client.createAgent(). It returns the agent’s ID as a string, which you use to open sessions and manage the agent later.
import { LettaAgentClient } from "@letta-ai/letta-agent-sdk";
const client = new LettaAgentClient({ backend: "cloud", apiKey: process.env.LETTA_API_KEY,});
const agentId = await client.createAgent({ model: "anthropic/claude-opus-4-8", persona: "You are Quinn, a digital research analyst who can inspect files, run commands, synthesize sources, and maintain living memory about the user's organization.", human: "The user prefers concise memos with evidence, caveats, and recommended next actions.",});// → "agent-3f97f111-3244-41a3-958f-63285dcb412d"Agents, conversations, and sessions
Section titled “Agents, conversations, and sessions”An agent is the persistent entity with memory. A conversation is a thread on that agent. A session is the active connection you use to send messages and stream events.
- Agent IDs look like
agent-xxxon the cloud backend andagent-local-xxxon the local backend. Conversation IDs look likeconv-xxx. createAgent()also creates the agent’s default conversation, so a new agent is immediately usable.createSession(agentId)starts a new conversation on an existing agent.resumeSession(id)accepts either kind of ID: pass anagent-xxxID to resume the agent’s default conversation, or aconv-xxxID to resume a specific conversation.
// New conversation on an existing agentawait using session = client.createSession(agentId);
// Later: save the thread and pick it up againconst conversationId = session.conversationId;// → "conv-cff81390-0763-431d-b24e-b218ae37ec81"await using resumed = client.resumeSession(conversationId);
// Or resume the agent's default conversationawait using main = client.resumeSession(agentId);Sessions expose read-only agentId, conversationId, and sessionId values once the backend resolves them. You can also create and inspect conversations without opening a session through client.conversations (create, list, retrieve, update, listMessages).
Memory
Section titled “Memory”Persona and human shorthand
Section titled “Persona and human shorthand”persona and human are conveniences that set the agent’s persona and human memory blocks:
const agentId = await client.createAgent({ persona: "You are a resident engineering teammate for this repository.", human: "The user prefers practical handoffs with commands to run.",});Memory blocks
Section titled “Memory blocks”Pass memory for full control. Each item is either a new block or a reference to an existing shared block:
const agentId = await client.createAgent({ model: "anthropic/claude-opus-4-8", memory: [ { label: "persona", value: "You are Quinn, a digital research analyst.", }, { label: "team-context", value: "The team ships weekly on Thursdays. Staging is at stage.example.com.", }, { blockId: "block-abc123" }, // attach an existing shared block ],});Use object blocks with label and value; current creation backends reject memory preset names. See Shared memory for sharing files and working context between Cloud agents.
Every agent has a MemFS, the git-backed memory filesystem where it stores long-term memory as Markdown files. It is enabled by default; pass memfs: false for worker-style agents that do not need durable memory (enabling MemFS adds a backend round trip at creation, and concurrent sessions on a shared-MemFS agent contend on its git state).
Where MemFS lives depends on the backend:
| Backend | MemFS location |
|---|---|
local (default) | ~/.letta/lc-local-backend/memfs/<agentId>/memory on the current machine |
local with appServer.harnessBackend: "api" | ~/.letta/agents/<agentId>/memory on the current machine |
cloud | Hosted in Letta Cloud; checked out on the computer executing the session |
On the local backend, override the root with the MEMORY_DIR or LETTA_MEMORY_DIR environment variable via the session’s env option. On the cloud backend, find the checkout path on the executing computer with (await session.getDeviceStatus()).memoryDirectory.
Skills
Section titled “Skills”Skills are packaged instructions the agent can load for particular kinds of tasks. A skill is a directory containing a SKILL.md with frontmatter (name, description), optionally alongside scripts/, references/, and templates/.
Skill sources
Section titled “Skill sources”Control which sources a session exposes with skillSources, at agent creation or per session:
type SkillSource = "bundled" | "global" | "agent" | "project";
const agentId = await client.createAgent({ skillSources: ["bundled", "agent"],});
// Or per session; pass [] to disable skills entirelyawait using session = client.createSession(agentId, { skillSources: [],});Each source loads from a specific place on the machine running the harness:
| Source | Location | Lifetime and scope |
|---|---|---|
bundled | Shipped with the harness package | Read-only; excluded skills vary by backend |
global | ~/.letta/skills/ | All agents on that machine |
agent | The agent’s MemFS under skills/ | Travels with the agent; versioned; syncs for cloud agents |
project | <cwd>/.agents/skills/ (and legacy <cwd>/.skills/) | Anything working in that directory |
When the same skill ID appears in multiple sources, the more specific one wins: project > agent > global > bundled.
Give an agent its own skills
Section titled “Give an agent its own skills”Agent-owned skills live in the agent’s MemFS under skills/<name>/SKILL.md, so they are versioned with the agent’s memory and follow it across machines. Install one from GitHub, a registry, or a URL with the Letta CLI:
# GitHub repo, tree URL, SKILL.md URL, or owner/repo/path shorthandletta skills install anthropics/skills/pdf --agent agent-abc123
# ClawHub registryletta skills install clawhub/nano-banana-pro --agent agent-abc123The install is committed to the agent’s MemFS git repository and, for cloud agents, synced back to Letta. Agents can also install skills for themselves (the bundled acquiring-skills skill teaches them how), or simply write skills/<name>/SKILL.md into their memory directly. New skills are discovered on the next message.
Use skills from npx skills or your repository
Section titled “Use skills from npx skills or your repository”Vercel’s npx skills add installs skills into the cross-tool .agents/skills/ project directory — which is exactly what Letta’s project source reads, so those skills work in sessions whose cwd is that project with no extra configuration:
npx skills add vercel-labs/agent-skillsThe same applies to skills you commit to a repository yourself: put them under .agents/skills/<name>/SKILL.md and any session working in that checkout picks them up. Letta does not read other tools’ agent-specific directories (.claude/skills/, ~/.config/agents/skills/ — where npx skills add -g installs), so use the universal .agents/skills/ project directory, or copy to ~/.letta/skills/ for machine-wide skills.
Dreaming
Section titled “Dreaming”Dreaming uses background subagents to review recent conversations, consolidate lessons, and update memory without interrupting active work. Configure it with dreaming:
const agentId = await client.createAgent({ persona: "You are a support engineer who learns each customer's environment.", dreaming: { trigger: "step-count", // "off" | "step-count" | "compaction-event" behavior: "auto-launch", // "reminder" | "auto-launch" stepCount: 25, },});triggercontrols when dreaming runs: after a number of steps, on context compaction, or never.behaviorcontrols what happens at the trigger: remind the agent to update memory, or automatically launch a background dreaming subagent. It can only be set at agent creation.stepCountis the step interval for the"step-count"trigger.
Sessions can override trigger and stepCount (but not behavior) with the session dreaming option. The init message reports the effective settings for a session.
Other creation options
Section titled “Other creation options”const agentId = await client.createAgent({ name: "quinn-research", description: "Research analyst for the growth team", tags: ["research", "growth"], hidden: true, // worker/subagent semantics: hidden from default listings baseTools: ["web_search"], // server-side tools; defaults to web_search and fetch_webpage, [] for none cwd: "/workspace/project",});System prompts
Section titled “System prompts”Keep the default system prompt for most agents. It teaches the agent how to use the harness, including tools and memory. Customize persona, human, and MemFS instead.
Set systemPrompt only when your application needs a deliberately different operating model — for example, a narrow pipeline worker that should not behave like a stateful assistant at all:
const agentId = await client.createAgent({ model: "anthropic/claude-opus-4-8", memfs: false, systemPrompt: 'You are a support-ticket triage classifier. For every message, respond with only a JSON object of the form {"category": string, "severity": "low" | "medium" | "high"}. Do not use tools. Do not add prose.',});If you find yourself writing a systemPrompt that describes who the agent is or how it should communicate, that belongs in the persona memory block instead.
createAgent() currently accepts custom system prompt strings, not preset names. systemPrompt and disallowedTools can only be set at agent creation; passing them to createSession() or resumeSession() throws.
Manage existing agents
Section titled “Manage existing agents”Use client.agents to work with agents outside a session:
const agents = await client.agents.list();const agent = await client.agents.retrieve(agentId);// → { id: "agent-3f97f111-…", name: "agent-d41dd2a0", created_at: "2026-08-11T07:01:45.195Z", … }await client.agents.update(agentId, { description: "Updated description" });await client.agents.delete(agentId);