Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
71 changes: 71 additions & 0 deletions pr-body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!--
Thank you for contributing to Roo Code!
-->

### Related GitHub Issue

Closes: #8293

### Roo Code Task Context (Optional)

_No Roo Code task context for this PR_

### Description

Condense currently preserves the initial user message visually but excludes it from the LLM's summarization input, causing summaries to omit the original ask. This leads to resume re-answering the initial ask instead of continuing the current work. This PR ensures the initial ask is included in the condense summarization input and hardens the prompt to always capture it.

### Changes Made

- Include the original first user message in LLM summarization input by changing the slice from messages.slice(1, -N_MESSAGES_TO_KEEP) to messages.slice(0, -N_MESSAGES_TO_KEEP) in [src/core/condense/index.ts](src/core/condense/index.ts).
- Harden SUMMARY_PROMPT to explicitly require the initial user ask be included, in [src/core/condense/index.ts](src/core/condense/index.ts).
- Add a unit test to assert the initial ask is present in the summarization input when no prior summary exists: [src/core/condense/**tests**/index.spec.ts](src/core/condense/__tests__/index.spec.ts).

### Test Procedure

- Run focused tests:

```bash
cd src
npx vitest run core/condense/__tests__/index.spec.ts core/condense/__tests__/condense.spec.ts
```

- Run full test suite:

```bash
cd src
npx vitest run
```

All tests pass locally.

### Verification of Acceptance Criteria

- [x] Summaries include the initial user ask so task resume maintains correct context.
- [x] Applies to manual condense flow (Task -> summarizeConversation) and automatic condense flow (sliding window -> summarizeConversation when thresholds trigger).
- [x] Guard ensures new context tokens do not exceed previous, preserving safety.

### Pre-Submission Checklist

- [x] Issue Linked
- [x] Scope focused on linked issue
- [x] Self-review completed
- [x] Tests added/updated and passing
- [x] Documentation impact considered
- [x] No breaking changes

### Screenshots / Videos

_No UI changes in this PR_

### Documentation Updates

- [x] No documentation updates are required.

### Additional Notes

- Minimal, targeted change; fallback behavior and token safety rails unchanged.
- Potential slight increase in summarization input length is bounded and protected by existing safeguards.

### Get in Touch

@roomote
26 changes: 26 additions & 0 deletions src/core/condense/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,32 @@ describe("summarizeConversation", () => {
const mockCallArgs = (maybeRemoveImageBlocks as Mock).mock.calls[0][0] as any[]
expect(mockCallArgs[mockCallArgs.length - 1]).toEqual(expectedFinalMessage)
})
it("should include the original first user message in summarization input", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a complementary test where an earlier summary exists to assert the original first ask remains present in the summarization input across both manual and automatic condense flows.

const messages: ApiMessage[] = [
{ role: "user", content: "Initial ask", ts: 1 },
{ role: "assistant", content: "Ack", ts: 2 },
{ role: "user", content: "Follow-up", ts: 3 },
{ role: "assistant", content: "Response", ts: 4 },
{ role: "user", content: "More", ts: 5 },
{ role: "assistant", content: "Later", ts: 6 },
{ role: "user", content: "Newest", ts: 7 },
]

await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId, DEFAULT_PREV_CONTEXT_TOKENS)

const mockCallArgs = (maybeRemoveImageBlocks as Mock).mock.calls[0][0] as any[]

// Expect the original first user message to be present in the messages sent to the summarizer
const hasInitialAsk = mockCallArgs.some(
(m) =>
m.role === "user" &&
(typeof m.content === "string"
? m.content === "Initial ask"
: Array.isArray(m.content) &&
m.content.some((b: any) => b.type === "text" && b.text === "Initial ask")),
)
expect(hasInitialAsk).toBe(true)
})

it("should calculate newContextTokens correctly with systemPrompt", async () => {
const messages: ApiMessage[] = [
Expand Down
4 changes: 2 additions & 2 deletions src/core/condense/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This summary should be thorough in capturing technical details, code patterns, a

Your summary should be structured as follows:
Context: The context to continue the conversation with. If applicable based on the current task, this should include:
1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow.
1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user, and explicitly include the conversation's initial user ask verbatim or a precise summary so the result can stand alone. This should be written to allow someone to be able to follow the general overarching conversation flow.
2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation.
3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work.
4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes.
Expand Down Expand Up @@ -104,7 +104,7 @@ export async function summarizeConversation(
// Always preserve the first message (which may contain slash command content)
const firstMessage = messages[0]
// Get messages to summarize, excluding the first message and last N messages
const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(1, -N_MESSAGES_TO_KEEP))
const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP))

if (messagesToSummarize.length <= 1) {
const error =
Expand Down
Loading