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