diff --git a/lib/hooks.ts b/lib/hooks.ts index e1e1bb4..5a16f54 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -120,7 +120,7 @@ export function createChatMessageTransformHandler( prune(state, logger, config, output.messages) insertPruneToolContext(state, config, logger, output.messages) - insertMessageIdContext(state, output.messages) + insertMessageIdContext(state, config, output.messages) applyPendingManualTriggerPrompt(state, output.messages, logger) diff --git a/lib/message-ids.ts b/lib/message-ids.ts index 75b981c..a6637b8 100644 --- a/lib/message-ids.ts +++ b/lib/message-ids.ts @@ -84,7 +84,7 @@ export function parseBoundaryId(id: string): ParsedBoundaryId | null { } export function formatMessageIdTag(ref: string): string { - return `<${MESSAGE_ID_TAG_NAME}>${ref}` + return `\n<${MESSAGE_ID_TAG_NAME}>${ref}` } export function assignMessageRefs(state: SessionState, messages: WithParts[]): number { diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index 74f412a..71026a7 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -288,7 +288,7 @@ export const insertPruneToolContext = ( return } - const combinedContent = contentParts.join("\n") + const combinedContent = `\n${contentParts.join("\n")}` if (!lastUserMessage) { return @@ -324,7 +324,15 @@ export const insertPruneToolContext = ( } } -export const insertMessageIdContext = (state: SessionState, messages: WithParts[]): void => { +export const insertMessageIdContext = ( + state: SessionState, + config: PluginConfig, + messages: WithParts[], +): void => { + if (config.tools.compress.permission === "deny") { + return + } + const lastUserMessage = getLastUserMessage(messages) const toolModelId = lastUserMessage ? ((lastUserMessage.info as UserMessage).model.modelID ?? "") diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 0e1d69c..1b9d31c 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -115,8 +115,7 @@ export const appendMessageIdTagToToolOutput = (part: ToolPart, tag: string): boo return true } - const separator = part.state.output.length > 0 && !part.state.output.endsWith("\n") ? "\n" : "" - part.state.output = `${part.state.output}${separator}${tag}` + part.state.output = `${part.state.output}${tag}` return true } diff --git a/lib/prompts/system.md b/lib/prompts/system.md index 6ff9b71..0e020ab 100644 --- a/lib/prompts/system.md +++ b/lib/prompts/system.md @@ -14,6 +14,8 @@ AVAILABLE TOOLS FOR CONTEXT MANAGEMENT THE COMPRESS TOOL `compress` is a sledgehammer and should be used accordingly. It's purpose is to reduce whole part of the conversation to its essence and technical details in order to leave room for newer context. Your summary MUST be technical and specific enough to preserve FULL understanding of WHAT TRANSPIRED, such that NO AMBIGUITY remains about what was done, found, or decided. Your compress summary must be thorough and precise. `compress` will replace everything in the range you match, user and assistant messages, tool inputs and outputs. It is preferred to not compress preemptively, but rather wait for natural breakpoints in the conversation. Those breakpoints are to be infered from user messages. You WILL NOT compress based on thinking that you are done with the task, wait for conversation queues that the user has moved on from current phase. Use injected boundary IDs (`startId`/`endId`) to select ranges. +Injected boundary IDs are surfaced as XML tags in conversation context, e.g. `m0001` for message IDs and `b3` for compressed blocks. These IDs are internal boundary markers for `compress` only. Do not reference, explain, or surface these IDs in normal user-facing responses unless you are actively constructing a `compress` tool call. + This tool will typically be used at the end of a phase of work, when conversation starts to accumulate noise that would better served summarized, or when you've done significant exploration and can FULLY synthesize your findings and understanding into a technical summary. Use only injected `mNNNN`/`bN` IDs that are visible in the current context. If compressed blocks are included in your range, preserve their content through required `(bN)` placeholders in your summary. Be VERY CAREFUL AND CONSERVATIVE when using `compress`.