From 43cf63e1892ec1932669c3c3af180cb11aee9d81 Mon Sep 17 00:00:00 2001 From: anj-s <32556631+anj-s@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:29:09 -0700 Subject: [PATCH] fix: update task tracker storage location in system prompt (#24034) --- evals/tracker.eval.ts | 17 ++++++++++ .../core/__snapshots__/prompts.test.ts.snap | 4 +-- packages/core/src/core/prompts.test.ts | 3 ++ .../core/src/prompts/promptProvider.test.ts | 33 +++++++++++++++++++ packages/core/src/prompts/promptProvider.ts | 15 +++++++-- packages/core/src/prompts/snippets.legacy.ts | 11 +++---- packages/core/src/prompts/snippets.ts | 11 +++---- 7 files changed, 77 insertions(+), 17 deletions(-) diff --git a/evals/tracker.eval.ts b/evals/tracker.eval.ts index 7afb41dbec..49bc903b0a 100644 --- a/evals/tracker.eval.ts +++ b/evals/tracker.eval.ts @@ -113,4 +113,21 @@ describe('tracker_mode', () => { assertModelHasOutput(result); }, }); + + evalTest('USUALLY_PASSES', { + name: 'should correctly identify the task tracker storage location from the system prompt', + params: { + settings: { experimental: { taskTracker: true } }, + }, + prompt: + 'Where is my task tracker storage located? Please provide the absolute path in your response.', + assert: async (rig, result) => { + // The rig sets GEMINI_CLI_HOME to rig.homeDir + const homeDir = rig.homeDir!; + // The response should contain the dynamic path which includes the home directory + // and follows the .gemini/tmp/.../tracker structure. + expect(result).toContain(homeDir); + expect(result).toMatch(/\.gemini\/tmp\/.*\/tracker/); + }, + }); }); diff --git a/packages/core/src/core/__snapshots__/prompts.test.ts.snap b/packages/core/src/core/__snapshots__/prompts.test.ts.snap index f95a4cc8df..91e2573e62 100644 --- a/packages/core/src/core/__snapshots__/prompts.test.ts.snap +++ b/packages/core/src/core/__snapshots__/prompts.test.ts.snap @@ -2899,7 +2899,7 @@ Use 'read_file' to understand context and validate any assumptions you may have. 6. **Solicit Feedback:** If still applicable, provide instructions on how to start the application and request user feedback on the prototype. # TASK MANAGEMENT PROTOCOL -You are operating with a persistent file-based task tracking system located at \`.tracker/tasks/\`. You must adhere to the following rules: +You are operating with a persistent file-based task tracking system located at \`/mock/.gemini/tmp/session/tracker\`. You must adhere to the following rules: 1. **NO IN-MEMORY LISTS**: Do not maintain a mental list of tasks or write markdown checkboxes in the chat. Use the provided tools (\`tracker_create_task\`, \`tracker_list_tasks\`, \`tracker_update_task\`) for all state management. 2. **IMMEDIATE DECOMPOSITION**: Upon receiving a task, evaluate its functional complexity and scope. If the request involves more than a single atomic modification, or necessitates research before execution, you MUST immediately decompose it into discrete entries using \`tracker_create_task\`. @@ -3079,7 +3079,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi 5. **Solicit Feedback:** Provide instructions on how to start the application and request user feedback on the prototype. # TASK MANAGEMENT PROTOCOL -You are operating with a persistent file-based task tracking system located at \`.tracker/tasks/\`. You must adhere to the following rules: +You are operating with a persistent file-based task tracking system located at \`/mock/.gemini/tmp/session/tracker\`. You must adhere to the following rules: 1. **NO IN-MEMORY LISTS**: Do not maintain a mental list of tasks or write markdown checkboxes in the chat. Use the provided tools (\`tracker_create_task\`, \`tracker_list_tasks\`, \`tracker_update_task\`) for all state management. 2. **IMMEDIATE DECOMPOSITION**: Upon receiving a task, evaluate its functional complexity and scope. If the request involves more than a single atomic modification, or necessitates research before execution, you MUST immediately decompose it into discrete entries using \`tracker_create_task\`. diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index 6e505dfa2b..c8f5fe6cc7 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -93,6 +93,9 @@ describe('Core System Prompt (prompts.ts)', () => { storage: { getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'), getPlansDir: vi.fn().mockReturnValue('/tmp/project-temp/plans'), + getProjectTempTrackerDir: vi + .fn() + .mockReturnValue('/mock/.gemini/tmp/session/tracker'), }, isInteractive: vi.fn().mockReturnValue(true), isInteractiveShellEnabled: vi.fn().mockReturnValue(true), diff --git a/packages/core/src/prompts/promptProvider.test.ts b/packages/core/src/prompts/promptProvider.test.ts index 554bad2003..2f82ae56a4 100644 --- a/packages/core/src/prompts/promptProvider.test.ts +++ b/packages/core/src/prompts/promptProvider.test.ts @@ -64,6 +64,9 @@ describe('PromptProvider', () => { storage: { getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'), getPlansDir: vi.fn().mockReturnValue('/tmp/project-temp/plans'), + getProjectTempTrackerDir: vi + .fn() + .mockReturnValue('/tmp/project-temp/tracker'), }, isInteractive: vi.fn().mockReturnValue(true), isInteractiveShellEnabled: vi.fn().mockReturnValue(true), @@ -104,6 +107,36 @@ describe('PromptProvider', () => { ); }); + it('should include the task tracker storage location in the system prompt', () => { + vi.mocked(mockConfig.isTrackerEnabled).mockReturnValue(true); + const mockTrackerDir = '/mock/tracker/path'; + vi.mocked(mockConfig.storage.getProjectTempTrackerDir).mockReturnValue( + mockTrackerDir, + ); + + const provider = new PromptProvider(); + const prompt = provider.getCoreSystemPrompt(mockConfig); + + expect(prompt).toContain('# TASK MANAGEMENT PROTOCOL'); + expect(prompt).toContain(`located at \`${mockTrackerDir}\``); + }); + + it('should sanitize the task tracker storage location in the system prompt', () => { + vi.mocked(mockConfig.isTrackerEnabled).mockReturnValue(true); + const mockTrackerDir = '/mock/tracker/path\nwith-newline]and-bracket'; + vi.mocked(mockConfig.storage.getProjectTempTrackerDir).mockReturnValue( + mockTrackerDir, + ); + + const provider = new PromptProvider(); + const prompt = provider.getCoreSystemPrompt(mockConfig); + + expect(prompt).toContain('# TASK MANAGEMENT PROTOCOL'); + expect(prompt).toContain( + 'located at `/mock/tracker/path with-newlineand-bracket`', + ); + }); + it('should handle multiple context filenames in user memory section', () => { vi.mocked(getAllGeminiMdFilenames).mockReturnValue([ DEFAULT_CONTEXT_FILENAME, diff --git a/packages/core/src/prompts/promptProvider.ts b/packages/core/src/prompts/promptProvider.ts index 3425809583..0036dae560 100644 --- a/packages/core/src/prompts/promptProvider.ts +++ b/packages/core/src/prompts/promptProvider.ts @@ -72,6 +72,16 @@ export class PromptProvider { const activeSnippets = isModernModel ? snippets : legacySnippets; const contextFilenames = getAllGeminiMdFilenames(); + let trackerDir = context.config.isTrackerEnabled() + ? context.config.storage.getProjectTempTrackerDir() + : undefined; + + if (trackerDir) { + // Sanitize path to prevent prompt injection + trackerDir = trackerDir.replace(/\n/g, ' ').replace(/\]/g, ''); + } + + // --- Context Gathering --- let planModeToolsList = ''; if (isPlanMode) { const allTools = context.toolRegistry.getAllTools(); @@ -149,7 +159,7 @@ export class PromptProvider { })), skills.length > 0, ), - taskTracker: context.config.isTrackerEnabled(), + taskTracker: trackerDir, hookContext: isSectionEnabled('hookContext') || undefined, primaryWorkflows: this.withSection( 'primaryWorkflows', @@ -167,7 +177,7 @@ export class PromptProvider { approvedPlan: approvedPlanPath ? { path: approvedPlanPath } : undefined, - taskTracker: context.config.isTrackerEnabled(), + taskTracker: trackerDir, topicUpdateNarration: context.config.isTopicUpdateNarrationEnabled(), }), @@ -180,7 +190,6 @@ export class PromptProvider { planModeToolsList, plansDir: context.config.storage.getPlansDir(), approvedPlanPath: context.config.getApprovedPlanPath(), - taskTracker: context.config.isTrackerEnabled(), }), isPlanMode, ), diff --git a/packages/core/src/prompts/snippets.legacy.ts b/packages/core/src/prompts/snippets.legacy.ts index 5b97886046..4fea88937b 100644 --- a/packages/core/src/prompts/snippets.legacy.ts +++ b/packages/core/src/prompts/snippets.legacy.ts @@ -37,7 +37,7 @@ export interface SystemPromptOptions { hookContext?: boolean; primaryWorkflows?: PrimaryWorkflowsOptions; planningWorkflow?: PlanningWorkflowOptions; - taskTracker?: boolean; + taskTracker?: string; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxOptions; interactiveYoloMode?: boolean; @@ -63,7 +63,7 @@ export interface PrimaryWorkflowsOptions { enableWriteTodosTool: boolean; enableEnterPlanModeTool: boolean; approvedPlan?: { path: string }; - taskTracker?: boolean; + taskTracker?: string; topicUpdateNarration?: boolean; } @@ -95,7 +95,6 @@ export interface PlanningWorkflowOptions { planModeToolsList: string; plansDir: string; approvedPlanPath?: string; - taskTracker?: boolean; } export interface AgentSkillOptions { @@ -132,7 +131,7 @@ ${ : renderPrimaryWorkflows(options.primaryWorkflows) } -${options.taskTracker ? renderTaskTracker() : ''} +${options.taskTracker ? renderTaskTracker(options.taskTracker) : ''} ${renderOperationalGuidelines(options.operationalGuidelines)} @@ -491,10 +490,10 @@ An approved plan is available for this task. `; } -export function renderTaskTracker(): string { +export function renderTaskTracker(trackerDir: string): string { return ` # TASK MANAGEMENT PROTOCOL -You are operating with a persistent file-based task tracking system located at \`.tracker/tasks/\`. You must adhere to the following rules: +You are operating with a persistent file-based task tracking system located at \`${trackerDir}\`. You must adhere to the following rules: 1. **NO IN-MEMORY LISTS**: Do not maintain a mental list of tasks or write markdown checkboxes in the chat. Use the provided tools (\`${TRACKER_CREATE_TASK_TOOL_NAME}\`, \`${TRACKER_LIST_TASKS_TOOL_NAME}\`, \`${TRACKER_UPDATE_TASK_TOOL_NAME}\`) for all state management. 2. **IMMEDIATE DECOMPOSITION**: Upon receiving a task, evaluate its functional complexity and scope. If the request involves more than a single atomic modification, or necessitates research before execution, you MUST immediately decompose it into discrete entries using \`${TRACKER_CREATE_TASK_TOOL_NAME}\`. diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index 77e397d5ca..5440583419 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -47,7 +47,7 @@ export interface SystemPromptOptions { hookContext?: boolean; primaryWorkflows?: PrimaryWorkflowsOptions; planningWorkflow?: PlanningWorkflowOptions; - taskTracker?: boolean; + taskTracker?: string; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxOptions; interactiveYoloMode?: boolean; @@ -74,7 +74,7 @@ export interface PrimaryWorkflowsOptions { enableGrep: boolean; enableGlob: boolean; approvedPlan?: { path: string }; - taskTracker?: boolean; + taskTracker?: string; topicUpdateNarration: boolean; } @@ -101,7 +101,6 @@ export interface PlanningWorkflowOptions { planModeToolsList: string; plansDir: string; approvedPlanPath?: string; - taskTracker?: boolean; } export interface AgentSkillOptions { @@ -139,7 +138,7 @@ ${ : renderPrimaryWorkflows(options.primaryWorkflows) } -${options.taskTracker ? renderTaskTracker() : ''} +${options.taskTracker ? renderTaskTracker(options.taskTracker) : ''} ${renderOperationalGuidelines(options.operationalGuidelines)} @@ -537,14 +536,14 @@ ${trimmed} return `\n---\n\n\n${sections.join('\n')}\n`; } -export function renderTaskTracker(): string { +export function renderTaskTracker(trackerDir: string): string { const trackerCreate = formatToolName(TRACKER_CREATE_TASK_TOOL_NAME); const trackerList = formatToolName(TRACKER_LIST_TASKS_TOOL_NAME); const trackerUpdate = formatToolName(TRACKER_UPDATE_TASK_TOOL_NAME); return ` # TASK MANAGEMENT PROTOCOL -You are operating with a persistent file-based task tracking system located at \`.tracker/tasks/\`. You must adhere to the following rules: +You are operating with a persistent file-based task tracking system located at \`${trackerDir}\`. You must adhere to the following rules: 1. **NO IN-MEMORY LISTS**: Do not maintain a mental list of tasks or write markdown checkboxes in the chat. Use the provided tools (${trackerCreate}, ${trackerList}, ${trackerUpdate}) for all state management. 2. **IMMEDIATE DECOMPOSITION**: Upon receiving a task, evaluate its functional complexity and scope. If the request involves more than a single atomic modification, or necessitates research before execution, you MUST immediately decompose it into discrete entries using ${trackerCreate}.