diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index 8b67186..c421826 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -6,7 +6,7 @@ import { loadPrompt } from "../prompts" import { extractParameterKey, buildToolIdList, - createSyntheticAssistantMessageWithToolPart, + createSyntheticAssistantMessage, isIgnoredUserMessage, } from "./utils" import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns" @@ -149,7 +149,5 @@ export const insertPruneToolContext = ( const userInfo = lastUserMessage.info as UserMessage const variant = state.variant ?? userInfo.variant - messages.push( - createSyntheticAssistantMessageWithToolPart(lastUserMessage, prunableToolsContent, variant), - ) + messages.push(createSyntheticAssistantMessage(lastUserMessage, prunableToolsContent, variant)) } diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index fafdccd..219027c 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -12,7 +12,7 @@ const isGeminiModel = (modelID: string): boolean => { return lowerModelID.includes("gemini") } -export const createSyntheticAssistantMessageWithToolPart = ( +export const createSyntheticAssistantMessage = ( baseMessage: WithParts, content: string, variant?: string, @@ -39,24 +39,11 @@ export const createSyntheticAssistantMessageWithToolPart = ( ...(variant !== undefined && { variant }), } - // For Gemini models, inject as text to avoid thought signature requirements - // Gemini 3+ has strict validation requiring thoughtSignature on functionCall parts - if (isGeminiModel(userInfo.model.modelID)) { - return { - info: baseInfo, - parts: [ - { - id: SYNTHETIC_PART_ID, - sessionID: userInfo.sessionID, - messageID: SYNTHETIC_MESSAGE_ID, - type: "text", - text: content, - }, - ], - } - } + // For Gemini models, add thoughtSignature bypass to avoid validation errors + const toolPartMetadata = isGeminiModel(userInfo.model.modelID) + ? { google: { thoughtSignature: "skip_thought_signature_validator" } } + : undefined - // For other models, use tool part for cleaner context return { info: baseInfo, parts: [ @@ -75,6 +62,7 @@ export const createSyntheticAssistantMessageWithToolPart = ( metadata: {}, time: { start: now, end: now }, }, + ...(toolPartMetadata && { metadata: toolPartMetadata }), }, ], } diff --git a/lib/prompts/discard-tool-spec.txt b/lib/prompts/discard-tool-spec.ts similarity index 58% rename from lib/prompts/discard-tool-spec.txt rename to lib/prompts/discard-tool-spec.ts index 68669ed..dcd2135 100644 --- a/lib/prompts/discard-tool-spec.txt +++ b/lib/prompts/discard-tool-spec.ts @@ -1,18 +1,18 @@ -Discards tool outputs from context to manage conversation size and reduce noise. +export const DISCARD_TOOL_SPEC = `Discards tool outputs from context to manage conversation size and reduce noise. ## IMPORTANT: The Prunable List -A `` list is provided to you showing available tool outputs you can discard when there are tools available for pruning. Each line has the format `ID: tool, parameter` (e.g., `20: read, /path/to/file.ts`). You MUST only use numeric IDs that appear in this list to select which tools to discard. +A \`\` list is provided to you showing available tool outputs you can discard when there are tools available for pruning. Each line has the format \`ID: tool, parameter\` (e.g., \`20: read, /path/to/file.ts\`). You MUST only use numeric IDs that appear in this list to select which tools to discard. ## When to Use This Tool -Use `discard` for removing tool content that is no longer needed +Use \`discard\` for removing tool content that is no longer needed - **Noise:** Irrelevant, unhelpful, or superseded outputs that provide no value. - **Task Completion:** Work is complete and there's no valuable information worth preserving. ## When NOT to Use This Tool -- **If the output contains useful information:** Use `extract` instead to preserve key findings. +- **If the output contains useful information:** Use \`extract\` instead to preserve key findings. - **If you'll need the output later:** Don't discard files you plan to edit or context you'll need for implementation. ## Best Practices @@ -21,9 +21,9 @@ Use `discard` for removing tool content that is no longer needed ## Format -- `ids`: Array where the first element is the reason, followed by numeric IDs from the `` list +- \`ids\`: Array where the first element is the reason, followed by numeric IDs from the \`\` list -Reasons: `noise` | `completion` +Reasons: \`noise\` | \`completion\` ## Example @@ -37,5 +37,4 @@ This file isn't relevant to the auth system. I'll remove it to clear the context Assistant: [Runs tests, they pass] The tests passed and I don't need to preserve any details. I'll clean up now. [Uses discard with ids: ["completion", "20", "21"]] - - +` diff --git a/lib/prompts/extract-tool-spec.txt b/lib/prompts/extract-tool-spec.ts similarity index 69% rename from lib/prompts/extract-tool-spec.txt rename to lib/prompts/extract-tool-spec.ts index 7986224..9324dc0 100644 --- a/lib/prompts/extract-tool-spec.txt +++ b/lib/prompts/extract-tool-spec.ts @@ -1,11 +1,11 @@ -Extracts key findings from tool outputs into distilled knowledge, then removes the raw outputs from context. +export const EXTRACT_TOOL_SPEC = `Extracts key findings from tool outputs into distilled knowledge, then removes the raw outputs from context. ## IMPORTANT: The Prunable List -A `` list is provided to you showing available tool outputs you can extract from when there are tools available for pruning. Each line has the format `ID: tool, parameter` (e.g., `20: read, /path/to/file.ts`). You MUST only use numeric IDs that appear in this list to select which tools to extract. +A \`\` list is provided to you showing available tool outputs you can extract from when there are tools available for pruning. Each line has the format \`ID: tool, parameter\` (e.g., \`20: read, /path/to/file.ts\`). You MUST only use numeric IDs that appear in this list to select which tools to extract. ## When to Use This Tool -Use `extract` when you have gathered useful information that you want to **preserve in distilled form** before removing the raw outputs: +Use \`extract\` when you have gathered useful information that you want to **preserve in distilled form** before removing the raw outputs: - **Task Completion:** You completed a unit of work and want to preserve key findings. - **Knowledge Preservation:** You have context that contains valuable information, but also a lot of unnecessary detail - you only need to preserve some specifics. @@ -22,8 +22,8 @@ Use `extract` when you have gathered useful information that you want to **prese ## Format -- `ids`: Array of numeric IDs as strings from the `` list -- `distillation`: Array of strings, one per ID (positional: distillation[0] is for ids[0], etc.) +- \`ids\`: Array of numeric IDs as strings from the \`\` list +- \`distillation\`: Array of strings, one per ID (positional: distillation[0] is for ids[0], etc.) Each distillation string should capture the essential information you need to preserve - function signatures, logic, constraints, values, etc. Be as detailed as needed for your task. @@ -44,4 +44,4 @@ I'll preserve the key details before extracting. Assistant: [Reads 'auth.ts' to understand the login flow] I've understood the auth flow. I'll need to modify this file to add the new validation, so I'm keeping this read in context rather than extracting. - +` diff --git a/lib/prompts/index.ts b/lib/prompts/index.ts index dbec5dd..bdfbc86 100644 --- a/lib/prompts/index.ts +++ b/lib/prompts/index.ts @@ -1,9 +1,33 @@ -import { readFileSync } from "fs" -import { join } from "path" +// Tool specs +import { DISCARD_TOOL_SPEC } from "./discard-tool-spec" +import { EXTRACT_TOOL_SPEC } from "./extract-tool-spec" + +// System prompts +import { SYSTEM_PROMPT_BOTH } from "./system/both" +import { SYSTEM_PROMPT_DISCARD } from "./system/discard" +import { SYSTEM_PROMPT_EXTRACT } from "./system/extract" + +// Nudge prompts +import { NUDGE_BOTH } from "./nudge/both" +import { NUDGE_DISCARD } from "./nudge/discard" +import { NUDGE_EXTRACT } from "./nudge/extract" + +const PROMPTS: Record = { + "discard-tool-spec": DISCARD_TOOL_SPEC, + "extract-tool-spec": EXTRACT_TOOL_SPEC, + "system/system-prompt-both": SYSTEM_PROMPT_BOTH, + "system/system-prompt-discard": SYSTEM_PROMPT_DISCARD, + "system/system-prompt-extract": SYSTEM_PROMPT_EXTRACT, + "nudge/nudge-both": NUDGE_BOTH, + "nudge/nudge-discard": NUDGE_DISCARD, + "nudge/nudge-extract": NUDGE_EXTRACT, +} export function loadPrompt(name: string, vars?: Record): string { - const filePath = join(__dirname, `${name}.txt`) - let content = readFileSync(filePath, "utf8").trim() + let content = PROMPTS[name] + if (!content) { + throw new Error(`Prompt not found: ${name}`) + } if (vars) { for (const [key, value] of Object.entries(vars)) { content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value) diff --git a/lib/prompts/nudge/nudge-both.txt b/lib/prompts/nudge/both.ts similarity index 54% rename from lib/prompts/nudge/nudge-both.txt rename to lib/prompts/nudge/both.ts index f9fa492..50fc0a9 100644 --- a/lib/prompts/nudge/nudge-both.txt +++ b/lib/prompts/nudge/both.ts @@ -1,10 +1,10 @@ - +export const NUDGE_BOTH = ` **CRITICAL CONTEXT WARNING:** Your context window is filling with tool outputs. Strict adherence to context hygiene is required. **Immediate Actions Required:** -1. **Task Completion:** If a sub-task is complete, decide: use `discard` if no valuable context to preserve (default), or use `extract` if insights are worth keeping. -2. **Noise Removal:** If you read files or ran commands that yielded no value, use `discard` to remove them. -3. **Knowledge Preservation:** If you are holding valuable raw data you'll need to reference later, use `extract` to distill the insights and remove the raw entry. +1. **Task Completion:** If a sub-task is complete, decide: use \`discard\` if no valuable context to preserve (default), or use \`extract\` if insights are worth keeping. +2. **Noise Removal:** If you read files or ran commands that yielded no value, use \`discard\` to remove them. +3. **Knowledge Preservation:** If you are holding valuable raw data you'll need to reference later, use \`extract\` to distill the insights and remove the raw entry. **Protocol:** You should prioritize this cleanup, but do not interrupt a critical atomic operation if one is in progress. Once the immediate step is done, you must perform context management. - +` diff --git a/lib/prompts/nudge/nudge-discard.txt b/lib/prompts/nudge/discard.ts similarity index 62% rename from lib/prompts/nudge/nudge-discard.txt rename to lib/prompts/nudge/discard.ts index 1ccecf9..18e9250 100644 --- a/lib/prompts/nudge/nudge-discard.txt +++ b/lib/prompts/nudge/discard.ts @@ -1,9 +1,9 @@ - +export const NUDGE_DISCARD = ` **CRITICAL CONTEXT WARNING:** Your context window is filling with tool outputs. Strict adherence to context hygiene is required. **Immediate Actions Required:** -1. **Task Completion:** If a sub-task is complete, use the `discard` tool to remove the tools used. -2. **Noise Removal:** If you read files or ran commands that yielded no value, use the `discard` tool to remove them. +1. **Task Completion:** If a sub-task is complete, use the \`discard\` tool to remove the tools used. +2. **Noise Removal:** If you read files or ran commands that yielded no value, use the \`discard\` tool to remove them. **Protocol:** You should prioritize this cleanup, but do not interrupt a critical atomic operation if one is in progress. Once the immediate step is done, you must discard unneeded tool outputs. - +` diff --git a/lib/prompts/nudge/nudge-extract.txt b/lib/prompts/nudge/extract.ts similarity index 71% rename from lib/prompts/nudge/nudge-extract.txt rename to lib/prompts/nudge/extract.ts index 5bdb370..243f585 100644 --- a/lib/prompts/nudge/nudge-extract.txt +++ b/lib/prompts/nudge/extract.ts @@ -1,9 +1,9 @@ - +export const NUDGE_EXTRACT = ` **CRITICAL CONTEXT WARNING:** Your context window is filling with tool outputs. Strict adherence to context hygiene is required. **Immediate Actions Required:** 1. **Task Completion:** If you have completed work, extract key findings from the tools used. Scale distillation depth to the value of the content. -2. **Knowledge Preservation:** If you are holding valuable raw data you'll need to reference later, use the `extract` tool with high-fidelity distillation to preserve the insights and remove the raw entry. +2. **Knowledge Preservation:** If you are holding valuable raw data you'll need to reference later, use the \`extract\` tool with high-fidelity distillation to preserve the insights and remove the raw entry. **Protocol:** You should prioritize this cleanup, but do not interrupt a critical atomic operation if one is in progress. Once the immediate step is done, you must extract valuable findings from tool outputs. - +` diff --git a/lib/prompts/system/system-prompt-both.txt b/lib/prompts/system/both.ts similarity index 70% rename from lib/prompts/system/system-prompt-both.txt rename to lib/prompts/system/both.ts index f1e88aa..e101e38 100644 --- a/lib/prompts/system/system-prompt-both.txt +++ b/lib/prompts/system/both.ts @@ -1,24 +1,24 @@ - +export const SYSTEM_PROMPT_BOTH = ` ENVIRONMENT -You are operating in a context-constrained environment and thus must proactively manage your context window using the `discard` and `extract` tools. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to prune. +You are operating in a context-constrained environment and thus must proactively manage your context window using the \`discard\` and \`extract\` tools. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to prune. TWO TOOLS FOR CONTEXT MANAGEMENT -- `discard`: Remove tool outputs that are no longer needed (completed tasks, noise, outdated info). No preservation of content. -- `extract`: Extract key findings into distilled knowledge before removing raw outputs. Use when you need to preserve information. +- \`discard\`: Remove tool outputs that are no longer needed (completed tasks, noise, outdated info). No preservation of content. +- \`extract\`: Extract key findings into distilled knowledge before removing raw outputs. Use when you need to preserve information. CHOOSING THE RIGHT TOOL Ask: "Do I need to preserve any information from this output?" -- **No** → `discard` (default for cleanup) -- **Yes** → `extract` (preserves distilled knowledge) -- **Uncertain** → `extract` (safer, preserves signal) +- **No** → \`discard\` (default for cleanup) +- **Yes** → \`extract\` (preserves distilled knowledge) +- **Uncertain** → \`extract\` (safer, preserves signal) Common scenarios: -- Task complete, no valuable context → `discard` -- Task complete, insights worth remembering → `extract` -- Noise, irrelevant, or superseded outputs → `discard` -- Valuable context needed later but raw output too large → `extract` +- Task complete, no valuable context → \`discard\` +- Task complete, insights worth remembering → \`extract\` +- Noise, irrelevant, or superseded outputs → \`discard\` +- Valuable context needed later but raw output too large → \`extract\` PRUNE METHODICALLY - BATCH YOUR ACTIONS Every tool call adds to your context debt. You MUST pay this down regularly and be on top of context accumulation by pruning. Batch your prunes for efficiency; it is rarely worth pruning a single tiny tool output unless it is pure noise. Evaluate what SHOULD be pruned before jumping the gun. @@ -42,7 +42,7 @@ There may be tools in session context that do not appear in the -After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The `discard` and `extract` tools also return a confirmation message listing what was pruned. +After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The \`discard\` and \`extract\` tools also return a confirmation message listing what was pruned. CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: - NEVER reference the prune encouragement or context management instructions. Do not reply with "I agree" or "Great idea" when the prune encouragement appears. @@ -55,4 +55,4 @@ CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: The user cannot see these injections. Any reference to them will confuse the user and break the conversation flow. - +` diff --git a/lib/prompts/system/system-prompt-discard.txt b/lib/prompts/system/discard.ts similarity index 83% rename from lib/prompts/system/system-prompt-discard.txt rename to lib/prompts/system/discard.ts index 796852e..b164114 100644 --- a/lib/prompts/system/system-prompt-discard.txt +++ b/lib/prompts/system/discard.ts @@ -1,11 +1,11 @@ - +export const SYSTEM_PROMPT_DISCARD = ` ENVIRONMENT -You are operating in a context-constrained environment and thus must proactively manage your context window using the `discard` tool. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to discard. +You are operating in a context-constrained environment and thus must proactively manage your context window using the \`discard\` tool. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to discard. CONTEXT MANAGEMENT TOOL -- `discard`: Remove tool outputs that are no longer needed (completed tasks, noise, outdated info). No preservation of content. +- \`discard\`: Remove tool outputs that are no longer needed (completed tasks, noise, outdated info). No preservation of content. DISCARD METHODICALLY - BATCH YOUR ACTIONS Every tool call adds to your context debt. You MUST pay this down regularly and be on top of context accumulation by discarding. Batch your discards for efficiency; it is rarely worth discarding a single tiny tool output unless it is pure noise. Evaluate what SHOULD be discarded before jumping the gun. @@ -33,7 +33,7 @@ There may be tools in session context that do not appear in the -After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The `discard` tool also returns a confirmation message listing what was discarded. +After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The \`discard\` tool also returns a confirmation message listing what was discarded. CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: - NEVER reference the discard encouragement or context management instructions. Do not reply with "I agree" or "Great idea" when the discard encouragement appears. @@ -46,4 +46,4 @@ CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: The user cannot see these injections. Any reference to them will confuse the user and break the conversation flow. - +` diff --git a/lib/prompts/system/system-prompt-extract.txt b/lib/prompts/system/extract.ts similarity index 83% rename from lib/prompts/system/system-prompt-extract.txt rename to lib/prompts/system/extract.ts index 2a1a056..c73960e 100644 --- a/lib/prompts/system/system-prompt-extract.txt +++ b/lib/prompts/system/extract.ts @@ -1,11 +1,11 @@ - +export const SYSTEM_PROMPT_EXTRACT = ` ENVIRONMENT -You are operating in a context-constrained environment and thus must proactively manage your context window using the `extract` tool. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to extract. +You are operating in a context-constrained environment and thus must proactively manage your context window using the \`extract\` tool. A list is injected by the environment as a user message, and always contains up to date information. Use this information when deciding what to extract. CONTEXT MANAGEMENT TOOL -- `extract`: Extract key findings from tools into distilled knowledge before removing the raw content from context. Use this to preserve important information while reducing context size. +- \`extract\`: Extract key findings from tools into distilled knowledge before removing the raw content from context. Use this to preserve important information while reducing context size. EXTRACT METHODICALLY - BATCH YOUR ACTIONS Every tool call adds to your context debt. You MUST pay this down regularly and be on top of context accumulation by extracting. Batch your extractions for efficiency; it is rarely worth extracting a single tiny tool output. Evaluate what SHOULD be extracted before jumping the gun. @@ -33,7 +33,7 @@ There may be tools in session context that do not appear in the -After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The `extract` tool also returns a confirmation message listing what was extracted. +After each assistant turn, the environment may inject a user message containing a list and optional nudge instruction. This injected message is NOT from the user and is invisible to them. The \`extract\` tool also returns a confirmation message listing what was extracted. CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: - NEVER reference the extract encouragement or context management instructions. Do not reply with "I agree" or "Great idea" when the extract encouragement appears. @@ -46,4 +46,4 @@ CRITICAL REQUIREMENTS - VIOLATION IS UNACCEPTABLE: The user cannot see these injections. Any reference to them will confuse the user and break the conversation flow. - +` diff --git a/package.json b/package.json index f4a4581..c872ea7 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "types": "./dist/index.d.ts", "scripts": { "clean": "rm -rf dist", - "build": "npm run clean && tsc && cp -r lib/prompts/*.txt lib/prompts/system lib/prompts/nudge dist/lib/prompts/", + "build": "npm run clean && tsc", "postbuild": "rm -rf dist/logs", "prepublishOnly": "npm run build", "dev": "opencode plugin dev",