A React Native app for the ultimate thinking partner.

Phase 1-5: Upgrade SDK from v0.0.68664 to v1.0.0-alpha.15

Breaking changes applied:
- Client init: LettaClient → Letta, token → apiKey, added baseURL
- Method renames: createStream() → stream()
- Parameter names: camelCase → snake_case (stream_tokens, include_pings, etc)
- Tool calls: tool_call → tool_calls (array)
- Message types: Handle both messageType and message_type
- Pagination: All list methods now access .items property
- Removed deprecated use_assistant_message parameter

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

+5 -219
package-lock.json
··· 10 "dependencies": { 11 "@expo-google-fonts/lexend": "^0.4.1", 12 "@expo/metro-runtime": "~6.1.2", 13 - "@letta-ai/letta-client": "^0.0.68664", 14 "@lottiefiles/dotlottie-react": "^0.13.5", 15 "@react-native-async-storage/async-storage": "2.2.0", 16 "@react-navigation/drawer": "^7.5.8", ··· 2663 } 2664 }, 2665 "node_modules/@letta-ai/letta-client": { 2666 - "version": "0.0.68664", 2667 - "resolved": "https://registry.npmjs.org/@letta-ai/letta-client/-/letta-client-0.0.68664.tgz", 2668 - "integrity": "sha512-/0g8dV3IIX0WfnOUDY1EEgnhj/747m73zhTmbLhldEMjCk/RzKyjvUeZbHiukiGoCf/u1nxRgcRUn66MKMYB2A==", 2669 - "dependencies": { 2670 - "form-data": "^4.0.0", 2671 - "form-data-encoder": "^4.0.2", 2672 - "formdata-node": "^6.0.3", 2673 - "node-fetch": "^2.7.0", 2674 - "qs": "^6.13.1", 2675 - "readable-stream": "^4.5.2", 2676 - "url-join": "4.0.1" 2677 - } 2678 }, 2679 "node_modules/@lottiefiles/dotlottie-react": { 2680 "version": "0.13.5", ··· 4439 "node": ">= 0.4" 4440 } 4441 }, 4442 - "node_modules/call-bound": { 4443 - "version": "1.0.4", 4444 - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 4445 - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 4446 - "license": "MIT", 4447 - "dependencies": { 4448 - "call-bind-apply-helpers": "^1.0.2", 4449 - "get-intrinsic": "^1.3.0" 4450 - }, 4451 - "engines": { 4452 - "node": ">= 0.4" 4453 - }, 4454 - "funding": { 4455 - "url": "https://github.com/sponsors/ljharb" 4456 - } 4457 - }, 4458 "node_modules/caller-callsite": { 4459 "version": "2.0.0", 4460 "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", ··· 5462 "node": ">=6" 5463 } 5464 }, 5465 - "node_modules/events": { 5466 - "version": "3.3.0", 5467 - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 5468 - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 5469 - "license": "MIT", 5470 - "engines": { 5471 - "node": ">=0.8.x" 5472 - } 5473 - }, 5474 "node_modules/exec-async": { 5475 "version": "2.2.0", 5476 "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", ··· 6115 "node": ">= 6" 6116 } 6117 }, 6118 - "node_modules/form-data-encoder": { 6119 - "version": "4.1.0", 6120 - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", 6121 - "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", 6122 - "license": "MIT", 6123 - "engines": { 6124 - "node": ">= 18" 6125 - } 6126 - }, 6127 - "node_modules/formdata-node": { 6128 - "version": "6.0.3", 6129 - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", 6130 - "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", 6131 - "license": "MIT", 6132 - "engines": { 6133 - "node": ">= 18" 6134 - } 6135 - }, 6136 "node_modules/freeport-async": { 6137 "version": "2.0.0", 6138 "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", ··· 8250 "node": ">=0.10.0" 8251 } 8252 }, 8253 - "node_modules/object-inspect": { 8254 - "version": "1.13.4", 8255 - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 8256 - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 8257 - "license": "MIT", 8258 - "engines": { 8259 - "node": ">= 0.4" 8260 - }, 8261 - "funding": { 8262 - "url": "https://github.com/sponsors/ljharb" 8263 - } 8264 - }, 8265 "node_modules/on-finished": { 8266 "version": "2.3.0", 8267 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", ··· 8892 "node": "^14.17.0 || ^16.13.0 || >=18.0.0" 8893 } 8894 }, 8895 - "node_modules/process": { 8896 - "version": "0.11.10", 8897 - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 8898 - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 8899 - "license": "MIT", 8900 - "engines": { 8901 - "node": ">= 0.6.0" 8902 - } 8903 - }, 8904 "node_modules/progress": { 8905 "version": "2.0.3", 8906 "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", ··· 8972 "qrcode-terminal": "bin/qrcode-terminal.js" 8973 } 8974 }, 8975 - "node_modules/qs": { 8976 - "version": "6.14.0", 8977 - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", 8978 - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", 8979 - "license": "BSD-3-Clause", 8980 - "dependencies": { 8981 - "side-channel": "^1.1.0" 8982 - }, 8983 - "engines": { 8984 - "node": ">=0.6" 8985 - }, 8986 - "funding": { 8987 - "url": "https://github.com/sponsors/ljharb" 8988 - } 8989 - }, 8990 "node_modules/query-string": { 8991 "version": "7.1.3", 8992 "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", ··· 9498 "node": ">=0.10.0" 9499 } 9500 }, 9501 - "node_modules/readable-stream": { 9502 - "version": "4.7.0", 9503 - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 9504 - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 9505 - "license": "MIT", 9506 - "dependencies": { 9507 - "abort-controller": "^3.0.0", 9508 - "buffer": "^6.0.3", 9509 - "events": "^3.3.0", 9510 - "process": "^0.11.10", 9511 - "string_decoder": "^1.3.0" 9512 - }, 9513 - "engines": { 9514 - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 9515 - } 9516 - }, 9517 - "node_modules/readable-stream/node_modules/buffer": { 9518 - "version": "6.0.3", 9519 - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 9520 - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 9521 - "funding": [ 9522 - { 9523 - "type": "github", 9524 - "url": "https://github.com/sponsors/feross" 9525 - }, 9526 - { 9527 - "type": "patreon", 9528 - "url": "https://www.patreon.com/feross" 9529 - }, 9530 - { 9531 - "type": "consulting", 9532 - "url": "https://feross.org/support" 9533 - } 9534 - ], 9535 - "license": "MIT", 9536 - "dependencies": { 9537 - "base64-js": "^1.3.1", 9538 - "ieee754": "^1.2.1" 9539 - } 9540 - }, 9541 "node_modules/regenerate": { 9542 "version": "1.4.2", 9543 "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", ··· 10082 "url": "https://github.com/sponsors/ljharb" 10083 } 10084 }, 10085 - "node_modules/side-channel": { 10086 - "version": "1.1.0", 10087 - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 10088 - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 10089 - "license": "MIT", 10090 - "dependencies": { 10091 - "es-errors": "^1.3.0", 10092 - "object-inspect": "^1.13.3", 10093 - "side-channel-list": "^1.0.0", 10094 - "side-channel-map": "^1.0.1", 10095 - "side-channel-weakmap": "^1.0.2" 10096 - }, 10097 - "engines": { 10098 - "node": ">= 0.4" 10099 - }, 10100 - "funding": { 10101 - "url": "https://github.com/sponsors/ljharb" 10102 - } 10103 - }, 10104 - "node_modules/side-channel-list": { 10105 - "version": "1.0.0", 10106 - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 10107 - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 10108 - "license": "MIT", 10109 - "dependencies": { 10110 - "es-errors": "^1.3.0", 10111 - "object-inspect": "^1.13.3" 10112 - }, 10113 - "engines": { 10114 - "node": ">= 0.4" 10115 - }, 10116 - "funding": { 10117 - "url": "https://github.com/sponsors/ljharb" 10118 - } 10119 - }, 10120 - "node_modules/side-channel-map": { 10121 - "version": "1.0.1", 10122 - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 10123 - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 10124 - "license": "MIT", 10125 - "dependencies": { 10126 - "call-bound": "^1.0.2", 10127 - "es-errors": "^1.3.0", 10128 - "get-intrinsic": "^1.2.5", 10129 - "object-inspect": "^1.13.3" 10130 - }, 10131 - "engines": { 10132 - "node": ">= 0.4" 10133 - }, 10134 - "funding": { 10135 - "url": "https://github.com/sponsors/ljharb" 10136 - } 10137 - }, 10138 - "node_modules/side-channel-weakmap": { 10139 - "version": "1.0.2", 10140 - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 10141 - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 10142 - "license": "MIT", 10143 - "dependencies": { 10144 - "call-bound": "^1.0.2", 10145 - "es-errors": "^1.3.0", 10146 - "get-intrinsic": "^1.2.5", 10147 - "object-inspect": "^1.13.3", 10148 - "side-channel-map": "^1.0.1" 10149 - }, 10150 - "engines": { 10151 - "node": ">= 0.4" 10152 - }, 10153 - "funding": { 10154 - "url": "https://github.com/sponsors/ljharb" 10155 - } 10156 - }, 10157 "node_modules/signal-exit": { 10158 "version": "4.1.0", 10159 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", ··· 10343 "license": "MIT", 10344 "engines": { 10345 "node": ">=4" 10346 - } 10347 - }, 10348 - "node_modules/string_decoder": { 10349 - "version": "1.3.0", 10350 - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 10351 - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 10352 - "license": "MIT", 10353 - "dependencies": { 10354 - "safe-buffer": "~5.2.0" 10355 } 10356 }, 10357 "node_modules/string-width": { ··· 11023 "peerDependencies": { 11024 "browserslist": ">= 4.21.0" 11025 } 11026 - }, 11027 - "node_modules/url-join": { 11028 - "version": "4.0.1", 11029 - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", 11030 - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", 11031 - "license": "MIT" 11032 }, 11033 "node_modules/use-latest-callback": { 11034 "version": "0.2.4",
··· 10 "dependencies": { 11 "@expo-google-fonts/lexend": "^0.4.1", 12 "@expo/metro-runtime": "~6.1.2", 13 + "@letta-ai/letta-client": "1.0.0-alpha.15", 14 "@lottiefiles/dotlottie-react": "^0.13.5", 15 "@react-native-async-storage/async-storage": "2.2.0", 16 "@react-navigation/drawer": "^7.5.8", ··· 2663 } 2664 }, 2665 "node_modules/@letta-ai/letta-client": { 2666 + "version": "1.0.0-alpha.15", 2667 + "resolved": "https://registry.npmjs.org/@letta-ai/letta-client/-/letta-client-1.0.0-alpha.15.tgz", 2668 + "integrity": "sha512-5OpXmloDnboA0nYC9xJIJuIWzAaVS06uDr9YLO6hR29zblwgeHPpaopWJFyg+FR0Cg7SSyPgEb3xzjGdRd6Eqg==", 2669 + "license": "Apache-2.0" 2670 }, 2671 "node_modules/@lottiefiles/dotlottie-react": { 2672 "version": "0.13.5", ··· 4431 "node": ">= 0.4" 4432 } 4433 }, 4434 "node_modules/caller-callsite": { 4435 "version": "2.0.0", 4436 "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", ··· 5438 "node": ">=6" 5439 } 5440 }, 5441 "node_modules/exec-async": { 5442 "version": "2.2.0", 5443 "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", ··· 6082 "node": ">= 6" 6083 } 6084 }, 6085 "node_modules/freeport-async": { 6086 "version": "2.0.0", 6087 "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", ··· 8199 "node": ">=0.10.0" 8200 } 8201 }, 8202 "node_modules/on-finished": { 8203 "version": "2.3.0", 8204 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", ··· 8829 "node": "^14.17.0 || ^16.13.0 || >=18.0.0" 8830 } 8831 }, 8832 "node_modules/progress": { 8833 "version": "2.0.3", 8834 "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", ··· 8900 "qrcode-terminal": "bin/qrcode-terminal.js" 8901 } 8902 }, 8903 "node_modules/query-string": { 8904 "version": "7.1.3", 8905 "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", ··· 9411 "node": ">=0.10.0" 9412 } 9413 }, 9414 "node_modules/regenerate": { 9415 "version": "1.4.2", 9416 "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", ··· 9955 "url": "https://github.com/sponsors/ljharb" 9956 } 9957 }, 9958 "node_modules/signal-exit": { 9959 "version": "4.1.0", 9960 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", ··· 10144 "license": "MIT", 10145 "engines": { 10146 "node": ">=4" 10147 } 10148 }, 10149 "node_modules/string-width": { ··· 10815 "peerDependencies": { 10816 "browserslist": ">= 4.21.0" 10817 } 10818 }, 10819 "node_modules/use-latest-callback": { 10820 "version": "0.2.4",
+1 -1
package.json
··· 14 "dependencies": { 15 "@expo-google-fonts/lexend": "^0.4.1", 16 "@expo/metro-runtime": "~6.1.2", 17 - "@letta-ai/letta-client": "^0.0.68664", 18 "@lottiefiles/dotlottie-react": "^0.13.5", 19 "@react-native-async-storage/async-storage": "2.2.0", 20 "@react-navigation/drawer": "^7.5.8",
··· 14 "dependencies": { 15 "@expo-google-fonts/lexend": "^0.4.1", 16 "@expo/metro-runtime": "~6.1.2", 17 + "@letta-ai/letta-client": "1.0.0-alpha.15", 18 "@lottiefiles/dotlottie-react": "^0.13.5", 19 "@react-native-async-storage/async-storage": "2.2.0", 20 "@react-navigation/drawer": "^7.5.8",
+39 -35
src/api/lettaApi.ts
··· 1 - import { LettaClient } from '@letta-ai/letta-client'; 2 import type { 3 LettaAgent, 4 LettaMessage, ··· 22 import { config } from '../config'; 23 24 class LettaApiService { 25 - private client: LettaClient | null = null; 26 private token: string | null = null; 27 28 constructor(token?: string) { ··· 36 if (!this.client) { 37 throw new Error('Client not initialized. Please set auth token first.'); 38 } 39 - const blocks = await this.client.agents.blocks.list(agentId); 40 return blocks as unknown as MemoryBlock[]; 41 } catch (error) { 42 throw this.handleError(error); ··· 84 85 // Initialize the official Letta client with extended timeout for agent creation 86 // Agent creation with sleeptime can take a while as it creates 2 agents 87 - this.client = new LettaClient({ 88 - token, 89 timeout: config.api.timeout 90 }); 91 this.token = token; ··· 157 console.log('listAgents - calling SDK with params:', params); 158 console.log('listAgents - JSON stringify params:', JSON.stringify(params)); 159 160 - const response = await this.client.agents.list(params); 161 - console.log('listAgents - SDK response count:', response?.length || 0); 162 console.log('listAgents - first few agents project_ids:', 163 - response?.slice(0, 3)?.map(a => ({ name: a.name, project_id: a.project_id })) || [] 164 ); 165 166 return response; ··· 284 285 // Transform messages to match our interface, preserving tool step types 286 const transformedMessages = (response.messages || []).map((message: any) => { 287 - const type = message.messageType; 288 // Extract possible tool call/return shapes from SDK variants 289 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); 290 const toolReturn = message.tool_response || message.toolResponse || message.tool_return || message.toolReturn; ··· 361 }; 362 }), 363 // Token streaming provides partial chunks for real-time UX 364 - streamTokens: messageData.stream_tokens !== false, 365 // Background mode prevents client-side terminations and enables resumption 366 background: true, 367 // Ping events keep connection alive during long operations 368 - includePings: true, 369 }; 370 371 // Only add optional params if they're defined 372 - if (messageData.use_assistant_message !== undefined) { 373 - lettaStreamingRequest.useAssistantMessage = messageData.use_assistant_message; 374 - } 375 if (messageData.max_steps !== undefined) { 376 - lettaStreamingRequest.maxSteps = messageData.max_steps; 377 } 378 379 console.log('=== SIMPLIFIED REQUEST ==='); ··· 399 } 400 }); 401 402 - console.log('=== CALLING SDK createStream ==='); 403 console.log('Agent ID:', agentId); 404 console.log('Client base URL:', (this.client as any)?.baseURL || 'unknown'); 405 console.log('About to call: POST /agents/{agentId}/messages/stream'); 406 407 - const stream = await this.client.agents.messages.createStream(agentId, lettaStreamingRequest); 408 409 console.log('=== STREAM OBJECT CREATED ==='); 410 console.log('Stream object type:', typeof stream); ··· 553 console.log('listMessages - agentId:', agentId); 554 console.log('listMessages - params:', params); 555 556 - const response = await this.client.agents.messages.list(agentId, params); 557 - console.log('listMessages - response count:', response?.length || 0); 558 559 /** 560 * MESSAGE TRANSFORMATION ARCHITECTURE ··· 594 * display as empty blocks like "tool({})" which is meaningless to the user. 595 */ 596 const transformedMessages: LettaMessage[] = response.map((message: any) => { 597 - const type = message.messageType as string; 598 599 // Extract tool call/return data (try multiple field name variants for SDK compatibility) 600 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); ··· 660 throw new Error('Client not initialized. Please set auth token first.'); 661 } 662 663 - const response = await this.client.tools.list(params); 664 - return response; 665 } catch (error) { 666 throw this.handleError(error); 667 } ··· 752 try { 753 const response = await this.client.agents.messages.create(agentId, sanitized); 754 const transformedMessages = (response.messages || []).map((message: any) => { 755 - const type = message.messageType; 756 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); 757 const toolReturn = message.tool_response || message.toolResponse || message.tool_return || message.toolReturn; 758 ··· 867 { 868 type: 'approval', 869 approve: params.approve, 870 - approvalRequestId: params.approval_request_id, 871 reason: params.reason, 872 }, 873 ], 874 - streamTokens: true, 875 background: true, 876 - includePings: true, 877 }; 878 879 - const stream = await this.client.agents.messages.createStream(agentId, body); 880 881 for await (const chunk of stream) { 882 const mt = (chunk as any).message_type || (chunk as any).messageType; ··· 958 ...(after && { after }) 959 }); 960 961 - // Convert response to array if needed 962 - const folders = Array.isArray(page) ? page : []; 963 964 console.log(`listFolders - page ${pageCount + 1}: ${folders.length} folders`); 965 console.log(`listFolders - page ${pageCount + 1} first 3 names:`, folders.slice(0, 3).map(f => f.name)); ··· 989 } 990 991 // No name filter, just return first page using SDK 992 - const folders = await this.client.folders.list(params); 993 console.log('listFolders - returned count:', folders.length); 994 return folders; 995 } catch (error) { ··· 1071 throw new Error('Client not initialized. Please set auth token first.'); 1072 } 1073 1074 - const files = await this.client.folders.files.list(folderId); 1075 - return files; 1076 } catch (error) { 1077 throw this.handleError(error); 1078 } ··· 1138 } 1139 1140 console.log('listPassages - agentId:', agentId, 'params:', params); 1141 - const passages = await this.client.agents.passages.list(agentId, params); 1142 - console.log('listPassages - result count:', passages?.length || 0); 1143 return passages as Passage[]; 1144 } catch (error) { 1145 console.error('listPassages - error:', error); ··· 1260 } 1261 1262 console.log('listAgentsForBlock - blockId:', blockId); 1263 - const agents = await this.client.blocks.agents.list(blockId); 1264 console.log('listAgentsForBlock - found agents:', agents.length); 1265 return agents; 1266 } catch (error) {
··· 1 + import Letta from '@letta-ai/letta-client'; 2 import type { 3 LettaAgent, 4 LettaMessage, ··· 22 import { config } from '../config'; 23 24 class LettaApiService { 25 + private client: Letta | null = null; 26 private token: string | null = null; 27 28 constructor(token?: string) { ··· 36 if (!this.client) { 37 throw new Error('Client not initialized. Please set auth token first.'); 38 } 39 + const blocksPage = await this.client.agents.blocks.list(agentId); 40 + const blocks = blocksPage.items || []; 41 return blocks as unknown as MemoryBlock[]; 42 } catch (error) { 43 throw this.handleError(error); ··· 85 86 // Initialize the official Letta client with extended timeout for agent creation 87 // Agent creation with sleeptime can take a while as it creates 2 agents 88 + this.client = new Letta({ 89 + apiKey: token, 90 + baseURL: config.api.baseURL, 91 timeout: config.api.timeout 92 }); 93 this.token = token; ··· 159 console.log('listAgents - calling SDK with params:', params); 160 console.log('listAgents - JSON stringify params:', JSON.stringify(params)); 161 162 + const responsePage = await this.client.agents.list(params); 163 + const response = responsePage.items || []; 164 + console.log('listAgents - SDK response count:', response.length); 165 console.log('listAgents - first few agents project_ids:', 166 + response.slice(0, 3).map(a => ({ name: a.name, project_id: a.project_id })) 167 ); 168 169 return response; ··· 287 288 // Transform messages to match our interface, preserving tool step types 289 const transformedMessages = (response.messages || []).map((message: any) => { 290 + const type = message.message_type || message.messageType; 291 // Extract possible tool call/return shapes from SDK variants 292 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); 293 const toolReturn = message.tool_response || message.toolResponse || message.tool_return || message.toolReturn; ··· 364 }; 365 }), 366 // Token streaming provides partial chunks for real-time UX 367 + stream_tokens: messageData.stream_tokens !== false, 368 // Background mode prevents client-side terminations and enables resumption 369 background: true, 370 // Ping events keep connection alive during long operations 371 + include_pings: true, 372 }; 373 374 // Only add optional params if they're defined 375 if (messageData.max_steps !== undefined) { 376 + lettaStreamingRequest.max_steps = messageData.max_steps; 377 } 378 379 console.log('=== SIMPLIFIED REQUEST ==='); ··· 399 } 400 }); 401 402 + console.log('=== CALLING SDK stream ==='); 403 console.log('Agent ID:', agentId); 404 console.log('Client base URL:', (this.client as any)?.baseURL || 'unknown'); 405 console.log('About to call: POST /agents/{agentId}/messages/stream'); 406 407 + const stream = await this.client.agents.messages.stream(agentId, lettaStreamingRequest); 408 409 console.log('=== STREAM OBJECT CREATED ==='); 410 console.log('Stream object type:', typeof stream); ··· 553 console.log('listMessages - agentId:', agentId); 554 console.log('listMessages - params:', params); 555 556 + const responsePage = await this.client.agents.messages.list(agentId, params); 557 + const response = responsePage.items || []; 558 + console.log('listMessages - response count:', response.length); 559 560 /** 561 * MESSAGE TRANSFORMATION ARCHITECTURE ··· 595 * display as empty blocks like "tool({})" which is meaningless to the user. 596 */ 597 const transformedMessages: LettaMessage[] = response.map((message: any) => { 598 + const type = (message.message_type || message.messageType) as string; 599 600 // Extract tool call/return data (try multiple field name variants for SDK compatibility) 601 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); ··· 661 throw new Error('Client not initialized. Please set auth token first.'); 662 } 663 664 + const responsePage = await this.client.tools.list(params); 665 + return responsePage.items || []; 666 } catch (error) { 667 throw this.handleError(error); 668 } ··· 753 try { 754 const response = await this.client.agents.messages.create(agentId, sanitized); 755 const transformedMessages = (response.messages || []).map((message: any) => { 756 + const type = message.message_type || message.messageType; 757 const toolCall = message.tool_call || message.toolCall || (message.tool_calls && message.tool_calls[0]); 758 const toolReturn = message.tool_response || message.toolResponse || message.tool_return || message.toolReturn; 759 ··· 868 { 869 type: 'approval', 870 approve: params.approve, 871 + approval_request_id: params.approval_request_id, 872 reason: params.reason, 873 }, 874 ], 875 + stream_tokens: true, 876 background: true, 877 + include_pings: true, 878 }; 879 880 + const stream = await this.client.agents.messages.stream(agentId, body); 881 882 for await (const chunk of stream) { 883 const mt = (chunk as any).message_type || (chunk as any).messageType; ··· 959 ...(after && { after }) 960 }); 961 962 + // SDK v1.0 returns page object with .items 963 + const folders = page.items || []; 964 965 console.log(`listFolders - page ${pageCount + 1}: ${folders.length} folders`); 966 console.log(`listFolders - page ${pageCount + 1} first 3 names:`, folders.slice(0, 3).map(f => f.name)); ··· 990 } 991 992 // No name filter, just return first page using SDK 993 + const foldersPage = await this.client.folders.list(params); 994 + const folders = foldersPage.items || []; 995 console.log('listFolders - returned count:', folders.length); 996 return folders; 997 } catch (error) { ··· 1073 throw new Error('Client not initialized. Please set auth token first.'); 1074 } 1075 1076 + const filesPage = await this.client.folders.files.list(folderId); 1077 + return filesPage.items || []; 1078 } catch (error) { 1079 throw this.handleError(error); 1080 } ··· 1140 } 1141 1142 console.log('listPassages - agentId:', agentId, 'params:', params); 1143 + const passagesPage = await this.client.agents.passages.list(agentId, params); 1144 + const passages = passagesPage.items || []; 1145 + console.log('listPassages - result count:', passages.length); 1146 return passages as Passage[]; 1147 } catch (error) { 1148 console.error('listPassages - error:', error); ··· 1263 } 1264 1265 console.log('listAgentsForBlock - blockId:', blockId); 1266 + const agentsPage = await this.client.blocks.agents.list(blockId); 1267 + const agents = agentsPage.items || []; 1268 console.log('listAgentsForBlock - found agents:', agents.length); 1269 return agents; 1270 } catch (error) {
+6 -4
src/hooks/useMessageStream.ts
··· 47 chatStore.accumulateReasoning(chunkId, chunk.reasoning); 48 } 49 else if (chunkType === 'tool_call_message' && chunkId) { 50 - const toolCall = (chunk as any).toolCall || (chunk as any).tool_call; 51 - if (toolCall) { 52 const toolName = toolCall.name || toolCall.tool_name || 'unknown'; 53 // Try multiple places for arguments 54 let args = toolCall.arguments || toolCall.args || ''; ··· 211 content: content, 212 reasoning: msg.reasoning, 213 ...(msg.type === 'tool_call' && msg.toolCallName ? { 214 - tool_call: { 215 name: msg.toolCallName, 216 arguments: msg.content, // Keep as JSON for parseToolCall fallback 217 - } 218 } : {}), 219 created_at: msg.timestamp, 220 } as any;
··· 47 chatStore.accumulateReasoning(chunkId, chunk.reasoning); 48 } 49 else if (chunkType === 'tool_call_message' && chunkId) { 50 + // SDK v1.0: tool_calls is now an array 51 + const toolCalls = (chunk as any).tool_calls || [(chunk as any).toolCall || (chunk as any).tool_call].filter(Boolean); 52 + if (toolCalls.length > 0) { 53 + const toolCall = toolCalls[0]; 54 const toolName = toolCall.name || toolCall.tool_name || 'unknown'; 55 // Try multiple places for arguments 56 let args = toolCall.arguments || toolCall.args || ''; ··· 213 content: content, 214 reasoning: msg.reasoning, 215 ...(msg.type === 'tool_call' && msg.toolCallName ? { 216 + tool_calls: [{ 217 name: msg.toolCallName, 218 arguments: msg.content, // Keep as JSON for parseToolCall fallback 219 + }] 220 } : {}), 221 created_at: msg.timestamp, 222 } as any;