From 5d9f2b68ff60127a053e37aee6532fb482ccbf7b Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Tue, 17 Feb 2026 16:45:28 -0800 Subject: [PATCH] fix(cli): reinject bootstrap reminders on conversation switches (#1000) Co-authored-by: Letta --- src/cli/App.tsx | 27 +++++++++++ .../bootstrap-reminders-reset-wiring.test.ts | 47 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/tests/cli/bootstrap-reminders-reset-wiring.test.ts diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 65c783e..6efe5f2 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -1474,6 +1474,12 @@ export default function App({ ); const hasInjectedSkillsRef = useRef(false); + const resetBootstrapReminderState = useCallback(() => { + hasSentSessionContextRef.current = false; + hasInjectedSkillsRef.current = false; + discoveredSkillsRef.current = null; + }, []); + // Track conversation turn count for periodic memory reminders const turnCountRef = useRef(0); @@ -5097,6 +5103,10 @@ export default function App({ setLlmConfig(agent.llm_config); setConversationId(targetConversationId); + // Ensure bootstrap reminders are re-injected on the first user turn + // after switching to a different conversation/agent context. + resetBootstrapReminderState(); + // Set conversation switch context for agent switch { const { getModelDisplayName } = await import("../agent/model"); @@ -5161,6 +5171,7 @@ export default function App({ isAgentBusy, resetDeferredToolCallCommits, resetTrajectoryBases, + resetBootstrapReminderState, ], ); @@ -5228,6 +5239,9 @@ export default function App({ // Reset context token tracking for new agent resetContextHistory(contextTrackerRef.current); + // Ensure bootstrap reminders are re-injected after creating a new agent. + resetBootstrapReminderState(); + const separator = { kind: "separator" as const, id: uid("sep"), @@ -5249,6 +5263,7 @@ export default function App({ setCommandRunning, resetDeferredToolCallCommits, resetTrajectoryBases, + resetBootstrapReminderState, ], ); @@ -6533,6 +6548,9 @@ export default function App({ // Reset context tokens for new conversation resetContextHistory(contextTrackerRef.current); + // Ensure bootstrap reminders are re-injected for the new conversation. + resetBootstrapReminderState(); + // Reset turn counter for memory reminders turnCountRef.current = 0; @@ -6611,6 +6629,9 @@ export default function App({ // Reset context tokens for new conversation resetContextHistory(contextTrackerRef.current); + // Ensure bootstrap reminders are re-injected for the new conversation. + resetBootstrapReminderState(); + // Reset turn counter for memory reminders turnCountRef.current = 0; @@ -6973,6 +6994,7 @@ export default function App({ buffersRef.current.order = []; buffersRef.current.tokenCount = 0; resetContextHistory(contextTrackerRef.current); + resetBootstrapReminderState(); emittedIdsRef.current.clear(); resetDeferredToolCallCommits(); setStaticItems([]); @@ -9994,6 +10016,7 @@ ${SYSTEM_REMINDER_CLOSE} // Reset context tokens for new conversation resetContextHistory(contextTrackerRef.current); + resetBootstrapReminderState(); cmd.finish( `Switched to conversation (${resumeData.messageHistory.length} messages)`, @@ -10034,6 +10057,7 @@ ${SYSTEM_REMINDER_CLOSE} setCommandRunning, commandRunner.getHandle, commandRunner.start, + resetBootstrapReminderState, ]); // Handle escape when profile confirmation is pending @@ -11314,6 +11338,7 @@ Plan file path: ${planFilePath}`; buffersRef.current.order = []; buffersRef.current.tokenCount = 0; resetContextHistory(contextTrackerRef.current); + resetBootstrapReminderState(); emittedIdsRef.current.clear(); resetDeferredToolCallCommits(); setStaticItems([]); @@ -11466,6 +11491,7 @@ Plan file path: ${planFilePath}`; buffersRef.current.order = []; buffersRef.current.tokenCount = 0; resetContextHistory(contextTrackerRef.current); + resetBootstrapReminderState(); emittedIdsRef.current.clear(); resetDeferredToolCallCommits(); setStaticItems([]); @@ -11606,6 +11632,7 @@ Plan file path: ${planFilePath}`; buffersRef.current.order = []; buffersRef.current.tokenCount = 0; resetContextHistory(contextTrackerRef.current); + resetBootstrapReminderState(); emittedIdsRef.current.clear(); resetDeferredToolCallCommits(); setStaticItems([]); diff --git a/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts b/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts new file mode 100644 index 0000000..9e9467d --- /dev/null +++ b/src/tests/cli/bootstrap-reminders-reset-wiring.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + +describe("bootstrap reminder reset wiring", () => { + test("defines helper that clears session, skills, and discovery cache", () => { + const appPath = fileURLToPath( + new URL("../../cli/App.tsx", import.meta.url), + ); + const source = readFileSync(appPath, "utf-8"); + + expect(source).toContain( + "const resetBootstrapReminderState = useCallback(() => {", + ); + expect(source).toContain("hasSentSessionContextRef.current = false;"); + expect(source).toContain("hasInjectedSkillsRef.current = false;"); + expect(source).toContain("discoveredSkillsRef.current = null;"); + }); + + test("invokes helper for all conversation/agent switch entry points", () => { + const appPath = fileURLToPath( + new URL("../../cli/App.tsx", import.meta.url), + ); + const source = readFileSync(appPath, "utf-8"); + + const anchors = [ + 'origin: "agent-switch"', + 'const inputCmd = "/new";', // new-agent creation flow + 'if (msg.trim() === "/new")', + 'if (msg.trim() === "/clear")', + 'origin: "resume-direct"', + 'if (action.type === "switch_conversation")', // queued conversation switch flow + 'origin: "resume-selector"', + "onNewConversation={async () => {", + 'origin: "search"', + ]; + + for (const anchor of anchors) { + const anchorIndex = source.indexOf(anchor); + expect(anchorIndex).toBeGreaterThanOrEqual(0); + + const windowEnd = Math.min(source.length, anchorIndex + 5000); + const scoped = source.slice(anchorIndex, windowEnd); + expect(scoped).toContain("resetBootstrapReminderState();"); + } + }); +});