feat: Personalize thinking prompts with agent name (#234)

This commit is contained in:
Cameron
2025-12-16 14:11:17 -08:00
committed by GitHub
parent c0e5abaff1
commit 562246d022
3 changed files with 116 additions and 48 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 <verb>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<string>();
// 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);
});
});