Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions lib/messages/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { loadPrompt } from "../prompts"
import {
extractParameterKey,
buildToolIdList,
createSyntheticAssistantMessageWithToolPart,
createSyntheticAssistantMessage,
isIgnoredUserMessage,
} from "./utils"
import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns"
Expand Down Expand Up @@ -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))
}
24 changes: 6 additions & 18 deletions lib/messages/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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: [
Expand All @@ -75,6 +62,7 @@ export const createSyntheticAssistantMessageWithToolPart = (
metadata: {},
time: { start: now, end: now },
},
...(toolPartMetadata && { metadata: toolPartMetadata }),
},
],
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 `<prunable-tools>` 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 \`<prunable-tools>\` 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
Expand All @@ -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 `<prunable-tools>` list
- \`ids\`: Array where the first element is the reason, followed by numeric IDs from the \`<prunable-tools>\` list

Reasons: `noise` | `completion`
Reasons: \`noise\` | \`completion\`

## Example

Expand All @@ -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"]]
</example_completion>

</example_completion>`
Original file line number Diff line number Diff line change
@@ -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 `<prunable-tools>` 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 \`<prunable-tools>\` 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.
Expand All @@ -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 `<prunable-tools>` 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 \`<prunable-tools>\` 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.

Expand All @@ -44,4 +44,4 @@ I'll preserve the key details before extracting.
<example_keep>
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.
</example_keep>
</example_keep>`
32 changes: 28 additions & 4 deletions lib/prompts/index.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = {
"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, string>): 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)
Expand Down
10 changes: 5 additions & 5 deletions lib/prompts/nudge/nudge-both.txt → lib/prompts/nudge/both.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<instruction name=context_management_required>
export const NUDGE_BOTH = `<instruction name=context_management_required>
**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.
</instruction>
</instruction>`
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<instruction name=context_management_required>
export const NUDGE_DISCARD = `<instruction name=context_management_required>
**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.
</instruction>
</instruction>`
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<instruction name=context_management_required>
export const NUDGE_EXTRACT = `<instruction name=context_management_required>
**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.
</instruction>
</instruction>`
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
<system-reminder>
export const SYSTEM_PROMPT_BOTH = `<system-reminder>
<instruction name=context_management_protocol policy_level=critical>

ENVIRONMENT
You are operating in a context-constrained environment and thus must proactively manage your context window using the `discard` and `extract` tools. A <prunable-tools> 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 <prunable-tools> 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.
Expand All @@ -42,7 +42,7 @@ There may be tools in session context that do not appear in the <prunable-tools>
</instruction>

<instruction name=injected_context_handling policy_level=critical>
After each assistant turn, the environment may inject a user message containing a <prunable-tools> 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 <prunable-tools> 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.
Expand All @@ -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.
</instruction>
</system-reminder>
</system-reminder>`
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<system-reminder>
export const SYSTEM_PROMPT_DISCARD = `<system-reminder>
<instruction name=context_management_protocol policy_level=critical>

ENVIRONMENT
You are operating in a context-constrained environment and thus must proactively manage your context window using the `discard` tool. A <prunable-tools> 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 <prunable-tools> 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.
Expand Down Expand Up @@ -33,7 +33,7 @@ There may be tools in session context that do not appear in the <prunable-tools>
</instruction>

<instruction name=injected_context_handling policy_level=critical>
After each assistant turn, the environment may inject a user message containing a <prunable-tools> 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 <prunable-tools> 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.
Expand All @@ -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.
</instruction>
</system-reminder>
</system-reminder>`
Loading