fix: reset-conversation clears per-channel conversations in v2 store (#320)
The reset-conversation CLI command was reading/writing the agent JSON file directly, missing the per-channel conversations map introduced with conversation routing. Now uses Store.clearConversation() which clears both the legacy conversationId and all per-channel conversation keys. Also adds multi-agent awareness: prompts which agent to reset when multiple agents are configured. Closes #316 Written by Cameron and Letta Code "Debugging is twice as hard as writing the code in the first place." -- Brian Kernighan
This commit is contained in:
86
src/cli.ts
86
src/cli.ts
@@ -419,52 +419,86 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'reset-conversation': {
|
case 'reset-conversation': {
|
||||||
const { existsSync, readFileSync, writeFileSync } = await import('node:fs');
|
|
||||||
const { join } = await import('node:path');
|
|
||||||
const p = await import('@clack/prompts');
|
const p = await import('@clack/prompts');
|
||||||
|
|
||||||
const dataDir = getDataDir();
|
|
||||||
const agentJsonPath = join(dataDir, 'lettabot-agent.json');
|
|
||||||
|
|
||||||
p.intro('Reset Conversation');
|
p.intro('Reset Conversation');
|
||||||
|
|
||||||
if (!existsSync(agentJsonPath)) {
|
const configuredName =
|
||||||
p.log.error('No agent store found. Run the server first to create an agent.');
|
(config.agent?.name?.trim())
|
||||||
|
|| (config.agents?.length && config.agents[0].name?.trim())
|
||||||
|
|| 'LettaBot';
|
||||||
|
|
||||||
|
const configuredAgents = (config.agents?.length ? config.agents : [{ name: configuredName }])
|
||||||
|
.map(agent => agent.name?.trim())
|
||||||
|
.filter((name): name is string => !!name);
|
||||||
|
|
||||||
|
const uniqueAgents = Array.from(new Set(configuredAgents));
|
||||||
|
|
||||||
|
let targetAgents = uniqueAgents;
|
||||||
|
if (uniqueAgents.length > 1) {
|
||||||
|
const choice = await p.select({
|
||||||
|
message: 'Which agent should be reset?',
|
||||||
|
options: [
|
||||||
|
{ value: '__all__', label: 'All configured agents' },
|
||||||
|
...uniqueAgents.map(name => ({ value: name, label: name })),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (p.isCancel(choice)) {
|
||||||
|
p.cancel('Cancelled');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
targetAgents = choice === '__all__' ? uniqueAgents : [choice as string];
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = targetAgents.map((name) => {
|
||||||
|
const store = new Store('lettabot-agent.json', name);
|
||||||
|
const info = store.getInfo();
|
||||||
|
const perChannelKeys = info.conversations ? Object.keys(info.conversations) : [];
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
store,
|
||||||
|
hasLegacy: !!info.conversationId,
|
||||||
|
perChannelKeys,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasAny = entries.some(entry => entry.hasLegacy || entry.perChannelKeys.length > 0);
|
||||||
|
if (!hasAny) {
|
||||||
|
p.log.info('No conversation IDs stored. Nothing to reset.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = JSON.parse(readFileSync(agentJsonPath, 'utf-8'));
|
for (const entry of entries) {
|
||||||
const oldConversationId = store.conversationId;
|
if (entry.hasLegacy) {
|
||||||
|
p.log.warn(`Current conversation (${entry.name}): ${entry.store.conversationId}`);
|
||||||
if (!oldConversationId) {
|
} else if (entry.perChannelKeys.length > 0) {
|
||||||
p.log.info('No conversation ID stored. Nothing to reset.');
|
p.log.warn(`Current per-channel conversations (${entry.name}): ${entry.perChannelKeys.length}`);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.log.warn(`Current conversation: ${oldConversationId}`);
|
|
||||||
p.log.message('');
|
p.log.message('');
|
||||||
p.log.message('This will clear the conversation ID, causing the bot to create');
|
p.log.message('This will clear the conversation ID(s), causing the bot to create');
|
||||||
p.log.message('a new conversation on the next message. Use this if you see:');
|
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(' • "stop_reason: error" with empty responses');
|
||||||
p.log.message(' • Messages not reaching the agent');
|
p.log.message(' • Messages not reaching the agent');
|
||||||
p.log.message(' • Agent returning empty results');
|
p.log.message(' • Agent returning empty results');
|
||||||
p.log.message('');
|
p.log.message('');
|
||||||
p.log.message('The agent and its memory will be preserved.');
|
p.log.message('The agent and its memory will be preserved.');
|
||||||
|
|
||||||
const confirmed = await p.confirm({
|
const confirmed = await p.confirm({
|
||||||
message: 'Reset conversation?',
|
message: `Reset conversation${entries.length > 1 ? 's' : ''}?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed || p.isCancel(confirmed)) {
|
if (!confirmed || p.isCancel(confirmed)) {
|
||||||
p.cancel('Cancelled');
|
p.cancel('Cancelled');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.conversationId = null;
|
for (const entry of entries) {
|
||||||
writeFileSync(agentJsonPath, JSON.stringify(store, null, 2));
|
entry.store.clearConversation();
|
||||||
|
}
|
||||||
p.log.success('Conversation ID cleared');
|
|
||||||
|
p.log.success(`Conversation ID${entries.length > 1 ? 's' : ''} cleared`);
|
||||||
p.outro('Restart the server - a new conversation will be created on the next message.');
|
p.outro('Restart the server - a new conversation will be created on the next message.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user