A React Native app for the ultimate thinking partner.
1import lettaApi from '../api/lettaApi';
2import type { LettaAgent } from '../types/letta';
3import { getDefaultMemoryBlocks } from '../constants/memoryBlocks';
4import { CO_SYSTEM_PROMPT } from '../constants/systemPrompt';
5import { Letta } from "@letta-ai/letta-client";
6
7const CO_TAG = 'co-app';
8
9/**
10 * Create Co - a comprehensive knowledge management assistant
11 */
12export async function createCoAgent(userName: string): Promise<LettaAgent> {
13 try {
14 const agent = await lettaApi.createAgent({
15 name: 'Co',
16 description: 'Co - A comprehensive knowledge management assistant designed to learn, adapt, and think alongside the user',
17 // agentType: Letta.AgentType.LettaV1Agent, // currently pending sleeptime fixes
18 // agentType: Letta.AgentType.MemgptV2Agent,
19 model: 'anthropic/claude-sonnet-4-5-20250929',
20 system: CO_SYSTEM_PROMPT,
21 tags: [CO_TAG],
22 memoryBlocks: getDefaultMemoryBlocks(),
23 // includeBaseTools: false,
24 tools: [
25 'conversation_search',
26 'web_search',
27 'fetch_webpage',
28 ],
29 toolRules: [], // No tool rules
30 enableSleeptime: true,
31 });
32
33 console.log('Agent created, finding sleeptime agent via shared memory blocks...');
34
35 // Get the first memory block ID - both primary and sleeptime agents share the same blocks
36 const blockId = agent.memory?.blocks?.[0]?.id;
37
38 if (!blockId) {
39 console.warn('No memory blocks found on agent. Cannot find sleeptime agent.');
40 return agent;
41 }
42
43 console.log('Using block ID to find agents:', blockId);
44
45 // Get all agents that share this memory block (should be primary + sleeptime)
46 const agentsForBlock = await lettaApi.listAgentsForBlock(blockId);
47 console.log('Agents sharing this block:', agentsForBlock.map(a => ({ id: a.id, name: a.name })));
48
49 // Find the sleeptime agent (the one that's NOT the primary agent)
50 const sleeptimeAgent = agentsForBlock.find(a => a.id !== agent.id);
51
52 if (sleeptimeAgent) {
53 console.log('Found sleeptime agent:', sleeptimeAgent.id, sleeptimeAgent.name);
54
55 // Retrieve full primary agent details and store sleeptime agent ID
56 const fullAgent = await lettaApi.getAgent(agent.id);
57 fullAgent.sleeptime_agent_id = sleeptimeAgent.id;
58
59 // Attach archival memory tools to sleeptime agent
60 await ensureSleeptimeTools(fullAgent);
61
62 return fullAgent;
63 } else {
64 console.warn('No sleeptime agent found sharing memory blocks. Only found:', agentsForBlock.length, 'agent(s)');
65 return agent;
66 }
67 } catch (error) {
68 console.error('Error creating Co agent:', error);
69 throw error;
70 }
71}
72
73/**
74 * Ensure sleeptime agent has required archival memory tools and co_memory block
75 */
76export async function ensureSleeptimeTools(agent: LettaAgent): Promise<void> {
77 try {
78 // Try to get sleeptime agent ID from either the custom property or multi_agent_group
79 const sleeptimeAgentId = agent.sleeptime_agent_id || agent.multi_agent_group?.agent_ids?.[0];
80
81 if (!sleeptimeAgentId) {
82 console.log('No sleeptime agent found for agent:', agent.id);
83 return;
84 }
85
86 console.log('Ensuring sleeptime agent has archival tools and co_memory block:', sleeptimeAgentId);
87
88 // Get the sleeptime agent to check its current tools and blocks
89 const sleeptimeAgent = await lettaApi.getAgent(sleeptimeAgentId);
90 const sleeptimeToolNames = sleeptimeAgent.tools?.map(t => t.name) || [];
91 const sleeptimeBlockLabels = sleeptimeAgent.memory?.blocks?.map(b => b.label) || [];
92
93 console.log('Current sleeptime tools:', sleeptimeToolNames);
94 console.log('Current sleeptime blocks:', sleeptimeBlockLabels);
95
96 // Attach missing tools
97 const requiredTools = ['archival_memory_search', 'archival_memory_insert'];
98 for (const toolName of requiredTools) {
99 if (!sleeptimeToolNames.includes(toolName)) {
100 console.log(`Attaching ${toolName} to sleeptime agent`);
101 try {
102 await lettaApi.attachToolToAgentByName(sleeptimeAgentId, toolName);
103 console.log(`✓ Successfully attached ${toolName}`);
104 } catch (error) {
105 console.error(`✗ Failed to attach ${toolName}:`, error);
106 throw error;
107 }
108 } else {
109 console.log(`✓ ${toolName} already attached`);
110 }
111 }
112
113 // Ensure co_memory block exists
114 if (!sleeptimeBlockLabels.includes('co_memory')) {
115 console.log('Creating co_memory block for sleeptime agent');
116 try {
117 const { CO_MEMORY_BLOCK } = await import('../constants/memoryBlocks');
118
119 // Two-step process: create block, then attach to agent
120 const createdBlock = await lettaApi.createBlock({
121 label: CO_MEMORY_BLOCK.label,
122 value: CO_MEMORY_BLOCK.value,
123 description: CO_MEMORY_BLOCK.description,
124 limit: CO_MEMORY_BLOCK.limit,
125 });
126
127 console.log('✓ Created co_memory block:', createdBlock.id);
128
129 await lettaApi.attachBlockToAgent(sleeptimeAgentId, createdBlock.id!);
130 console.log('✓ Successfully attached co_memory block to sleeptime agent');
131 } catch (error) {
132 console.error('✗ Failed to create/attach co_memory block:', error);
133 throw error;
134 }
135 } else {
136 console.log('✓ co_memory block already exists');
137 }
138 } catch (error) {
139 console.error('Error in ensureSleeptimeTools:', error);
140 throw error;
141 }
142}
143
144/**
145 * Find or create the Co agent for the logged-in user
146 */
147export async function findOrCreateCo(userName: string): Promise<LettaAgent> {
148 try {
149 // Try to find existing Co agent
150 const existingAgent = await lettaApi.findAgentByTags([CO_TAG]);
151
152 if (existingAgent) {
153 console.log('Found existing Co agent:', existingAgent.id);
154
155 // Retrieve full agent details
156 const fullAgent = await lettaApi.getAgent(existingAgent.id);
157
158 // Find sleeptime agent using shared memory blocks (same approach as createCoAgent)
159 const blockId = fullAgent.memory?.blocks?.[0]?.id;
160
161 if (blockId) {
162 console.log('Using block ID to find sleeptime agent:', blockId);
163 const agentsForBlock = await lettaApi.listAgentsForBlock(blockId);
164 console.log('Agents sharing this block:', agentsForBlock.map(a => ({ id: a.id, name: a.name })));
165
166 // Find the sleeptime agent (the one that's NOT the primary agent)
167 const sleeptimeAgent = agentsForBlock.find(a => a.id !== fullAgent.id);
168
169 if (sleeptimeAgent) {
170 fullAgent.sleeptime_agent_id = sleeptimeAgent.id;
171 console.log('Found sleeptime agent for existing Co:', sleeptimeAgent.id);
172 } else {
173 console.warn('No sleeptime agent found sharing memory blocks for existing agent');
174 }
175 } else {
176 console.warn('No memory blocks found on existing agent. Cannot find sleeptime agent.');
177 }
178
179 // Ensure sleeptime agent has required tools
180 await ensureSleeptimeTools(fullAgent);
181
182 return fullAgent;
183 }
184
185 // Create new Co agent
186 console.log('Creating new Co agent for user:', userName);
187 return await createCoAgent(userName);
188 } catch (error) {
189 console.error('Error in findOrCreateCo:', error);
190 throw error;
191 }
192}