Files
letta-code-sdk/examples/v2-examples.ts
2026-02-03 16:44:05 -08:00

844 lines
32 KiB
TypeScript

#!/usr/bin/env bun
/**
* Letta Code SDK V2 Examples
*
* Comprehensive tests for all SDK features.
*
* Run with: bun examples/v2-examples.ts [example]
*/
import { createAgent, createSession, resumeSession, prompt } from '../src/index.js';
async function main() {
const example = process.argv[2] || 'basic';
switch (example) {
case 'basic':
await basicSession();
break;
case 'multi-turn':
await multiTurn();
break;
case 'one-shot':
await oneShot();
break;
case 'resume':
await sessionResume();
break;
case 'options':
await testOptions();
break;
case 'message-types':
await testMessageTypes();
break;
case 'session-properties':
await testSessionProperties();
break;
case 'tool-execution':
await testToolExecution();
break;
case 'permission-callback':
await testPermissionCallback();
break;
case 'system-prompt':
await testSystemPrompt();
break;
case 'memory-config':
await testMemoryConfig();
break;
case 'convenience-props':
await testConvenienceProps();
break;
case 'conversations':
await testConversations();
break;
case 'all':
await basicSession();
await multiTurn();
await oneShot();
await sessionResume();
await testOptions();
await testMessageTypes();
await testSessionProperties();
await testToolExecution();
await testPermissionCallback();
await testSystemPrompt();
await testMemoryConfig();
await testConvenienceProps();
await testConversations();
console.log('\n✅ All examples passed');
break;
default:
console.log('Usage: bun v2-examples.ts [basic|multi-turn|one-shot|resume|options|message-types|session-properties|tool-execution|permission-callback|system-prompt|memory-config|convenience-props|conversations|all]');
}
}
// ═══════════════════════════════════════════════════════════════
// BASIC EXAMPLES
// ═══════════════════════════════════════════════════════════════
// Basic session with send/receive pattern
async function basicSession() {
console.log('=== Basic Session ===\n');
// Create agent, then resume default conversation
const agentId = await createAgent();
await using session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('Hello! Introduce yourself in one sentence.');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') {
response += msg.content;
}
if (msg.type === 'result') {
console.log(`Letta: ${response.trim()}`);
console.log(`[Result: ${msg.success ? 'success' : 'failed'}, ${msg.durationMs}ms]`);
}
}
console.log();
}
// Multi-turn conversation
async function multiTurn() {
console.log('=== Multi-Turn Conversation ===\n');
// Create new agent + new conversation
await using session = createSession(undefined, {
model: 'haiku',
permissionMode: 'bypassPermissions',
});
// Turn 1
await session.send('What is 5 + 3? Just the number.');
let turn1Result = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') turn1Result += msg.content;
}
console.log(`Turn 1: ${turn1Result.trim()}`);
// Turn 2 - agent remembers context
await session.send('Multiply that by 2. Just the number.');
let turn2Result = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') turn2Result += msg.content;
}
console.log(`Turn 2: ${turn2Result.trim()}`);
console.log();
}
// One-shot convenience function
async function oneShot() {
console.log('=== One-Shot Prompt ===\n');
// One-shot creates new agent
const result = await prompt('What is the capital of France? One word.');
if (result.success) {
console.log(`Answer: ${result.result}`);
console.log(`Duration: ${result.durationMs}ms`);
} else {
console.log(`Error: ${result.error}`);
}
console.log();
}
// Session resume - with PERSISTENT MEMORY
async function sessionResume() {
console.log('=== Session Resume (Persistent Memory) ===\n');
// Create agent first
const agentId = await createAgent();
console.log(`[Setup] Created agent: ${agentId}\n`);
// First session - establish a memory
{
const session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
console.log('[Session 1] Teaching agent a secret word...');
await session.send('Remember this secret word: "pineapple". Store it in your memory.');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') response += msg.content;
if (msg.type === 'result') {
console.log(`[Session 1] Agent: ${response.trim()}`);
}
}
console.log(`[Session 1] Agent ID: ${session.agentId}\n`);
session.close();
}
console.log('--- Session closed. Agent persists on server. ---\n');
// Resume and verify agent remembers
{
await using session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
console.log('[Session 2] Asking agent for the secret word...');
await session.send('What is the secret word I told you to remember?');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') response += msg.content;
if (msg.type === 'result') {
console.log(`[Session 2] Agent: ${response.trim()}`);
}
}
}
console.log();
}
// ═══════════════════════════════════════════════════════════════
// OPTIONS TESTS
// ═══════════════════════════════════════════════════════════════
async function testOptions() {
console.log('=== Testing Options ===\n');
// Test basic session
console.log('Testing basic session...');
const agentId = await createAgent();
const modelResult = await prompt('Say "model test ok"', agentId);
console.log(` basic: ${modelResult.success ? 'PASS' : 'FAIL'} - ${modelResult.result?.slice(0, 50)}`);
// Test systemPrompt preset via createSession (only presets allowed)
console.log('Testing systemPrompt preset...');
const sysPromptSession = createSession(undefined, {
systemPrompt: 'letta-claude',
permissionMode: 'bypassPermissions',
});
await sysPromptSession.send('Hello, what kind of agent are you?');
let sysPromptResponse = '';
for await (const msg of sysPromptSession.stream()) {
if (msg.type === 'result') sysPromptResponse = msg.result || '';
}
sysPromptSession.close();
console.log(` systemPrompt preset: ${sysPromptResponse ? 'PASS' : 'FAIL'} - ${sysPromptResponse.slice(0, 80)}`);
// Test cwd option via createSession
console.log('Testing cwd option...');
const cwdSession = createSession(undefined, {
cwd: '/tmp',
allowedTools: ['Bash'],
permissionMode: 'bypassPermissions',
});
await cwdSession.send('Run pwd to show current directory');
let cwdResponse = '';
for await (const msg of cwdSession.stream()) {
if (msg.type === 'result') cwdResponse = msg.result || '';
}
cwdSession.close();
const hasTmp = cwdResponse.includes('/tmp');
console.log(` cwd: ${hasTmp ? 'PASS' : 'CHECK'} - ${cwdResponse.slice(0, 60)}`);
// Test allowedTools option with tool execution
console.log('Testing allowedTools option...');
const toolsSession = createSession(undefined, {
allowedTools: ['Bash'],
permissionMode: 'bypassPermissions',
});
await toolsSession.send('Run: echo tool-test-ok');
let toolsResponse = '';
for await (const msg of toolsSession.stream()) {
if (msg.type === 'result') toolsResponse = msg.result || '';
}
toolsSession.close();
const hasToolOutput = toolsResponse.includes('tool-test-ok');
console.log(` allowedTools: ${hasToolOutput ? 'PASS' : 'CHECK'} - ${toolsResponse.slice(0, 60)}`);
// Test permissionMode: bypassPermissions
console.log('Testing permissionMode: bypassPermissions...');
const bypassSession = createSession(undefined, {
allowedTools: ['Bash'],
permissionMode: 'bypassPermissions',
});
await bypassSession.send('Run: echo bypass-test');
let bypassResponse = '';
for await (const msg of bypassSession.stream()) {
if (msg.type === 'result') bypassResponse = msg.result || '';
}
bypassSession.close();
const hasBypassOutput = bypassResponse.includes('bypass-test');
console.log(` permissionMode: ${hasBypassOutput ? 'PASS' : 'CHECK'}`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// MESSAGE TYPES TESTS
// ═══════════════════════════════════════════════════════════════
async function testMessageTypes() {
console.log('=== Testing Message Types ===\n');
const agentId = await createAgent();
const session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('Say "hello" exactly');
let sawAssistant = false;
let sawResult = false;
let assistantContent = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') {
sawAssistant = true;
assistantContent += msg.content;
// Verify assistant message has uuid
if (!msg.uuid) {
console.log(' assistant.uuid: FAIL - missing uuid');
}
}
if (msg.type === 'result') {
sawResult = true;
// Verify result message structure
const hasSuccess = typeof msg.success === 'boolean';
const hasDuration = typeof msg.durationMs === 'number';
console.log(` result.success: ${hasSuccess ? 'PASS' : 'FAIL'}`);
console.log(` result.durationMs: ${hasDuration ? 'PASS' : 'FAIL'}`);
console.log(` result.result: ${msg.result ? 'PASS' : 'FAIL (empty)'}`);
}
}
console.log(` assistant message received: ${sawAssistant ? 'PASS' : 'FAIL'}`);
console.log(` result message received: ${sawResult ? 'PASS' : 'FAIL'}`);
session.close();
console.log();
}
// ═══════════════════════════════════════════════════════════════
// SESSION PROPERTIES TESTS
// ═══════════════════════════════════════════════════════════════
async function testSessionProperties() {
console.log('=== Testing Session Properties ===\n');
// Create new agent + new conversation
const session = createSession(undefined, {
model: 'haiku',
permissionMode: 'bypassPermissions',
});
// Before send - properties should be null
console.log(` agentId before send: ${session.agentId === null ? 'PASS (null)' : 'FAIL'}`);
console.log(` sessionId before send: ${session.sessionId === null ? 'PASS (null)' : 'FAIL'}`);
await session.send('Hi');
for await (const _ of session.stream()) {
// drain
}
// After send - properties should be set
const hasAgentId = session.agentId !== null && session.agentId.startsWith('agent-');
const hasSessionId = session.sessionId !== null;
console.log(` agentId after send: ${hasAgentId ? 'PASS' : 'FAIL'} - ${session.agentId}`);
console.log(` sessionId after send: ${hasSessionId ? 'PASS' : 'FAIL'} - ${session.sessionId}`);
// Test close()
session.close();
console.log(` close(): PASS (no error)`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// TOOL EXECUTION TESTS
// ═══════════════════════════════════════════════════════════════
async function testToolExecution() {
console.log('=== Testing Tool Execution ===\n');
// Create a shared agent for tool tests
const agentId = await createAgent();
async function runWithTools(message: string, tools: string[]): Promise<string> {
const session = createSession(agentId, {
allowedTools: tools,
permissionMode: 'bypassPermissions',
});
await session.send(message);
let result = '';
for await (const msg of session.stream()) {
if (msg.type === 'result') result = msg.result || '';
}
session.close();
return result;
}
// Test 1: Basic command execution
console.log('Testing basic command execution...');
const echoResult = await runWithTools('Run: echo hello-world', ['Bash']);
const hasHello = echoResult.includes('hello-world');
console.log(` echo command: ${hasHello ? 'PASS' : 'FAIL'}`);
// Test 2: Command with arguments
console.log('Testing command with arguments...');
const argsResult = await runWithTools('Run: echo "arg1 arg2 arg3"', ['Bash']);
const hasArgs = argsResult.includes('arg1') && argsResult.includes('arg3');
console.log(` echo with args: ${hasArgs ? 'PASS' : 'FAIL'}`);
// Test 3: File reading with Glob
console.log('Testing Glob tool...');
const globResult = await runWithTools('List all .ts files in the current directory using Glob', ['Glob']);
console.log(` Glob tool: ${globResult ? 'PASS' : 'FAIL'}`);
// Test 4: Multi-step tool usage (agent decides which tools to use)
console.log('Testing multi-step tool usage...');
const multiResult = await runWithTools('First run "echo step1", then run "echo step2". Show me both outputs.', ['Bash']);
const hasStep1 = multiResult.includes('step1');
const hasStep2 = multiResult.includes('step2');
console.log(` multi-step: ${hasStep1 && hasStep2 ? 'PASS' : 'PARTIAL'} (step1: ${hasStep1}, step2: ${hasStep2})`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// PERMISSION CALLBACK TESTS
// ═══════════════════════════════════════════════════════════════
async function testPermissionCallback() {
console.log('=== Testing Permission Callback ===\n');
// Note: permissionMode 'default' with NO allowedTools triggers callback
// allowedTools auto-allows tools, bypassing the callback
// Test 1: Allow specific commands via callback
console.log('Testing canUseTool callback (allow)...');
const allowSession = createSession(undefined, {
// NO allowedTools - this ensures callback is invoked
permissionMode: 'default',
canUseTool: async (toolName, toolInput) => {
console.error('CALLBACK:', toolName, toolInput);
const command = (toolInput as { command?: string }).command || '';
if (command.includes('callback-allowed')) {
return { behavior: 'allow', updatedInput: null };
}
return { behavior: 'deny', message: 'Command not whitelisted' };
},
});
await allowSession.send('Run: echo callback-allowed');
let allowResult = '';
for await (const msg of allowSession.stream()) {
if (msg.type === 'result') allowResult = msg.result || '';
}
allowSession.close();
const hasAllowed = allowResult.includes('callback-allowed');
console.log(` allow via callback: ${hasAllowed ? 'PASS' : 'FAIL'}`);
// Test 2: Deny specific commands via callback
console.log('Testing canUseTool callback (deny)...');
const denySession = createSession(undefined, {
permissionMode: 'default',
canUseTool: async (toolName, toolInput) => {
const command = (toolInput as { command?: string }).command || '';
if (command.includes('dangerous')) {
return { behavior: 'deny', message: 'Dangerous command blocked' };
}
return { behavior: 'allow', updatedInput: null };
},
});
await denySession.send('Run: echo dangerous-command');
let denyResult = '';
for await (const msg of denySession.stream()) {
if (msg.type === 'result') denyResult = msg.result || '';
}
denySession.close();
// Agent should report that it couldn't execute the command
const wasDenied = !denyResult.includes('dangerous-command');
console.log(` deny via callback: ${wasDenied ? 'PASS' : 'CHECK'}`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// SYSTEM PROMPT TESTS
// ═══════════════════════════════════════════════════════════════
async function testSystemPrompt() {
console.log('=== Testing System Prompt Configuration ===\n');
async function runWithSystemPrompt(msg: string, systemPrompt: any): Promise<string> {
const agentId = await createAgent({ systemPrompt });
const session = resumeSession(agentId, { permissionMode: 'bypassPermissions' });
await session.send(msg);
let result = '';
for await (const m of session.stream()) {
if (m.type === 'result') result = m.result || '';
}
session.close();
return result;
}
// Test 1: Preset system prompt
console.log('Testing preset system prompt...');
const presetResult = await runWithSystemPrompt(
'What kind of agent are you? One sentence.',
{ type: 'preset', preset: 'letta-claude' }
);
console.log(` preset (letta-claude): ${presetResult ? 'PASS' : 'FAIL'}`);
console.log(` Response: ${presetResult.slice(0, 80)}...`);
// Test 2: Preset with append
console.log('Testing preset with append...');
const appendResult = await runWithSystemPrompt(
'Say hello',
{ type: 'preset', preset: 'letta-claude', append: 'Always end your responses with "🎉"' }
);
const hasEmoji = appendResult.includes('🎉');
console.log(` preset with append: ${hasEmoji ? 'PASS' : 'CHECK'}`);
console.log(` Response: ${appendResult.slice(0, 80)}...`);
// Test 3: Custom string system prompt
console.log('Testing custom string system prompt...');
const customResult = await runWithSystemPrompt(
'What is your specialty?',
'You are a pirate captain. Always speak like a pirate.'
);
const hasPirateSpeak = customResult.toLowerCase().includes('arr') ||
customResult.toLowerCase().includes('matey') ||
customResult.toLowerCase().includes('ship');
console.log(` custom string: ${customResult ? 'PASS' : 'FAIL'}`);
console.log(` Response: ${customResult.slice(0, 80)}...`);
// Test 4: Basic preset (claude - no skills/memory)
console.log('Testing basic preset (claude)...');
const basicResult = await runWithSystemPrompt(
'Hello, just say hi back',
{ type: 'preset', preset: 'claude' }
);
console.log(` basic preset (claude): ${basicResult ? 'PASS' : 'FAIL'}`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// MEMORY CONFIGURATION TESTS
// ═══════════════════════════════════════════════════════════════
async function testMemoryConfig() {
console.log('=== Testing Memory Configuration ===\n');
async function runWithMemory(msg: string, memory?: any[]): Promise<{ success: boolean; result: string }> {
const agentId = await createAgent({ memory });
const session = resumeSession(agentId, { permissionMode: 'bypassPermissions' });
await session.send(msg);
let result = '';
let success = false;
for await (const m of session.stream()) {
if (m.type === 'result') {
result = m.result || '';
success = m.success;
}
}
session.close();
return { success, result };
}
// Test 1: Default memory (persona, human, project)
console.log('Testing default memory blocks...');
const defaultResult = await runWithMemory('What memory blocks do you have? List their labels.');
const hasDefaultBlocks = defaultResult.result.includes('persona') ||
defaultResult.result.includes('project');
console.log(` default blocks: ${defaultResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions blocks: ${hasDefaultBlocks ? 'yes' : 'check manually'}`);
// Test 2: Specific preset blocks only
console.log('Testing specific preset blocks...');
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...');
const customResult = await runWithMemory(
'What does your "rules" memory block say?',
[{ label: 'rules', value: 'Always be concise. Never use more than 10 words.' }]
);
const isConcise = (customResult.result.split(' ').length || 0) < 20;
console.log(` custom blocks: ${customResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response is concise: ${isConcise ? 'yes' : 'check'}`);
// Test 4: Multiple preset blocks
console.log('Testing multiple preset blocks...');
const multipleResult = await runWithMemory(
'List your memory blocks',
['persona', 'human']
);
console.log(` multiple presets: ${multipleResult.success ? 'PASS' : 'FAIL'}`);
// Test 5: Empty memory (core blocks only)
console.log('Testing empty memory (core only)...');
const emptyResult = await runWithMemory('Hello', []);
console.log(` empty memory: ${emptyResult.success ? 'PASS' : 'FAIL'}`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// CONVENIENCE PROPS TESTS
// ═══════════════════════════════════════════════════════════════
async function testConvenienceProps() {
console.log('=== Testing Convenience Props ===\n');
async function runWithProps(msg: string, props: Record<string, any>): Promise<{ success: boolean; result: string }> {
const agentId = await createAgent(props);
const session = resumeSession(agentId, { permissionMode: 'bypassPermissions' });
await session.send(msg);
let result = '';
let success = false;
for await (const m of session.stream()) {
if (m.type === 'result') {
result = m.result || '';
success = m.success;
}
}
session.close();
return { success, result };
}
// Test 1: persona prop
console.log('Testing persona prop...');
const personaResult = await runWithProps(
'Describe your personality in one sentence',
{ persona: 'You are an enthusiastic cooking assistant who loves Italian food.' }
);
const hasItalian = personaResult.result.toLowerCase().includes('italian') ||
personaResult.result.toLowerCase().includes('cook');
console.log(` persona: ${personaResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions cooking/Italian: ${hasItalian ? 'yes' : 'check'}`);
// Test 2: project block (custom)
console.log('Testing project block (custom)...');
const projectResult = await runWithProps(
'What project are you helping with?',
{ 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 (custom): ${projectResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions project: ${hasProject ? 'yes' : 'check'}`);
// Test 3: human prop
console.log('Testing human prop...');
const humanResult = await runWithProps(
'What do you know about me?',
{ human: 'Name: Bob. Senior developer. Prefers TypeScript over JavaScript.' }
);
const hasHuman = humanResult.result.toLowerCase().includes('bob') ||
humanResult.result.toLowerCase().includes('typescript');
console.log(` human: ${humanResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions user info: ${hasHuman ? 'yes' : 'check'}`);
// Test 4: Multiple convenience props together
console.log('Testing multiple convenience props...');
const multiResult = await runWithProps(
'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)}...`);
// Test 5: Convenience props with specific memory blocks
console.log('Testing convenience props with memory config...');
const combinedResult = await runWithProps(
'What is in your persona block?',
{ memory: ['persona'], persona: 'You are a database expert specializing in PostgreSQL.' }
);
const hasDB = combinedResult.result.toLowerCase().includes('database') ||
combinedResult.result.toLowerCase().includes('postgresql');
console.log(` props with memory: ${combinedResult.success ? 'PASS' : 'FAIL'}`);
console.log(` Response mentions DB: ${hasDB ? 'yes' : 'check'}`);
console.log();
}
// ═══════════════════════════════════════════════════════════════
// CONVERSATION TESTS
// ═══════════════════════════════════════════════════════════════
async function testConversations() {
console.log('=== Testing Conversation Support ===\n');
let conversationId1: string | null = null;
let conversationId2: string | null = null;
// Create agent first
const agentId = await createAgent();
console.log(`Created agent: ${agentId}\n`);
// Test 1: Resume default conversation and get conversationId
console.log('Test 1: Resume default conversation...');
{
const session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('Remember: the secret code is ALPHA. Store this in memory.');
for await (const msg of session.stream()) {
// drain
}
conversationId1 = session.conversationId;
const hasConvId = conversationId1 !== null;
console.log(` agentId: ${session.agentId}`);
console.log(` conversationId: ${hasConvId ? 'PASS' : 'FAIL'} - ${conversationId1}`);
session.close();
}
// Test 2: Create NEW conversation using createSession
console.log('\nTest 2: Create new conversation (createSession)...');
{
const session = createSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('Remember: the secret code for THIS conversation is BETA.');
for await (const msg of session.stream()) {
// drain
}
conversationId1 = session.conversationId;
const isRealConvId = conversationId1 !== null && conversationId1 !== 'default';
console.log(` newConversation created: ${isRealConvId ? 'PASS' : 'FAIL'}`);
console.log(` conversationId: ${conversationId1}`);
session.close();
}
// Test 3: Resume conversation by conversationId (auto-detects conv-xxx)
console.log('\nTest 3: Resume conversation by conversationId...');
if (conversationId1 === 'default') {
console.log(' SKIP - "default" is not a real conversation ID');
console.log(' Use resumeSession(agentId) to resume default conversation');
} else {
await using session = resumeSession(conversationId1!, {
permissionMode: 'bypassPermissions',
});
await session.send('What is the secret code for this conversation?');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') response += msg.content;
}
const remembers = response.toLowerCase().includes('beta');
console.log(` resumeSession(convId): ${remembers ? 'PASS' : 'FAIL'}`);
console.log(` Response: ${response.slice(0, 80)}...`);
// Verify same conversationId
const sameConv = session.conversationId === conversationId1;
console.log(` same conversationId: ${sameConv ? 'PASS' : 'FAIL'}`);
}
// Test 4: Create another new conversation (verify different IDs)
console.log('\nTest 4: Create another new conversation...');
{
await using session = createSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('Say "third conversation"');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') response += msg.content;
}
conversationId2 = session.conversationId;
// New conversation should have different ID
const differentConv = conversationId2 !== conversationId1;
console.log(` different from conversationId1: ${differentConv ? 'PASS' : 'FAIL'}`);
console.log(` conversationId1: ${conversationId1}`);
console.log(` conversationId2: ${conversationId2}`);
}
// Test 5: Resume default conversation via resumeSession(agentId)
console.log('\nTest 5: Resume default conversation via resumeSession(agentId)...');
{
await using session = resumeSession(agentId, {
permissionMode: 'bypassPermissions',
});
await session.send('What is the secret code? (should be ALPHA from default conversation)');
let response = '';
for await (const msg of session.stream()) {
if (msg.type === 'assistant') response += msg.content;
}
const remembersAlpha = response.toLowerCase().includes('alpha');
console.log(` resumeSession(agentId): ${remembersAlpha ? 'PASS' : 'CHECK'}`);
console.log(` Response: ${response.slice(0, 80)}...`);
}
// Test 6: conversationId in result message
console.log('\nTest 6: conversationId in result message...');
{
await using session = resumeSession(conversationId1!, {
permissionMode: 'bypassPermissions',
});
await session.send('Hi');
let resultConvId: string | null = null;
for await (const msg of session.stream()) {
if (msg.type === 'result') {
resultConvId = msg.conversationId;
}
}
const hasResultConvId = resultConvId !== null;
const matchesSession = resultConvId === session.conversationId;
console.log(` result.conversationId: ${hasResultConvId ? 'PASS' : 'FAIL'}`);
console.log(` matches session.conversationId: ${matchesSession ? 'PASS' : 'FAIL'}`);
}
// Test 7: createSession() without agentId uses LRU agent + new conversation (like `letta`)
console.log('\nTest 7: createSession() without agentId (uses LRU agent)...');
{
await using session = createSession(undefined, {
model: 'haiku',
permissionMode: 'bypassPermissions',
});
await session.send('Say "lru agent test ok"');
for await (const msg of session.stream()) {
// drain
}
const hasAgentId = session.agentId !== null;
const hasConvId = session.conversationId !== null;
console.log(` has agentId: ${hasAgentId ? 'PASS' : 'FAIL'} - ${session.agentId}`);
console.log(` has conversationId: ${hasConvId ? 'PASS' : 'FAIL'} - ${session.conversationId}`);
}
console.log();
}
main().catch(console.error);