Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 339f5aa

Browse files
fix: convert orphaned tool_results to text blocks after condensing (#10927)
* fix: convert orphaned tool_results to text blocks after condensing When condensing occurs after assistant sends tool_uses but before user responds, the tool_use blocks get condensed away. User messages containing tool_results that reference condensed tool_use_ids become orphaned and get filtered out by getEffectiveApiHistory, causing user feedback to be lost. This fix enhances the existing check in addToApiConversationHistory to detect when the previous effective message is not an assistant and converts any tool_result blocks to text blocks, preventing them from being filtered as orphans. The conversion happens at the latest possible moment (message insertion) because: - Tool results are created before we know if condensing will occur - We need actual effective history state to make the decision - This is the last checkpoint before orphan filtering happens * Only include environment details in summary for automatic condensing For automatic condensing (during attemptApiRequest), environment details are included in the summary because the API request is already in progress and the next user message won't have fresh environment details injected. For manual condensing (via condenseContext button), environment details are NOT included because fresh details will be injected on the very next turn via getEnvironmentDetails() in recursivelyMakeClineRequests(). This uses the existing isAutomaticTrigger flag to differentiate behavior. --------- Co-authored-by: Hannes Rudolph <hrudolph@gmail.com>
1 parent 526488e commit 339f5aa

2 files changed

Lines changed: 34 additions & 5 deletions

File tree

src/core/condense/index.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,14 @@ export type SummarizeResponse = {
131131
* - Post-condense, the model sees only the summary (true fresh start)
132132
* - All messages are still stored but tagged with condenseParent
133133
* - <command> blocks from the original task are preserved across condensings
134-
* - <environment_details> is included to provide current workspace context
134+
*
135+
* Environment details handling:
136+
* - For AUTOMATIC condensing (isAutomaticTrigger=true): Environment details are included
137+
* in the summary because the API request is already in progress and the next user
138+
* message won't have fresh environment details injected.
139+
* - For MANUAL condensing (isAutomaticTrigger=false): Environment details are NOT included
140+
* because fresh environment details will be injected on the very next turn via
141+
* getEnvironmentDetails() in recursivelyMakeClineRequests().
135142
*
136143
* @param {ApiMessage[]} messages - The conversation messages
137144
* @param {ApiHandler} apiHandler - The API handler to use for summarization and token counting
@@ -140,7 +147,7 @@ export type SummarizeResponse = {
140147
* @param {boolean} isAutomaticTrigger - Whether the summarization is triggered automatically
141148
* @param {string} customCondensingPrompt - Optional custom prompt to use for condensing
142149
* @param {ApiHandlerCreateMessageMetadata} metadata - Optional metadata to pass to createMessage (tools, taskId, etc.)
143-
* @param {string} environmentDetails - Optional environment details string to include in the summary
150+
* @param {string} environmentDetails - Optional environment details string to include in the summary (only used when isAutomaticTrigger=true)
144151
* @returns {SummarizeResponse} - The result of the summarization operation (see above)
145152
*/
146153
export async function summarizeConversation(
@@ -294,8 +301,10 @@ ${commandBlocks}
294301
})
295302
}
296303

297-
// Add environment details as a separate text block if provided
298-
if (environmentDetails?.trim()) {
304+
// Add environment details as a separate text block if provided AND this is an automatic trigger.
305+
// For manual condensing, fresh environment details will be injected on the next turn.
306+
// For automatic condensing, the API request is already in progress so we need them in the summary.
307+
if (isAutomaticTrigger && environmentDetails?.trim()) {
299308
summaryContent.push({
300309
type: "text",
301310
text: environmentDetails,

src/core/task/Task.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,27 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
10031003
const effectiveHistoryForValidation = getEffectiveApiHistory(this.apiConversationHistory)
10041004
const lastEffective = effectiveHistoryForValidation[effectiveHistoryForValidation.length - 1]
10051005
const historyForValidation = lastEffective?.role === "assistant" ? effectiveHistoryForValidation : []
1006-
const validatedMessage = validateAndFixToolResultIds(message, historyForValidation)
1006+
1007+
// If the previous effective message is NOT an assistant, convert tool_result blocks to text blocks.
1008+
// This prevents orphaned tool_results from being filtered out by getEffectiveApiHistory.
1009+
// This can happen when condensing occurs after the assistant sends tool_uses but before
1010+
// the user responds - the tool_use blocks get condensed away, leaving orphaned tool_results.
1011+
let messageToAdd = message
1012+
if (lastEffective?.role !== "assistant" && Array.isArray(message.content)) {
1013+
messageToAdd = {
1014+
...message,
1015+
content: message.content.map((block) =>
1016+
block.type === "tool_result"
1017+
? {
1018+
type: "text" as const,
1019+
text: `Tool result:\n${typeof block.content === "string" ? block.content : JSON.stringify(block.content)}`,
1020+
}
1021+
: block,
1022+
),
1023+
}
1024+
}
1025+
1026+
const validatedMessage = validateAndFixToolResultIds(messageToAdd, historyForValidation)
10071027
const messageWithTs = { ...validatedMessage, ts: Date.now() }
10081028
this.apiConversationHistory.push(messageWithTs)
10091029
}

0 commit comments

Comments
 (0)