diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index 4e0cd8854c..99cd794cc9 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -11,6 +11,8 @@ import { ENTER_PLAN_MODE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, CREATE_NEW_TOPIC_TOOL_NAME, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, GLOB_TOOL_NAME, GREP_TOOL_NAME, MEMORY_TOOL_NAME, @@ -351,7 +353,7 @@ export function renderOperationalGuidelines( - **Role:** A senior software engineer and collaborative peer programmer. - **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and ${ options.topicUpdateNarration - ? 'per-tool explanations.' + ? 'unnecessary per-tool explanations.' : 'mechanical tool-use narration (e.g., "I will now call...").' } - **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment. @@ -583,18 +585,20 @@ function mandateTopicUpdateModel(): string { - **1. Chapter Initialization:** - **The Trigger:** You MUST call \`${CREATE_NEW_TOPIC_TOOL_NAME}\` ONLY when beginning a new task or when the broad logical nature of your work changes (e.g., transitioning from Research to Strategy, or from Strategy to Implementation). - - **The Format:** Provide a concise, high-level title for the chapter (e.g., \`create_new_topic(title="Researching Agent Skills")\`). - - **Start of Task:** Your very first tool execution in a new session must be \`${CREATE_NEW_TOPIC_TOOL_NAME}\`. + - **Granularity:** You MUST NOT combine topics (e.g., do NOT use "Researching and Implementing"). Use granular, single-focus titles like "Researching [Task]", "Strategy for [Task]", or "Implementing [Specific Idea]". + - **Deviations:** If your work deviates from the current topic's stated goal, you MUST call \`${CREATE_NEW_TOPIC_TOOL_NAME}\` again with a new title (e.g., "Implementing [New Idea]") to reflect the shift. + - **The Format:** Provide a concise, high-level title for the chapter. You MUST also provide a detailed explanation (5-10 sentences) of what the new topic intends to achieve in the \`${TOPIC_PARAM_CURRENT_SUMMARY}\` parameter. When shifting topics, you MUST additionally provide a detailed summary (5-10 sentences) of the work completed in the previous topic in the \`${TOPIC_PARAM_PREVIOUS_SUMMARY}\` parameter. + - **Example:** \`create_new_topic(title="Implementing Auth", ${TOPIC_PARAM_CURRENT_SUMMARY}="Implement the OAuth2 flow and link it to the existing user session. This involves creating new routes for the callback and state management for the token. We will also need to update the middleware to handle authenticated sessions correctly. This will ensure that all subsequent API calls are properly authorized and user-specific. The implementation will follow the project's security standards and include unit tests for each new component.", ${TOPIC_PARAM_PREVIOUS_SUMMARY}="Completed research on OAuth2 and mapped endpoints. We identified the necessary client libraries and configured the initial developer credentials for the authentication provider. A prototype script was written to verify the handshake process with the API. The existing user database schema was reviewed and found to be compatible with the new OAuth IDs. We also finalized the design of the new login UI and obtained user feedback on the mockups.")\` + - **Start of Task:** Your very first tool execution in a new session must be \`${CREATE_NEW_TOPIC_TOOL_NAME}\`. For the first topic, leave \`${TOPIC_PARAM_PREVIOUS_SUMMARY}\` empty but you MUST still provide \`${TOPIC_PARAM_CURRENT_SUMMARY}\`. -- **2. Zero-Noise Execution:** +- **2. Strategic Narration:** + - **Intent:** At the start of each chapter, or before a significant sequence of tools, you SHOULD provide a concise, one-sentence statement of your intent or strategy. - **No Text Headers:** You are FORBIDDEN from printing "Topic: " or any similar text-based headers in your response. The tool handles all UI narration. - - **Silent Mode:** No conversational filler, no "I will now...", and no summaries between tools. - - Only the tool execution is permitted to define the state. Everything in between must be silent. + - **Minimal Noise:** Avoid conversational filler and apologies. While executing tools within a chapter, maintain a high signal-to-noise ratio; brief explanations are encouraged if they provide essential context or clarify your strategy. - **3. Internal Reasoning:** - You MUST reason about your plan, track tool calls, and strategize internally before executing tools. - - This reasoning process must remain internal. You are strictly FORBIDDEN from including your reasoning, "thoughts," or explanations in your text response. - - Between tool calls, your text output MUST remain completely empty (Zero-Noise). + - While your deep reasoning should remain internal, you are encouraged to share your high-level strategy and key findings in your text responses to maintain alignment and improve task performance. - **4. Completion:** - Only when the entire task is finalized do you provide a **Final Summary**. diff --git a/packages/core/src/tools/definitions/base-declarations.ts b/packages/core/src/tools/definitions/base-declarations.ts index c0a01ff6e0..4ee06c1199 100644 --- a/packages/core/src/tools/definitions/base-declarations.ts +++ b/packages/core/src/tools/definitions/base-declarations.ts @@ -126,3 +126,5 @@ export const PLAN_MODE_PARAM_REASON = 'reason'; // -- create_new_topic -- export const CREATE_NEW_TOPIC_TOOL_NAME = 'create_new_topic'; export const TOPIC_PARAM_TITLE = 'title'; +export const TOPIC_PARAM_PREVIOUS_SUMMARY = 'previous_topic_summary'; +export const TOPIC_PARAM_CURRENT_SUMMARY = 'current_topic_summary'; diff --git a/packages/core/src/tools/definitions/coreTools.ts b/packages/core/src/tools/definitions/coreTools.ts index c254bf3881..ccc2080ed2 100644 --- a/packages/core/src/tools/definitions/coreTools.ts +++ b/packages/core/src/tools/definitions/coreTools.ts @@ -92,6 +92,8 @@ import { SKILL_PARAM_NAME, CREATE_NEW_TOPIC_TOOL_NAME as CREATE_NEW_TOPIC_TOOL_NAME_BASE, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, } from './base-declarations.js'; export { @@ -167,6 +169,8 @@ export { EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, }; // Re-export sets for compatibility diff --git a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts index 56f4d57a68..c1bd7c7727 100644 --- a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts +++ b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts @@ -25,6 +25,8 @@ import { SKILL_PARAM_NAME, CREATE_NEW_TOPIC_TOOL_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, } from './base-declarations.js'; /** @@ -181,7 +183,7 @@ export function getCreateNewTopicDeclaration(): FunctionDeclaration { return { name: CREATE_NEW_TOPIC_TOOL_NAME, description: - 'Organizes work into a new "Chapter" or "Topic". Call this when transitioning between major phases (e.g., from Research to Implementation).', + 'Organizes work into a granular, single-focus "Chapter" or "Topic". You MUST NOT combine distinct phases (e.g., do NOT use "Researching and Implementing"). Use specific, singular titles like "Researching", "Planning", or "Implementing [Specific Idea]". If your work deviates from the current topic\'s stated goal, you MUST create a new topic to reflect the shift (e.g., "Implementing [New Idea]"). When shifting topics, you MUST provide a detailed summary (5-10 sentences) of the work completed in the previous topic and a clear statement of what the new topic aims to achieve.', parametersJsonSchema: { type: 'object', properties: { @@ -189,8 +191,18 @@ export function getCreateNewTopicDeclaration(): FunctionDeclaration { type: 'string', description: 'The title of the new topic or chapter.', }, + [TOPIC_PARAM_PREVIOUS_SUMMARY]: { + type: 'string', + description: + '(OPTIONAL) A detailed summary (5-10 sentences) of the work completed in the previous topic. This is required when transitioning between topics to maintain continuity.', + }, + [TOPIC_PARAM_CURRENT_SUMMARY]: { + type: 'string', + description: + 'A detailed explanation (5-10 sentences) of what the new topic intends to achieve. This is mandatory for all new topics to provide clear strategic intent.', + }, }, - required: [TOPIC_PARAM_TITLE], + required: [TOPIC_PARAM_TITLE, TOPIC_PARAM_CURRENT_SUMMARY], }, }; } diff --git a/packages/core/src/tools/tool-names.ts b/packages/core/src/tools/tool-names.ts index dd5061019a..540cd10644 100644 --- a/packages/core/src/tools/tool-names.ts +++ b/packages/core/src/tools/tool-names.ts @@ -77,6 +77,8 @@ import { EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, } from './definitions/coreTools.js'; export { @@ -152,6 +154,8 @@ export { EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, }; export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anything used the old exported name directly diff --git a/packages/core/src/tools/topicTool.test.ts b/packages/core/src/tools/topicTool.test.ts index 0bdf944183..1c64e24343 100644 --- a/packages/core/src/tools/topicTool.test.ts +++ b/packages/core/src/tools/topicTool.test.ts @@ -11,6 +11,8 @@ import type { PolicyEngine } from '../policy/policy-engine.js'; import { CREATE_NEW_TOPIC_TOOL_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, } from './definitions/base-declarations.js'; import type { Config } from '../config/config.js'; @@ -79,16 +81,37 @@ describe('CreateNewTopicTool', () => { expect(tool.displayName).toBe('Create New Topic'); }); - it('should update TopicState on execute', async () => { - const invocation = tool.build({ [TOPIC_PARAM_TITLE]: 'New Chapter' }); + it('should update TopicState and include current goal on execute', async () => { + const invocation = tool.build({ + [TOPIC_PARAM_TITLE]: 'New Chapter', + [TOPIC_PARAM_CURRENT_SUMMARY]: 'The goal is to implement X', + }); const result = await invocation.execute(new AbortController().signal); - expect(result.llmContent).toBe('Current topic: "New Chapter"'); + expect(result.llmContent).toContain('Current topic: "New Chapter"'); + expect(result.llmContent).toContain( + 'Topic goal: The goal is to implement X', + ); expect(mockConfig.topicState.getTopic()).toBe('New Chapter'); }); + it('should include previous summary if provided', async () => { + const invocation = tool.build({ + [TOPIC_PARAM_TITLE]: 'New Chapter', + [TOPIC_PARAM_CURRENT_SUMMARY]: 'The goal is to implement X', + [TOPIC_PARAM_PREVIOUS_SUMMARY]: 'Finished Y', + }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Previous topic summary: Finished Y'); + expect(result.llmContent).toContain('Current topic: "New Chapter"'); + }); + it('should return error if title is invalid after sanitization', async () => { - const invocation = tool.build({ [TOPIC_PARAM_TITLE]: ' \n ' }); + const invocation = tool.build({ + [TOPIC_PARAM_TITLE]: ' \n ', + [TOPIC_PARAM_CURRENT_SUMMARY]: 'Goal', + }); const result = await invocation.execute(new AbortController().signal); expect(result.error).toBeDefined(); diff --git a/packages/core/src/tools/topicTool.ts b/packages/core/src/tools/topicTool.ts index c204dffee4..9aa9a99763 100644 --- a/packages/core/src/tools/topicTool.ts +++ b/packages/core/src/tools/topicTool.ts @@ -7,6 +7,8 @@ import { CREATE_NEW_TOPIC_TOOL_NAME, TOPIC_PARAM_TITLE, + TOPIC_PARAM_PREVIOUS_SUMMARY, + TOPIC_PARAM_CURRENT_SUMMARY, } from './definitions/coreTools.js'; import { BaseDeclarativeTool, @@ -60,6 +62,8 @@ export class TopicState { interface CreateNewTopicParams { [TOPIC_PARAM_TITLE]: string; + [TOPIC_PARAM_PREVIOUS_SUMMARY]?: string; + [TOPIC_PARAM_CURRENT_SUMMARY]: string; } class CreateNewTopicInvocation extends BaseToolInvocation< @@ -76,11 +80,14 @@ class CreateNewTopicInvocation extends BaseToolInvocation< } getDescription(): string { - return `Create new topic: "${this.params[TOPIC_PARAM_TITLE]}"`; + const title = this.params[TOPIC_PARAM_TITLE]; + return `Create new topic: "${title}"`; } async execute(): Promise { const title = this.params[TOPIC_PARAM_TITLE]; + const previousSummary = this.params[TOPIC_PARAM_PREVIOUS_SUMMARY]; + const currentSummary = this.params[TOPIC_PARAM_CURRENT_SUMMARY]; const success = this.config.topicState.setTopic(title); @@ -98,9 +105,20 @@ class CreateNewTopicInvocation extends BaseToolInvocation< const setTopic = this.config.topicState.getTopic()!; debugLogger.log(`[TopicTool] Changing topic to: "${setTopic}"`); + let llmContent = `Current topic: "${setTopic}"\nTopic goal: ${currentSummary}`; + let returnDisplay = `Current topic: **${setTopic}**\n\n**Topic Goal:**\n${currentSummary}`; + + if (previousSummary) { + llmContent = + `Previous topic summary: ${previousSummary}\n\n` + llmContent; + returnDisplay = + `**Previous Topic Summary:**\n${previousSummary}\n\n---\n\n` + + returnDisplay; + } + return { - llmContent: `Current topic: "${setTopic}"`, - returnDisplay: `Current topic: **${setTopic}**`, + llmContent, + returnDisplay, }; } }