From 72f172d9d984011ff85927f93bea0d915c66b1a7 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Thu, 15 Jan 2026 18:08:31 -0800 Subject: [PATCH] fix: pin existing default agents and rename --new to --new-agent (#556) Co-authored-by: Letta --- src/agent/defaults.ts | 7 ++++++- src/cli/App.tsx | 1 + src/cli/components/ConversationSelector.tsx | 6 +++++- src/headless.ts | 14 +++++++++++-- src/index.ts | 20 ++++++++++++++++--- src/tests/headless-input-format.test.ts | 2 +- src/tests/headless-scenario.ts | 2 +- src/tests/headless-stream-json-format.test.ts | 2 +- src/tests/headless-windows.ts | 2 +- 9 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/agent/defaults.ts b/src/agent/defaults.ts index 2987449..fe1bf1c 100644 --- a/src/agent/defaults.ts +++ b/src/agent/defaults.ts @@ -105,6 +105,8 @@ export async function ensureDefaultAgents( const existingMemo = await findDefaultAgent(client, MEMO_TAG); if (existingMemo) { memoAgent = existingMemo; + // Ensure it's pinned (might not be if settings were cleared or new machine) + settingsManager.pinGlobal(existingMemo.id); } else { const { agent } = await createAgent(DEFAULT_AGENT_CONFIGS.memo); await addTagToAgent(client, agent.id, MEMO_TAG); @@ -114,7 +116,10 @@ export async function ensureDefaultAgents( // Check/create Incognito const existingIncognito = await findDefaultAgent(client, INCOGNITO_TAG); - if (!existingIncognito) { + if (existingIncognito) { + // Ensure it's pinned (might not be if settings were cleared or new machine) + settingsManager.pinGlobal(existingIncognito.id); + } else { const { agent } = await createAgent(DEFAULT_AGENT_CONFIGS.incognito); await addTagToAgent(client, agent.id, INCOGNITO_TAG); settingsManager.pinGlobal(agent.id); diff --git a/src/cli/App.tsx b/src/cli/App.tsx index f1d0277..261485c 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -7414,6 +7414,7 @@ Plan file path: ${planFilePath}`; {activeOverlay === "conversations" && ( { closeOverlay(); diff --git a/src/cli/components/ConversationSelector.tsx b/src/cli/components/ConversationSelector.tsx index 0dbd3e1..337ff43 100644 --- a/src/cli/components/ConversationSelector.tsx +++ b/src/cli/components/ConversationSelector.tsx @@ -13,6 +13,7 @@ const SOLID_LINE = "─"; interface ConversationSelectorProps { agentId: string; + agentName?: string; currentConversationId: string; onSelect: (conversationId: string) => void; onNewConversation: () => void; @@ -186,6 +187,7 @@ function getMessageStats(messages: Message[]): { export function ConversationSelector({ agentId, + agentName, currentConversationId, onSelect, onNewConversation, @@ -511,7 +513,9 @@ export function ConversationSelector({ {/* Empty state */} {!loading && !error && conversations.length === 0 && ( - No conversations found + + No conversations for {agentName || agentId.slice(0, 12)} + Press N to start a new conversation )} diff --git a/src/headless.ts b/src/headless.ts index 2c124d3..937aa45 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -68,7 +68,8 @@ export async function handleHeadlessCommand( continue: { type: "boolean", short: "c" }, resume: { type: "boolean", short: "r" }, conversation: { type: "string" }, - new: { type: "boolean" }, + "new-agent": { type: "boolean" }, + new: { type: "boolean" }, // Deprecated - kept for helpful error message agent: { type: "string", short: "a" }, model: { type: "string", short: "m" }, system: { type: "string", short: "s" }, @@ -180,12 +181,21 @@ export async function handleHeadlessCommand( process.exit(1); } + // Check for deprecated --new flag + if (values.new) { + console.error( + "Error: --new has been renamed to --new-agent\n" + + 'Usage: letta -p "..." --new-agent', + ); + process.exit(1); + } + // Resolve agent (same logic as interactive mode) let agent: AgentState | null = null; const specifiedAgentId = values.agent as string | undefined; const specifiedConversationId = values.conversation as string | undefined; const shouldContinue = values.continue as boolean | undefined; - const forceNew = values.new as boolean | undefined; + const forceNew = values["new-agent"] as boolean | undefined; const systemPromptPreset = values.system as string | undefined; const systemCustom = values["system-custom"] as string | undefined; const systemAppend = values["system-append"] as string | undefined; diff --git a/src/index.ts b/src/index.ts index 638d0a5..3f350a9 100755 --- a/src/index.ts +++ b/src/index.ts @@ -344,7 +344,8 @@ async function main(): Promise { continue: { type: "boolean" }, // Deprecated - kept for error message resume: { type: "boolean", short: "r" }, // Resume last session (or specific conversation with --conversation) conversation: { type: "string", short: "C" }, // Specific conversation ID to resume (--conv alias supported) - new: { type: "boolean" }, + "new-agent": { type: "boolean" }, // Force create a new agent + new: { type: "boolean" }, // Deprecated - kept for helpful error message "init-blocks": { type: "string" }, "base-tools": { type: "string" }, agent: { type: "string", short: "a" }, @@ -426,7 +427,17 @@ async function main(): Promise { const shouldResume = (values.resume as boolean | undefined) ?? false; const specifiedConversationId = (values.conversation as string | undefined) ?? null; // Specific conversation to resume - const forceNew = (values.new as boolean | undefined) ?? false; + const forceNew = (values["new-agent"] as boolean | undefined) ?? false; + + // Check for deprecated --new flag + if (values.new) { + console.error( + "Error: --new has been renamed to --new-agent\n" + + "Usage: letta --new-agent", + ); + process.exit(1); + } + const initBlocksRaw = values["init-blocks"] as string | undefined; const baseToolsRaw = values["base-tools"] as string | undefined; let specifiedAgentId = (values.agent as string | undefined) ?? null; @@ -838,6 +849,7 @@ async function main(): Promise { >(null); // Track agent and conversation for conversation selector (--resume flag) const [resumeAgentId, setResumeAgentId] = useState(null); + const [resumeAgentName, setResumeAgentName] = useState(null); const [selectedConversationId, setSelectedConversationId] = useState< string | null >(null); @@ -969,8 +981,9 @@ async function main(): Promise { if (lastAgentId) { // Verify agent exists try { - await client.agents.retrieve(lastAgentId); + const agent = await client.agents.retrieve(lastAgentId); setResumeAgentId(lastAgentId); + setResumeAgentName(agent.name ?? null); setLoadingState("selecting_conversation"); return; } catch { @@ -1493,6 +1506,7 @@ async function main(): Promise { if (loadingState === "selecting_conversation" && resumeAgentId) { return React.createElement(ConversationSelector, { agentId: resumeAgentId, + agentName: resumeAgentName ?? undefined, currentConversationId: "", // No current conversation yet onSelect: (conversationId: string) => { setSelectedConversationId(conversationId); diff --git a/src/tests/headless-input-format.test.ts b/src/tests/headless-input-format.test.ts index 69e549b..8a6eae7 100644 --- a/src/tests/headless-input-format.test.ts +++ b/src/tests/headless-input-format.test.ts @@ -38,7 +38,7 @@ async function runBidirectional( "stream-json", "--output-format", "stream-json", - "--new", + "--new-agent", "-m", "haiku", "--yolo", diff --git a/src/tests/headless-scenario.ts b/src/tests/headless-scenario.ts index e5929aa..7a73e39 100644 --- a/src/tests/headless-scenario.ts +++ b/src/tests/headless-scenario.ts @@ -70,7 +70,7 @@ async function runCLI( "-p", scenarioPrompt(), "--yolo", - "--new", + "--new-agent", "--output-format", output, "-m", diff --git a/src/tests/headless-stream-json-format.test.ts b/src/tests/headless-stream-json-format.test.ts index e21ad5c..1953bee 100644 --- a/src/tests/headless-stream-json-format.test.ts +++ b/src/tests/headless-stream-json-format.test.ts @@ -22,7 +22,7 @@ async function runHeadlessCommand( [ "run", "dev", - "--new", + "--new-agent", "-p", prompt, "--output-format", diff --git a/src/tests/headless-windows.ts b/src/tests/headless-windows.ts index 1712725..5ed71c5 100644 --- a/src/tests/headless-windows.ts +++ b/src/tests/headless-windows.ts @@ -61,7 +61,7 @@ async function runCLI( "-p", windowsScenarioPrompt(), "--yolo", - "--new", + "--new-agent", "--output-format", "text", "-m",