diff --git a/package-lock.json b/package-lock.json index 2d07a0b..d0d3a0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@letta-ai/letta-code", - "version": "0.19.4", + "version": "0.19.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@letta-ai/letta-code", - "version": "0.19.4", + "version": "0.19.5", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index f4b04a3..dffbe95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@letta-ai/letta-code", - "version": "0.19.4", + "version": "0.19.5", "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.", "type": "module", "bin": { @@ -68,7 +68,7 @@ "fix": "bunx --bun @biomejs/biome@2.2.5 check --write src", "typecheck": "tsc --noEmit", "check": "bun run scripts/check.js", - "dev": "LETTA_DEBUG=1 bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts", + "dev": "LETTA_DEBUG=${LETTA_DEBUG:-1} bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts", "build": "node scripts/postinstall-patches.js && bun run build.js", "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual", "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup", diff --git a/src/cli/App.tsx b/src/cli/App.tsx index ca49d3e..23f0776 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -8130,10 +8130,14 @@ export default function App({ } // Special handling for /new command - start new conversation - if (msg.trim() === "/new") { + const newMatch = msg.trim().match(/^\/new(?:\s+(.+))?$/); + if (newMatch) { + const conversationName = newMatch[1]?.trim(); const cmd = commandRunner.start( msg.trim(), - "Starting new conversation...", + conversationName + ? `Starting new conversation: ${conversationName}...` + : "Starting new conversation...", ); // New conversations should not inherit pending reasoning-tier debounce. @@ -8150,8 +8154,14 @@ export default function App({ const conversation = await client.conversations.create({ agent_id: agentId, isolated_block_labels: [...ISOLATED_BLOCK_LABELS], + ...(conversationName && { summary: conversationName }), }); + // If we created the conversation with an explicit summary, mark it as set + // to prevent auto-summary from first user message overwriting it + if (conversationName) { + hasSetConversationSummaryRef.current = true; + } await maybeCarryOverActiveConversationModel(conversation.id); // Update conversationId state @@ -13939,6 +13949,11 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa messageHistory: resumeData.messageHistory, }; + // If the conversation already has a summary, prevent auto-summary from overwriting it + if (selectorContext?.summary) { + hasSetConversationSummaryRef.current = true; + } + settingsManager.setLocalLastSession( { agentId, conversationId: convId }, process.cwd(), diff --git a/src/cli/components/ConversationSelector.tsx b/src/cli/components/ConversationSelector.tsx index 6a5fab7..3f92e1e 100644 --- a/src/cli/components/ConversationSelector.tsx +++ b/src/cli/components/ConversationSelector.tsx @@ -441,20 +441,6 @@ export function ConversationSelector({ const bracket = {"⎿ "}; const indent = " "; // Same width as "⎿ " for alignment - // Priority 1: Summary - if (conv.summary) { - return ( - - {bracket} - - {conv.summary.length > 57 - ? `${conv.summary.slice(0, 54)}...` - : conv.summary} - - - ); - } - // Priority 2: Preview lines with emoji prefixes if (previewLines.length > 0) { return ( @@ -516,9 +502,15 @@ export function ConversationSelector({ bold={isSelected} color={isSelected ? colors.selector.itemHighlighted : undefined} > - {isDefault ? "default" : conv.id} + {conv.summary + ? `${conv.summary.length > 40 ? `${conv.summary.slice(0, 37)}...` : conv.summary} (${conv.id})` + : isDefault + ? "default" + : conv.id} - {isDefault && (agent's default conversation)} + {!conv.summary && isDefault && ( + (agent's default conversation) + )} {isCurrent && ( (current) )} diff --git a/src/cli/components/MessageSearch.tsx b/src/cli/components/MessageSearch.tsx index cb0405c..acc720c 100644 --- a/src/cli/components/MessageSearch.tsx +++ b/src/cli/components/MessageSearch.tsx @@ -116,7 +116,7 @@ export function MessageSearch({ const [searchInput, setSearchInput] = useState(initialQuery ?? ""); const [activeQuery, setActiveQuery] = useState(initialQuery ?? ""); const [searchMode, setSearchMode] = useState("hybrid"); - const [searchRange, setSearchRange] = useState("all"); + const [searchRange, setSearchRange] = useState("agent"); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); diff --git a/src/models.json b/src/models.json index c39a1d5..9de2ad9 100644 --- a/src/models.json +++ b/src/models.json @@ -1468,12 +1468,24 @@ "parallel_tool_calls": true } }, + { + "id": "minimax-m2.7", + "handle": "minimax/MiniMax-M2.7", + "label": "MiniMax 2.7", + "description": "MiniMax's latest coding model", + "isFeatured": true, + "free": true, + "updateArgs": { + "context_window": 160000, + "max_output_tokens": 64000, + "parallel_tool_calls": true + } + }, { "id": "minimax-m2.5", "handle": "minimax/MiniMax-M2.5", "label": "MiniMax 2.5", - "description": "MiniMax's latest coding model", - "isFeatured": true, + "description": "MiniMax's latest coding model (legacy)", "free": true, "updateArgs": { "context_window": 160000, diff --git a/src/tests/agent/model-preset-refresh.wiring.test.ts b/src/tests/agent/model-preset-refresh.wiring.test.ts index 6d5d011..58c4459 100644 --- a/src/tests/agent/model-preset-refresh.wiring.test.ts +++ b/src/tests/agent/model-preset-refresh.wiring.test.ts @@ -153,8 +153,9 @@ describe("model preset refresh wiring", () => { ) ?? []; expect(carryOverCalls.length).toBeGreaterThanOrEqual(3); - const newCmdAnchor = source.indexOf('if (msg.trim() === "/new")'); - expect(newCmdAnchor).toBeGreaterThanOrEqual(0); + const newCmdAnchor = source.indexOf( + "const newMatch = msg.trim().match(/^\\/new(?:\\s+(.+))?$/);", + ); const newCmdWindow = source.slice(newCmdAnchor, newCmdAnchor + 1800); expect(newCmdWindow).toContain( "await maybeCarryOverActiveConversationModel(conversation.id);", diff --git a/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts b/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts index 3f79bdd..2f8ef3c 100644 --- a/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts +++ b/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts @@ -29,7 +29,7 @@ describe("bootstrap reminder reset wiring", () => { const anchors = [ 'origin: "agent-switch"', 'const inputCmd = "/new";', // new-agent creation flow - 'if (msg.trim() === "/new")', + "const newMatch = msg.trim().match(/^\\/new(?:\\s+(.+))?$/);", 'if (msg.trim() === "/clear")', 'origin: "resume-direct"', 'if (action.type === "switch_conversation")', // queued conversation switch flow