feat: add reset-conversation command and better error detection (#122)
When conversations become corrupted on Letta Cloud, users see empty responses with no useful error message. This adds: 1. Warning message when empty result detected: - Logs: "Agent returned empty result with no response" - Suggests running `lettabot reset-conversation` 2. New CLI command `lettabot reset-conversation`: - Clears the conversationId from lettabot-agent.json - Preserves agent and memory - Next message creates fresh conversation Symptoms of corrupted conversation: - stop_reason: "error" with empty result - Messages not appearing in agent history - duration_api_ms: 0 (no API call made) Written by Cameron ◯ Letta Code "When in doubt, start fresh." - Ancient debugging wisdom
This commit is contained in:
30
package-lock.json
generated
30
package-lock.json
generated
@@ -1211,9 +1211,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
|
||||
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
@@ -1277,13 +1277,13 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@letta-ai/letta-code": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@letta-ai/letta-code/-/letta-code-0.14.1.tgz",
|
||||
"integrity": "sha512-4XQQxqDUlFNo7uKBilyIJ4KKHC8QrFoeMfZXmr9LgtNMtXYqB+I5AYuCnG9BBueeZgO1hlDN2ekJZELyJUqrPQ==",
|
||||
"version": "0.14.8",
|
||||
"resolved": "https://registry.npmjs.org/@letta-ai/letta-code/-/letta-code-0.14.8.tgz",
|
||||
"integrity": "sha512-sS5jsAcA1hLIuQXzlKGveA1IjFw6TTcjK/oZmtVq3suB2HUdML9RdNpqwMnDRz6NS4tT4nyWvbu732uyJEq7ig==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@letta-ai/letta-client": "^1.7.6",
|
||||
"@letta-ai/letta-client": "^1.7.7",
|
||||
"glob": "^13.0.0",
|
||||
"ink-link": "^5.0.0",
|
||||
"open": "^10.2.0",
|
||||
@@ -1306,12 +1306,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@letta-ai/letta-code/node_modules/glob": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz",
|
||||
"integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==",
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.1.tgz",
|
||||
"integrity": "sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.1.1",
|
||||
"minimatch": "^10.1.2",
|
||||
"minipass": "^7.1.2",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
@@ -1332,12 +1332,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@letta-ai/letta-code/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz",
|
||||
"integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
"@isaacs/brace-expansion": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
|
||||
54
src/cli.ts
54
src/cli.ts
@@ -184,6 +184,7 @@ Commands:
|
||||
logout Logout from Letta Platform (revoke OAuth tokens)
|
||||
skills Configure which skills are enabled
|
||||
skills status Show skills status
|
||||
reset-conversation Clear conversation ID (fixes corrupted conversations)
|
||||
destroy Delete all local data and start fresh
|
||||
pairing list <ch> List pending pairing requests
|
||||
pairing approve <ch> <code> Approve a pairing code
|
||||
@@ -344,6 +345,57 @@ async function main() {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'reset-conversation': {
|
||||
const { existsSync, readFileSync, writeFileSync } = await import('node:fs');
|
||||
const { join } = await import('node:path');
|
||||
const p = await import('@clack/prompts');
|
||||
|
||||
const dataDir = getDataDir();
|
||||
const agentJsonPath = join(dataDir, 'lettabot-agent.json');
|
||||
|
||||
p.intro('Reset Conversation');
|
||||
|
||||
if (!existsSync(agentJsonPath)) {
|
||||
p.log.error('No agent store found. Run the server first to create an agent.');
|
||||
break;
|
||||
}
|
||||
|
||||
const store = JSON.parse(readFileSync(agentJsonPath, 'utf-8'));
|
||||
const oldConversationId = store.conversationId;
|
||||
|
||||
if (!oldConversationId) {
|
||||
p.log.info('No conversation ID stored. Nothing to reset.');
|
||||
break;
|
||||
}
|
||||
|
||||
p.log.warn(`Current conversation: ${oldConversationId}`);
|
||||
p.log.message('');
|
||||
p.log.message('This will clear the conversation ID, causing the bot to create');
|
||||
p.log.message('a new conversation on the next message. Use this if you see:');
|
||||
p.log.message(' • "stop_reason: error" with empty responses');
|
||||
p.log.message(' • Messages not reaching the agent');
|
||||
p.log.message(' • Agent returning empty results');
|
||||
p.log.message('');
|
||||
p.log.message('The agent and its memory will be preserved.');
|
||||
|
||||
const confirmed = await p.confirm({
|
||||
message: 'Reset conversation?',
|
||||
initialValue: true,
|
||||
});
|
||||
|
||||
if (!confirmed || p.isCancel(confirmed)) {
|
||||
p.cancel('Cancelled');
|
||||
break;
|
||||
}
|
||||
|
||||
store.conversationId = null;
|
||||
writeFileSync(agentJsonPath, JSON.stringify(store, null, 2));
|
||||
|
||||
p.log.success('Conversation ID cleared');
|
||||
p.outro('Restart the server - a new conversation will be created on the next message.');
|
||||
break;
|
||||
}
|
||||
|
||||
case 'logout': {
|
||||
const { revokeToken } = await import('./auth/oauth.js');
|
||||
const { loadTokens, deleteTokens } = await import('./auth/tokens.js');
|
||||
@@ -382,7 +434,7 @@ async function main() {
|
||||
|
||||
case undefined:
|
||||
console.log('Usage: lettabot <command>\n');
|
||||
console.log('Commands: onboard, server, configure, skills, destroy, help\n');
|
||||
console.log('Commands: onboard, server, configure, skills, reset-conversation, destroy, help\n');
|
||||
console.log('Run "lettabot help" for more information.');
|
||||
break;
|
||||
|
||||
|
||||
@@ -373,6 +373,14 @@ export class LettaBot {
|
||||
}
|
||||
|
||||
if (streamMsg.type === 'result') {
|
||||
// Check for corrupted conversation (empty result usually means error)
|
||||
const resultMsg = streamMsg as { result?: string; success?: boolean };
|
||||
if (resultMsg.success && resultMsg.result === '' && !response.trim()) {
|
||||
console.error('[Bot] Warning: Agent returned empty result with no response.');
|
||||
console.error('[Bot] This often indicates a corrupted conversation.');
|
||||
console.error('[Bot] Try running: lettabot reset-conversation');
|
||||
}
|
||||
|
||||
// Save agent ID and conversation ID
|
||||
if (session.agentId && session.agentId !== this.store.agentId) {
|
||||
const isNewAgent = !this.store.agentId;
|
||||
|
||||
Reference in New Issue
Block a user