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/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