From 562246d022a575d5c36cbde3b7a41afb3a77c16a Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 16 Dec 2025 14:11:17 -0800 Subject: [PATCH] feat: Personalize thinking prompts with agent name (#234) --- src/cli/App.tsx | 37 ++++++++---- src/cli/helpers/thinkingMessages.ts | 81 ++++++++++++++------------ src/tests/cli/thinkingMessages.test.ts | 46 +++++++++++++++ 3 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 src/tests/cli/thinkingMessages.test.ts diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 961fe0b..1bed59f 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -426,7 +426,7 @@ export default function App({ // Current thinking message (rotates each turn) const [thinkingMessage, setThinkingMessage] = useState( - getRandomThinkingMessage(), + getRandomThinkingMessage(agentName), ); // Session stats tracking @@ -1108,7 +1108,7 @@ export default function App({ } // Rotate to a new thinking message - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); refreshDerived(); await processConversation([ @@ -1270,7 +1270,7 @@ export default function App({ abortControllerRef.current = null; } }, - [appendError, refreshDerived, refreshDerivedThrottled], + [appendError, refreshDerived, refreshDerivedThrottled, agentName], ); const handleExit = useCallback(() => { @@ -2578,7 +2578,7 @@ ${recentCommits} // Reset token counter for this turn (only count the agent's response) buffersRef.current.tokenCount = 0; // Rotate to a new thinking message for this turn - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); // Show streaming state immediately for responsiveness setStreaming(true); refreshDerived(); @@ -2854,7 +2854,7 @@ ${recentCommits} } // Rotate to a new thinking message - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); refreshDerived(); const wasAborted = approvalAbortController.signal.aborted; @@ -2891,6 +2891,7 @@ ${recentCommits} processConversation, refreshDerived, appendError, + agentName, ], ); @@ -3005,7 +3006,7 @@ ${recentCommits} if (currentIndex + 1 >= pendingApprovals.length) { // All approvals collected, execute and send to backend // sendAllResults owns the lock release via its finally block - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); await sendAllResults(decision); } else { // Not done yet, store decision and show next approval @@ -3026,6 +3027,7 @@ ${recentCommits} sendAllResults, appendError, isExecutingTool, + agentName, ], ); @@ -3384,7 +3386,7 @@ ${recentCommits} stderr: toolResult.stderr, }); - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); refreshDerived(); const decision = { @@ -3412,6 +3414,7 @@ ${recentCommits} sendAllResults, appendError, refreshDerived, + agentName, ], ); @@ -3493,7 +3496,7 @@ ${recentCommits} stderr: null, }); - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); refreshDerived(); const decision = { @@ -3509,7 +3512,13 @@ ${recentCommits} setApprovalResults((prev) => [...prev, decision]); } }, - [pendingApprovals, approvalResults, sendAllResults, refreshDerived], + [ + pendingApprovals, + approvalResults, + sendAllResults, + refreshDerived, + agentName, + ], ); const handleEnterPlanModeApprove = useCallback(async () => { @@ -3559,7 +3568,7 @@ Plan file path: ${planFilePath}`; stderr: null, }); - setThinkingMessage(getRandomThinkingMessage()); + setThinkingMessage(getRandomThinkingMessage(agentName)); refreshDerived(); const decision = { @@ -3574,7 +3583,13 @@ Plan file path: ${planFilePath}`; } else { setApprovalResults((prev) => [...prev, decision]); } - }, [pendingApprovals, approvalResults, sendAllResults, refreshDerived]); + }, [ + pendingApprovals, + approvalResults, + sendAllResults, + refreshDerived, + agentName, + ]); const handleEnterPlanModeReject = useCallback(async () => { const currentIndex = approvalResults.length; diff --git a/src/cli/helpers/thinkingMessages.ts b/src/cli/helpers/thinkingMessages.ts index 5204bd6..012e5e4 100644 --- a/src/cli/helpers/thinkingMessages.ts +++ b/src/cli/helpers/thinkingMessages.ts @@ -1,41 +1,48 @@ -// Machine god AI themed thinking messages -const THINKING_MESSAGES = [ - "Thinking", - "Processing", - "Computing", - "Calculating", - "Analyzing", - "Synthesizing", - "Deliberating", - "Cogitating", - "Reflecting", - "Reasoning", - "Spinning", - "Focusing", - "Machinating", - "Contemplating", - "Ruminating", - "Considering", - "Pondering", - "Evaluating", - "Assessing", - "Inferring", - "Deducing", - "Interpreting", - "Formulating", - "Strategizing", - "Orchestrating", - "Optimizing", - "Calibrating", - "Indexing", - "Compiling", - "Rendering", - "Executing", - "Initializing", +// Machine god AI themed thinking verbs +const THINKING_VERBS = [ + "thinking", + "processing", + "computing", + "calculating", + "analyzing", + "synthesizing", + "deliberating", + "cogitating", + "reflecting", + "reasoning", + "spinning", + "focusing", + "machinating", + "contemplating", + "ruminating", + "considering", + "pondering", + "evaluating", + "assessing", + "inferring", + "deducing", + "interpreting", + "formulating", + "strategizing", + "orchestrating", + "optimizing", + "calibrating", + "indexing", + "compiling", + "rendering", + "executing", + "initializing", ] as const; // Get a random thinking message -export function getRandomThinkingMessage(): string { - const index = Math.floor(Math.random() * THINKING_MESSAGES.length); - return THINKING_MESSAGES[index] ?? "Thinking"; +export function getRandomThinkingMessage(agentName?: string | null): string { + const index = Math.floor(Math.random() * THINKING_VERBS.length); + const verb = THINKING_VERBS[index] ?? "thinking"; + + if (agentName) { + return `${agentName} is ${verb}`; + } + + // Fallback to capitalized verb if no agent name + return verb.charAt(0).toUpperCase() + verb.slice(1); } diff --git a/src/tests/cli/thinkingMessages.test.ts b/src/tests/cli/thinkingMessages.test.ts new file mode 100644 index 0000000..92a54ee --- /dev/null +++ b/src/tests/cli/thinkingMessages.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, test } from "bun:test"; +import { getRandomThinkingMessage } from "../../cli/helpers/thinkingMessages"; + +describe("Thinking messages", () => { + test("returns formatted message with agent name", () => { + const message = getRandomThinkingMessage("Letta"); + + // Should be in format "Letta is ing" + expect(message).toMatch(/^Letta is \w+$/); + expect(message.startsWith("Letta is ")).toBe(true); + }); + + test("returns capitalized verb without agent name", () => { + const message = getRandomThinkingMessage(); + + // Should be a capitalized verb (e.g., "Thinking", "Processing") + expect(message).toMatch(/^[A-Z][a-z]+$/); + expect(message[0]).toMatch(/[A-Z]/); + }); + + test("handles null agent name", () => { + const message = getRandomThinkingMessage(null); + + // Should fall back to capitalized verb + expect(message).toMatch(/^[A-Z][a-z]+$/); + }); + + test("handles empty string agent name", () => { + const message = getRandomThinkingMessage(""); + + // Should fall back to capitalized verb (empty string is falsy) + expect(message).toMatch(/^[A-Z][a-z]+$/); + }); + + test("generates different messages on multiple calls", () => { + const messages = new Set(); + + // Generate 10 messages, should get some variety + for (let i = 0; i < 10; i++) { + messages.add(getRandomThinkingMessage("Agent")); + } + + // Should have more than 1 unique message (with high probability) + expect(messages.size).toBeGreaterThan(1); + }); +});