fix: memory blocks config on createAgent (#21)

This commit is contained in:
Christina Tong
2026-02-03 16:44:05 -08:00
committed by GitHub
parent 4cb8af7be8
commit 5f37e513c8
5 changed files with 41 additions and 100 deletions

View File

@@ -45,9 +45,8 @@ import { createAgent, resumeSession } from '@letta-ai/letta-code-sdk';
// Create an agent with custom memory (has default conversation)
const agentId = await createAgent({
memory: ['persona', 'project'],
persona: 'You are a helpful coding assistant',
project: 'A TypeScript web application'
memory: ['persona'],
persona: 'You are a helpful coding assistant for TypeScript projects'
});
// Resume the default conversation
@@ -160,62 +159,7 @@ createSession(agentId, {
- `codex` - Basic Codex
- `gemini` - Basic Gemini
### Memory Blocks
Configure which memory blocks the session uses:
```typescript
// Use default blocks (persona, human, project)
createSession(agentId);
// Use specific preset blocks
createSession(agentId, {
memory: ['project', 'persona'] // Only these blocks
});
// Use custom blocks
createSession(agentId, {
memory: [
{ label: 'context', value: 'API documentation for Acme Corp...' },
{ label: 'rules', value: 'Always use TypeScript. Prefer functional patterns.' }
]
});
// No optional blocks (only core skills blocks)
createSession(agentId, {
memory: []
});
```
### Convenience Props
Quickly customize common memory blocks:
```typescript
createSession(agentId, {
persona: 'You are a senior Python developer who writes clean, tested code.',
human: 'Name: Alice. Prefers concise responses.',
project: 'FastAPI backend for a todo app using PostgreSQL.'
});
```
### Tool Execution
Execute tools with automatic permission handling:
```typescript
import { createAgent, createSession } from '@letta-ai/letta-code-sdk';
// Create agent and run commands
const agentId = await createAgent();
const session = createSession(agentId, {
allowedTools: ['Glob', 'Bash'],
permissionMode: 'bypassPermissions',
cwd: '/path/to/project'
});
await session.send('List all TypeScript files');
for await (const msg of session.stream()) { /* ... */ }
```
## API Reference
@@ -251,9 +195,10 @@ await createAgent();
await createAgent({
model: 'claude-sonnet-4',
systemPrompt: 'You are a helpful Python expert.',
memory: ['persona', 'project'],
persona: 'You are a senior Python developer',
project: 'FastAPI backend for a todo app'
memory: [
{ label: 'persona', value: 'You are a senior Python developer' },
{ label: 'project', value: 'FastAPI backend for a todo app' }
]
});
```

View File

@@ -557,8 +557,8 @@ async function testMemoryConfig() {
// Test 2: Specific preset blocks only
console.log('Testing specific preset blocks...');
const specificResult = await runWithMemory('List your memory block labels', ['project']);
console.log(` specific blocks [project]: ${specificResult.success ? 'PASS' : 'FAIL'}`);
const specificResult = await runWithMemory('List your memory block labels', ['persona']);
console.log(` specific blocks [persona]: ${specificResult.success ? 'PASS' : 'FAIL'}`);
// Test 3: Custom blocks
console.log('Testing custom memory blocks...');
@@ -570,13 +570,13 @@ async function testMemoryConfig() {
console.log(` custom blocks: ${customResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response is concise: ${isConcise ? 'yes' : 'check'}`);
// Test 4: Mixed preset and custom blocks
console.log('Testing mixed blocks (preset + custom)...');
const mixedResult = await runWithMemory(
// Test 4: Multiple preset blocks
console.log('Testing multiple preset blocks...');
const multipleResult = await runWithMemory(
'List your memory blocks',
['project', { label: 'custom-context', value: 'This is a test context block.' }]
['persona', 'human']
);
console.log(` mixed blocks: ${mixedResult.success ? 'PASS' : 'FAIL'}`);
console.log(` multiple presets: ${multipleResult.success ? 'PASS' : 'FAIL'}`);
// Test 5: Empty memory (core blocks only)
console.log('Testing empty memory (core only)...');
@@ -620,16 +620,16 @@ async function testConvenienceProps() {
console.log(` persona: ${personaResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions cooking/Italian: ${hasItalian ? 'yes' : 'check'}`);
// Test 2: project prop
console.log('Testing project prop...');
// Test 2: project block (custom)
console.log('Testing project block (custom)...');
const projectResult = await runWithProps(
'What project are you helping with?',
{ project: 'A React Native mobile app for tracking daily habits.' }
{ memory: [{ label: 'project', value: 'A React Native mobile app for tracking daily habits.' }] }
);
const hasProject = projectResult.result.toLowerCase().includes('react') ||
projectResult.result.toLowerCase().includes('habit') ||
projectResult.result.toLowerCase().includes('mobile');
console.log(` project: ${projectResult.success ? 'PASS' : 'FAIL'}`);
console.log(` project (custom): ${projectResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions project: ${hasProject ? 'yes' : 'check'}`);
// Test 3: human prop
@@ -646,8 +646,12 @@ async function testConvenienceProps() {
// Test 4: Multiple convenience props together
console.log('Testing multiple convenience props...');
const multiResult = await runWithProps(
'Introduce yourself and the project briefly',
{ persona: 'You are a friendly code reviewer.', project: 'FastAPI backend service.', human: 'Name: Alice.' }
'Introduce yourself briefly',
{
memory: ['persona', 'human'],
persona: 'You are a friendly code reviewer.',
human: 'Name: Alice.'
}
);
console.log(` multiple props: ${multiResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response: ${multiResult.result.slice(0, 100)}...`);
@@ -656,7 +660,7 @@ async function testConvenienceProps() {
console.log('Testing convenience props with memory config...');
const combinedResult = await runWithProps(
'What is in your persona block?',
{ memory: ['persona', 'project'], persona: 'You are a database expert specializing in PostgreSQL.' }
{ memory: ['persona'], persona: 'You are a database expert specializing in PostgreSQL.' }
);
const hasDB = combinedResult.result.toLowerCase().includes('database') ||
combinedResult.result.toLowerCase().includes('postgresql');

View File

@@ -258,14 +258,20 @@ export class SubprocessTransport {
}
}
// Add preset names via --init-blocks
if (presetNames.length > 0) {
args.push("--init-blocks", presetNames.join(","));
}
// Add custom blocks and block references via --memory-blocks
// NOTE: When custom blocks are provided via --memory-blocks, they define the complete
// memory configuration. Preset blocks (--init-blocks) cannot be mixed with custom blocks.
if (memoryBlocksJson.length > 0) {
// Use custom blocks only
args.push("--memory-blocks", JSON.stringify(memoryBlocksJson));
if (presetNames.length > 0) {
console.warn(
"[letta-code-sdk] Using custom memory blocks. " +
`Preset blocks are ignored when custom blocks are provided: ${presetNames.join(", ")}`
);
}
} else if (presetNames.length > 0) {
// Use presets only
args.push("--init-blocks", presetNames.join(","));
}
}
}
@@ -278,9 +284,6 @@ export class SubprocessTransport {
if (this.options.human !== undefined) {
args.push("--block-value", `human=${this.options.human}`);
}
if (this.options.project !== undefined) {
args.push("--block-value", `project=${this.options.project}`);
}
}
// Permission mode

View File

@@ -113,7 +113,7 @@ export type MemoryItem =
/**
* Default memory block preset names.
*/
export type MemoryPreset = "persona" | "human" | "project";
export type MemoryPreset = "persona" | "human" | "skills" | "loaded_skills";
// ═══════════════════════════════════════════════════════════════
// SESSION OPTIONS
@@ -147,9 +147,8 @@ export interface InternalSessionOptions {
// Memory blocks (only for new agents)
memory?: MemoryItem[];
persona?: string;
human?: string;
project?: string;
persona?: string; // Convenience for persona block
human?: string; // Convenience for human block
// Permissions
allowedTools?: string[];
@@ -203,8 +202,8 @@ export interface CreateAgentOptions {
/**
* Memory block configuration. Each item can be:
* - string: Preset block name ("project", "persona", "human")
* - CreateBlock: Custom block definition
* - string: Preset block name ("persona", "human", "skills", "loaded_skills")
* - CreateBlock: Custom block definition (e.g., { label: "project", value: "..." })
* - { blockId: string }: Reference to existing shared block
*/
memory?: MemoryItem[];
@@ -215,9 +214,6 @@ export interface CreateAgentOptions {
/** Convenience: Set human block value directly */
human?: string;
/** Convenience: Set project block value directly */
project?: string;
/** List of allowed tool names */
allowedTools?: string[];

View File

@@ -77,13 +77,6 @@ export function validateCreateAgentOptions(options: CreateAgentOptions): void {
"Either add 'human' to memory array or remove the human option."
);
}
if (options.project !== undefined && !blockLabels.includes("project")) {
throw new Error(
"Cannot set 'project' value - block not included in 'memory'. " +
"Either add 'project' to memory array or remove the project option."
);
}
}
// Validate systemPrompt preset if provided as preset object