refactor: use conversations (#475)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-01-13 16:40:59 -08:00
committed by GitHub
parent 3615247d14
commit ef7d8c98df
26 changed files with 1572 additions and 168 deletions

129
src/agent/defaults.ts Normal file
View File

@@ -0,0 +1,129 @@
/**
* Default agents (Memo & Incognito) creation and management.
*
* Memo: Stateful agent with full memory - learns and grows with the user.
* Incognito: Stateless agent - fresh experience without accumulated memory.
*/
import type { Letta } from "@letta-ai/letta-client";
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
import { settingsManager } from "../settings-manager";
import { type CreateAgentOptions, createAgent } from "./create";
import { parseMdxFrontmatter } from "./memory";
import { MEMORY_PROMPTS } from "./promptAssets";
// Tags used to identify default agents
const MEMO_TAG = "default:memo";
const INCOGNITO_TAG = "default:incognito";
// Memo's persona - loaded from persona_memo.mdx
const MEMO_PERSONA = parseMdxFrontmatter(
MEMORY_PROMPTS["persona_memo.mdx"] ?? "",
).body;
// Agent descriptions shown in /agents selector
const MEMO_DESCRIPTION = "A stateful coding agent with persistent memory";
const INCOGNITO_DESCRIPTION =
"A stateless coding agent without memory (incognito mode)";
/**
* Default agent configurations.
*/
export const DEFAULT_AGENT_CONFIGS: Record<string, CreateAgentOptions> = {
memo: {
name: "Memo",
description: MEMO_DESCRIPTION,
// Uses default memory blocks and tools (full stateful config)
// Override persona block with Memo-specific personality
blockValues: {
persona: MEMO_PERSONA,
},
},
incognito: {
name: "Incognito",
description: INCOGNITO_DESCRIPTION,
initBlocks: ["skills", "loaded_skills"], // Only skills blocks, no personal memory
baseTools: ["web_search", "conversation_search", "fetch_webpage", "Skill"], // No memory tool
},
};
/**
* Check if a default agent exists by its tag.
*/
async function findDefaultAgent(
client: Letta,
tag: string,
): Promise<AgentState | null> {
try {
const result = await client.agents.list({ tags: [tag], limit: 1 });
return result.items[0] ?? null;
} catch {
return null;
}
}
/**
* Add a tag to an existing agent.
*/
async function addTagToAgent(
client: Letta,
agentId: string,
newTag: string,
): Promise<void> {
try {
const agent = await client.agents.retrieve(agentId);
const currentTags = agent.tags || [];
if (!currentTags.includes(newTag)) {
await client.agents.update(agentId, {
tags: [...currentTags, newTag],
});
}
} catch (err) {
console.warn(
`Warning: Failed to add tag to agent: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
/**
* Ensure default agents exist. Creates missing ones and pins them globally.
* Respects `createDefaultAgents` setting (defaults to true).
*
* @returns The Memo agent (or null if creation disabled/failed).
*/
export async function ensureDefaultAgents(
client: Letta,
): Promise<AgentState | null> {
if (!settingsManager.shouldCreateDefaultAgents()) {
return null;
}
let memoAgent: AgentState | null = null;
try {
// Check/create Memo
const existingMemo = await findDefaultAgent(client, MEMO_TAG);
if (existingMemo) {
memoAgent = existingMemo;
} else {
const { agent } = await createAgent(DEFAULT_AGENT_CONFIGS.memo);
await addTagToAgent(client, agent.id, MEMO_TAG);
memoAgent = agent;
settingsManager.pinGlobal(agent.id);
}
// Check/create Incognito
const existingIncognito = await findDefaultAgent(client, INCOGNITO_TAG);
if (!existingIncognito) {
const { agent } = await createAgent(DEFAULT_AGENT_CONFIGS.incognito);
await addTagToAgent(client, agent.id, INCOGNITO_TAG);
settingsManager.pinGlobal(agent.id);
}
} catch (err) {
console.warn(
`Warning: Failed to ensure default agents: ${err instanceof Error ? err.message : String(err)}`,
);
}
return memoAgent;
}