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

Commit ef960eb

Browse files
committed
refactor: move generateFoldedFileContext() inside summarizeConversation()
- Update summarizeConversation() to accept filesReadByRoo, cwd, rooIgnoreController instead of pre-generated sections - Move folded file context generation inside summarizeConversation() (lines 319-339) - Update ContextManagementOptions type and manageContext() to pass new parameters - Remove generateFoldedFileContext from Task.ts imports - folding now handled internally - Update all tests to use new parameter signature - Reduces Task.ts complexity by moving folding logic to summarization module
1 parent 3bde3cd commit ef960eb

5 files changed

Lines changed: 336 additions & 38 deletions

File tree

src/core/condense/__tests__/foldedFileContext.spec.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,20 @@ vi.mock("../../../services/tree-sitter", () => ({
1111
parseSourceCodeDefinitionsForFile: vi.fn(),
1212
}))
1313

14+
// Mock generateFoldedFileContext for summarizeConversation tests
15+
vi.mock("../foldedFileContext", async (importOriginal) => {
16+
const actual = await importOriginal<typeof import("../foldedFileContext")>()
17+
return {
18+
...actual,
19+
generateFoldedFileContext: vi.fn().mockImplementation(actual.generateFoldedFileContext),
20+
}
21+
})
22+
1423
import { generateFoldedFileContext } from "../foldedFileContext"
1524
import { parseSourceCodeDefinitionsForFile } from "../../../services/tree-sitter"
1625

26+
const mockedGenerateFoldedFileContext = vi.mocked(generateFoldedFileContext)
27+
1728
const mockedParseSourceCodeDefinitions = vi.mocked(parseSourceCodeDefinitionsForFile)
1829

1930
describe("foldedFileContext", () => {
@@ -262,8 +273,8 @@ describe("foldedFileContext", () => {
262273
{ role: "user", content: "Seventh message" },
263274
]
264275

265-
// Folded file context sections - each file in its own <system-reminder> block
266-
const foldedFileContextSections = [
276+
// Mock generateFoldedFileContext to return the expected folded sections
277+
const mockFoldedSections = [
267278
`<system-reminder>
268279
## File Context: src/user.ts
269280
1--5 | export interface User
@@ -276,6 +287,17 @@ describe("foldedFileContext", () => {
276287
</system-reminder>`,
277288
]
278289

290+
mockedGenerateFoldedFileContext.mockResolvedValue({
291+
content: mockFoldedSections.join("\n"),
292+
sections: mockFoldedSections,
293+
filesProcessed: 2,
294+
filesSkipped: 0,
295+
characterCount: mockFoldedSections.join("\n").length,
296+
})
297+
298+
const filesReadByRoo = ["src/user.ts", "src/api.ts"]
299+
const cwd = "/test/project"
300+
279301
const result = await summarizeConversation(
280302
messages,
281303
mockApiHandler,
@@ -285,9 +307,17 @@ describe("foldedFileContext", () => {
285307
undefined, // customCondensingPrompt
286308
undefined, // metadata
287309
undefined, // environmentDetails
288-
foldedFileContextSections, // Array of sections, each file in its own block
310+
filesReadByRoo,
311+
cwd,
312+
undefined, // rooIgnoreController
289313
)
290314

315+
// Verify generateFoldedFileContext was called with the right arguments
316+
expect(mockedGenerateFoldedFileContext).toHaveBeenCalledWith(filesReadByRoo, {
317+
cwd,
318+
rooIgnoreController: undefined,
319+
})
320+
291321
// Verify the summary was created
292322
expect(result.summary).toBeDefined()
293323
expect(result.messages.length).toBeGreaterThan(0)
@@ -318,7 +348,7 @@ describe("foldedFileContext", () => {
318348
expect(apiFileBlock.text).toContain("fetchData")
319349
})
320350

321-
it("should not include file context section when foldedFileContextSections is empty", async () => {
351+
it("should not include file context section when filesReadByRoo is empty", async () => {
322352
const { summarizeConversation } = await import("../index")
323353

324354
const mockApiHandler = new MockApiHandler()
@@ -334,6 +364,9 @@ describe("foldedFileContext", () => {
334364
{ role: "user", content: "Seventh message" },
335365
]
336366

367+
// Reset the mock to ensure clean state
368+
mockedGenerateFoldedFileContext.mockClear()
369+
337370
const result = await summarizeConversation(
338371
messages,
339372
mockApiHandler,
@@ -343,9 +376,14 @@ describe("foldedFileContext", () => {
343376
undefined, // customCondensingPrompt
344377
undefined, // metadata
345378
undefined, // environmentDetails
346-
[], // Empty foldedFileContextSections array
379+
[], // Empty filesReadByRoo array
380+
"/test/project",
381+
undefined, // rooIgnoreController
347382
)
348383

384+
// generateFoldedFileContext should NOT be called when filesReadByRoo is empty
385+
expect(mockedGenerateFoldedFileContext).not.toHaveBeenCalled()
386+
349387
// Find the summary message
350388
const summaryMessage = result.messages.find((msg: any) => msg.isSummary)
351389
expect(summaryMessage).toBeDefined()

src/core/condense/index.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import { ApiMessage } from "../task-persistence/apiMessages"
99
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
1010
import { findLast } from "../../shared/array"
1111
import { supportPrompt } from "../../shared/support-prompt"
12+
import { RooIgnoreController } from "../ignore/RooIgnoreController"
1213

1314
// Re-export folded file context utilities
14-
export { generateFoldedFileContext } from "./foldedFileContext"
15+
import { generateFoldedFileContext } from "./foldedFileContext"
16+
export { generateFoldedFileContext }
1517
export type { FoldedFileContextResult, FoldedFileContextOptions } from "./foldedFileContext"
1618

1719
export const MIN_CONDENSE_THRESHOLD = 5 // Minimum percentage of context window to trigger condensing
@@ -153,7 +155,9 @@ export type SummarizeResponse = {
153155
* @param {string} customCondensingPrompt - Optional custom prompt to use for condensing
154156
* @param {ApiHandlerCreateMessageMetadata} metadata - Optional metadata to pass to createMessage (tools, taskId, etc.)
155157
* @param {string} environmentDetails - Optional environment details string to include in the summary (only used when isAutomaticTrigger=true)
156-
* @param {string[]} foldedFileContextSections - Optional array of folded file context sections (each file in its own <system-reminder> block)
158+
* @param {string[]} filesReadByRoo - Optional array of file paths read by Roo during the task (will be folded via tree-sitter)
159+
* @param {string} cwd - Optional current working directory for resolving file paths (required if filesReadByRoo is provided)
160+
* @param {RooIgnoreController} rooIgnoreController - Optional controller for file access validation
157161
* @returns {SummarizeResponse} - The result of the summarization operation (see above)
158162
*/
159163
export async function summarizeConversation(
@@ -165,7 +169,9 @@ export async function summarizeConversation(
165169
customCondensingPrompt?: string,
166170
metadata?: ApiHandlerCreateMessageMetadata,
167171
environmentDetails?: string,
168-
foldedFileContextSections?: string[],
172+
filesReadByRoo?: string[],
173+
cwd?: string,
174+
rooIgnoreController?: RooIgnoreController,
169175
): Promise<SummarizeResponse> {
170176
TelemetryService.instance.captureContextCondensed(
171177
taskId,
@@ -308,16 +314,27 @@ ${commandBlocks}
308314
})
309315
}
310316

311-
// Add folded file context (smart code folding) if present
317+
// Generate and add folded file context (smart code folding) if file paths are provided
312318
// Each file gets its own <system-reminder> block as a separate content block
313-
if (foldedFileContextSections && foldedFileContextSections.length > 0) {
314-
for (const section of foldedFileContextSections) {
315-
if (section.trim()) {
316-
summaryContent.push({
317-
type: "text",
318-
text: section,
319-
})
319+
if (filesReadByRoo && filesReadByRoo.length > 0 && cwd) {
320+
try {
321+
const foldedResult = await generateFoldedFileContext(filesReadByRoo, {
322+
cwd,
323+
rooIgnoreController,
324+
})
325+
if (foldedResult.sections.length > 0) {
326+
for (const section of foldedResult.sections) {
327+
if (section.trim()) {
328+
summaryContent.push({
329+
type: "text",
330+
text: section,
331+
})
332+
}
333+
}
320334
}
335+
} catch (error) {
336+
console.error("[summarizeConversation] Failed to generate folded file context:", error)
337+
// Continue without folded context - non-critical failure
321338
}
322339
}
323340

0 commit comments

Comments
 (0)