Skip to content
Open
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
3 changes: 0 additions & 3 deletions packages/types/src/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ export const organizationDefaultSettingsSchema = globalSettingsSchema
maxWorkspaceFiles: true,
showRooIgnoredFiles: true,
terminalCommandDelay: true,
terminalCompressProgressBar: true,
terminalOutputLineLimit: true,
terminalShellIntegrationDisabled: true,
terminalShellIntegrationTimeout: true,
terminalZshClearEolMark: true,
Expand All @@ -112,7 +110,6 @@ export const organizationDefaultSettingsSchema = globalSettingsSchema
maxReadFileLine: z.number().int().gte(-1).optional(),
maxWorkspaceFiles: z.number().int().nonnegative().optional(),
terminalCommandDelay: z.number().int().nonnegative().optional(),
terminalOutputLineLimit: z.number().int().nonnegative().optional(),
terminalShellIntegrationTimeout: z.number().int().nonnegative().optional(),
}),
)
Expand Down
44 changes: 34 additions & 10 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,40 @@ import { languagesSchema } from "./vscode.js"
export const DEFAULT_WRITE_DELAY_MS = 1000

/**
* Default terminal output character limit constant.
* This provides a reasonable default that aligns with typical terminal usage
* while preventing context window explosions from extremely long lines.
* Terminal output preview size options for persisted command output.
*
* Controls how much command output is kept in memory as a "preview" before
* the LLM decides to retrieve more via `read_command_output`. Larger previews
* mean more immediate context but consume more of the context window.
*
* - `small`: 2KB preview - Best for long-running commands with verbose output
* - `medium`: 4KB preview - Balanced default for most use cases
* - `large`: 8KB preview - Best when commands produce critical info early
*
* @see OutputInterceptor - Uses this setting to determine when to spill to disk
* @see PersistedCommandOutput - Contains the resulting preview and artifact reference
*/
export const DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT = 50_000
export type TerminalOutputPreviewSize = "small" | "medium" | "large"

/**
* Byte limits for each terminal output preview size.
*
* Maps preview size names to their corresponding byte thresholds.
* When command output exceeds these thresholds, the excess is persisted
* to disk and made available via the `read_command_output` tool.
*/
export const TERMINAL_PREVIEW_BYTES: Record<TerminalOutputPreviewSize, number> = {
small: 2048, // 2KB
medium: 4096, // 4KB
large: 8192, // 8KB
}

/**
* Default terminal output preview size.
* The "medium" (4KB) setting provides a good balance between immediate
* visibility and context window conservation for most use cases.
*/
export const DEFAULT_TERMINAL_OUTPUT_PREVIEW_SIZE: TerminalOutputPreviewSize = "medium"

/**
* Minimum checkpoint timeout in seconds.
Expand Down Expand Up @@ -147,8 +176,7 @@ export const globalSettingsSchema = z.object({
maxImageFileSize: z.number().optional(),
maxTotalImageSize: z.number().optional(),

terminalOutputLineLimit: z.number().optional(),
terminalOutputCharacterLimit: z.number().optional(),
terminalOutputPreviewSize: z.enum(["small", "medium", "large"]).optional(),
terminalShellIntegrationTimeout: z.number().optional(),
terminalShellIntegrationDisabled: z.boolean().optional(),
terminalCommandDelay: z.number().optional(),
Expand All @@ -157,7 +185,6 @@ export const globalSettingsSchema = z.object({
terminalZshOhMy: z.boolean().optional(),
terminalZshP10k: z.boolean().optional(),
terminalZdotdir: z.boolean().optional(),
terminalCompressProgressBar: z.boolean().optional(),

diagnosticsEnabled: z.boolean().optional(),

Expand Down Expand Up @@ -338,16 +365,13 @@ export const EVALS_SETTINGS: RooCodeSettings = {
soundEnabled: false,
soundVolume: 0.5,

terminalOutputLineLimit: 500,
terminalOutputCharacterLimit: DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT,
terminalShellIntegrationTimeout: 30000,
terminalCommandDelay: 0,
terminalPowershellCounter: false,
terminalZshOhMy: true,
terminalZshClearEolMark: true,
terminalZshP10k: false,
terminalZdotdir: true,
terminalCompressProgressBar: true,
terminalShellIntegrationDisabled: true,

diagnosticsEnabled: true,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export const clineSays = [
"codebase_search_result",
"user_edit_todos",
"too_many_tools_warning",
"tool",
] as const

export const clineSaySchema = z.enum(clineSays)
Expand Down
66 changes: 66 additions & 0 deletions packages/types/src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,69 @@ export const commandExecutionStatusSchema = z.discriminatedUnion("status", [
])

export type CommandExecutionStatus = z.infer<typeof commandExecutionStatusSchema>

/**
* PersistedCommandOutput
*
* Represents the result of a terminal command execution that may have been
* truncated and persisted to disk.
*
* When command output exceeds the configured preview threshold, the full
* output is saved to a disk artifact file. The LLM receives this structure
* which contains:
* - A preview of the output (for immediate display in context)
* - Metadata about the full output (size, truncation status)
* - A path to the artifact file for later retrieval via `read_command_output`
*
* ## Usage in execute_command Response
*
* The response format depends on whether truncation occurred:
*
* **Not truncated** (output fits in preview):
* ```json
* {
* "preview": "full output here...",
* "totalBytes": 1234,
* "artifactPath": null,
* "truncated": false
* }
* ```
*
* **Truncated** (output exceeded threshold):
* ```json
* {
* "preview": "first 4KB of output...",
* "totalBytes": 1048576,
* "artifactPath": "/path/to/tasks/123/command-output/cmd-1706119234567.txt",
* "truncated": true
* }
* ```
*
* @see OutputInterceptor - Creates these results during command execution
* @see ReadCommandOutputTool - Retrieves full content from artifact files
*/
export interface PersistedCommandOutput {
/**
* Preview of the command output, truncated to the preview threshold.
* Always contains the beginning of the output, even if truncated.
*/
preview: string

/**
* Total size of the command output in bytes.
* Useful for determining if additional reads are needed.
*/
totalBytes: number

/**
* Absolute path to the artifact file containing full output.
* `null` if output wasn't truncated (no artifact was created).
*/
artifactPath: string | null

/**
* Whether the output was truncated (exceeded preview threshold).
* When `true`, use `read_command_output` to retrieve full content.
*/
truncated: boolean
}
1 change: 1 addition & 0 deletions packages/types/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type ToolGroup = z.infer<typeof toolGroupsSchema>
export const toolNames = [
"execute_command",
"read_file",
"read_command_output",
"write_to_file",
"apply_diff",
"search_and_replace",
Expand Down
9 changes: 6 additions & 3 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,7 @@ export type ExtensionState = Pick<
| "soundEnabled"
| "soundVolume"
| "maxConcurrentFileReads"
| "terminalOutputLineLimit"
| "terminalOutputCharacterLimit"
| "terminalOutputPreviewSize"
| "terminalShellIntegrationTimeout"
| "terminalShellIntegrationDisabled"
| "terminalCommandDelay"
Expand All @@ -312,7 +311,6 @@ export type ExtensionState = Pick<
| "terminalZshOhMy"
| "terminalZshP10k"
| "terminalZdotdir"
| "terminalCompressProgressBar"
| "diagnosticsEnabled"
| "language"
| "modeApiConfigs"
Expand Down Expand Up @@ -780,6 +778,7 @@ export interface ClineSayTool {
| "newFileCreated"
| "codebaseSearch"
| "readFile"
| "readCommandOutput"
| "fetchInstructions"
| "listFilesTopLevel"
| "listFilesRecursive"
Expand All @@ -792,6 +791,10 @@ export interface ClineSayTool {
| "runSlashCommand"
| "updateTodoList"
path?: string
// For readCommandOutput
readStart?: number
readEnd?: number
totalBytes?: number
diff?: string
content?: string
// Unified diff statistics computed by the extension
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/core/assistant-message/NativeToolCallParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,17 @@ export class NativeToolCallParser {
}
break

case "read_command_output":
if (args.artifact_id !== undefined) {
nativeArgs = {
artifact_id: args.artifact_id,
search: args.search,
offset: args.offset,
limit: args.limit,
} as NativeArgsFor<TName>
}
break

case "write_to_file":
if (args.path !== undefined && args.content !== undefined) {
nativeArgs = {
Expand Down
13 changes: 12 additions & 1 deletion src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Task } from "../task/Task"
import { fetchInstructionsTool } from "../tools/FetchInstructionsTool"
import { listFilesTool } from "../tools/ListFilesTool"
import { readFileTool } from "../tools/ReadFileTool"
import { readCommandOutputTool } from "../tools/ReadCommandOutputTool"
import { writeToFileTool } from "../tools/WriteToFileTool"
import { searchAndReplaceTool } from "../tools/SearchAndReplaceTool"
import { searchReplaceTool } from "../tools/SearchReplaceTool"
Expand Down Expand Up @@ -402,8 +403,10 @@ export async function presentAssistantMessage(cline: Task) {
return `[${block.name}]`
case "switch_mode":
return `[${block.name} to '${block.params.mode_slug}'${block.params.reason ? ` because: ${block.params.reason}` : ""}]`
case "codebase_search": // Add case for the new tool
case "codebase_search":
return `[${block.name} for '${block.params.query}']`
case "read_command_output":
return `[${block.name} for '${block.params.artifact_id}']`
case "update_todo_list":
return `[${block.name}]`
case "new_task": {
Expand Down Expand Up @@ -846,6 +849,13 @@ export async function presentAssistantMessage(cline: Task) {
pushToolResult,
})
break
case "read_command_output":
await readCommandOutputTool.handle(cline, block as ToolUse<"read_command_output">, {
askApproval,
handleError,
pushToolResult,
})
break
case "use_mcp_tool":
await useMcpToolTool.handle(cline, block as ToolUse<"use_mcp_tool">, {
askApproval,
Expand Down Expand Up @@ -1088,6 +1098,7 @@ function containsXmlToolMarkup(text: string): boolean {
"generate_image",
"list_files",
"new_task",
"read_command_output",
"read_file",
"search_and_replace",
"search_files",
Expand Down
19 changes: 3 additions & 16 deletions src/core/environment/getEnvironmentDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import pWaitFor from "p-wait-for"
import delay from "delay"

import type { ExperimentId } from "@roo-code/types"
import { DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT } from "@roo-code/types"

import { formatLanguage } from "../../shared/language"
import { defaultModeSlug, getFullModeDetails } from "../../shared/modes"
Expand All @@ -26,11 +25,7 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo

const clineProvider = cline.providerRef.deref()
const state = await clineProvider?.getState()
const {
terminalOutputLineLimit = 500,
terminalOutputCharacterLimit = DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT,
maxWorkspaceFiles = 200,
} = state ?? {}
const { maxWorkspaceFiles = 200 } = state ?? {}

// It could be useful for cline to know if the user went from one or no
// file to another between messages, so we always include this context.
Expand Down Expand Up @@ -112,11 +107,7 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
let newOutput = TerminalRegistry.getUnretrievedOutput(busyTerminal.id)

if (newOutput) {
newOutput = Terminal.compressTerminalOutput(
newOutput,
terminalOutputLineLimit,
terminalOutputCharacterLimit,
)
newOutput = Terminal.compressTerminalOutput(newOutput)
terminalDetails += `\n### New Output\n${newOutput}`
}
}
Expand Down Expand Up @@ -144,11 +135,7 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
let output = process.getUnretrievedOutput()

if (output) {
output = Terminal.compressTerminalOutput(
output,
terminalOutputLineLimit,
terminalOutputCharacterLimit,
)
output = Terminal.compressTerminalOutput(output)
terminalOutputs.push(`Command: \`${process.command}\`\n${output}`)
}
}
Expand Down
Loading
Loading