Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0a70e04
feat: add read_command_output tool for retrieving truncated command o…
hannesrudolph Jan 24, 2026
525bf9f
fix: address PR review comments
hannesrudolph Jan 24, 2026
980e616
fix: resolve CI failures for PR #10944
hannesrudolph Jan 25, 2026
cd1b9f4
fix(parser): add read_command_output to NativeToolCallParser
hannesrudolph Jan 27, 2026
47c3f49
feat(ui): add read_command_output activity indicator
hannesrudolph Jan 27, 2026
e60d689
refactor: remove terminalOutputLineLimit and terminalOutputCharacterL…
hannesrudolph Jan 27, 2026
af0f70a
Delete claude-code.md
hannesrudolph Jan 27, 2026
d4e0305
Delete codex-extract-terminal-spawning-tool.md
hannesrudolph Jan 27, 2026
22346f0
Delete Roo-EXTRACTION-terminal-shell-integration.md
hannesrudolph Jan 27, 2026
cf74729
feat: update OutputInterceptor to use 50/50 head/tail split like Codex
hannesrudolph Jan 27, 2026
2dcd3a1
fix: ensure lossless artifact storage and strict mode compatibility
hannesrudolph Jan 27, 2026
f5faf0f
fix: use chunked streaming for search to avoid memory blowup
hannesrudolph Jan 27, 2026
6bc2a14
fix: address review feedback for lossless terminal output
hannesrudolph Jan 27, 2026
5711f1f
fix: ensure onCompleted callback finishes before using persistedResult
hannesrudolph Jan 27, 2026
70787f7
feat: align output limits with terminal integration spec
hannesrudolph Jan 28, 2026
f15bea2
feat: update i18n labels for new preview sizes (5KB/10KB/20KB)
hannesrudolph Jan 28, 2026
d6aab9f
fix: display search pattern and match count in read_command_output UI
hannesrudolph Jan 28, 2026
3b99b93
feat: add write_stdin tool for interactive terminal support
hannesrudolph Jan 27, 2026
c9af762
feat: add terminate_session and list_sessions tools
hannesrudolph Jan 28, 2026
cb28f75
feat: add stdin support for ExecaTerminalProcess
hannesrudolph Jan 28, 2026
56da141
feat(ui): improve write_stdin, terminate_session, list_sessions rende…
hannesrudolph Jan 28, 2026
1071717
feat(ui): add terminal session indicator in TaskHeader
hannesrudolph Jan 28, 2026
bb5ee6c
fix: add activeTerminalSessions to initial state in ExtensionStateCon…
hannesrudolph Jan 28, 2026
65a6dde
i18n: add missing translations for interactive terminal feature
hannesrudolph Jan 28, 2026
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: 3 additions & 0 deletions packages/types/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export type ToolGroup = z.infer<typeof toolGroupsSchema>

export const toolNames = [
"execute_command",
"write_stdin",
"terminate_session",
"list_sessions",
"read_file",
"read_command_output",
"write_to_file",
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export type ExtensionState = Pick<
organizationSettingsVersion?: number

isBrowserSessionActive: boolean // Actual browser session state
activeTerminalSessions: number // Count of active terminal sessions for current task

autoCondenseContext: boolean
autoCondenseContextPercent: number
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.

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

case "write_stdin":
if (partialArgs.session_id !== undefined) {
nativeArgs = {
session_id: partialArgs.session_id,
chars: partialArgs.chars,
yield_time_ms: partialArgs.yield_time_ms,
max_output_tokens: partialArgs.max_output_tokens,
}
}
break

case "terminate_session":
if (partialArgs.session_id !== undefined) {
nativeArgs = {
session_id: partialArgs.session_id,
}
}
break

case "list_sessions":
// No parameters needed
nativeArgs = {}
break

case "write_to_file":
if (partialArgs.path || partialArgs.content) {
nativeArgs = {
Expand Down Expand Up @@ -687,6 +711,30 @@ export class NativeToolCallParser {
}
break

case "write_stdin":
if (args.session_id !== undefined) {
nativeArgs = {
session_id: args.session_id,
chars: args.chars,
yield_time_ms: args.yield_time_ms,
max_output_tokens: args.max_output_tokens,
} as NativeArgsFor<TName>
}
break

case "terminate_session":
if (args.session_id !== undefined) {
nativeArgs = {
session_id: args.session_id,
} as NativeArgsFor<TName>
}
break

case "list_sessions":
// No parameters needed
nativeArgs = {} as NativeArgsFor<TName>
break

case "apply_diff":
if (args.path !== undefined && args.diff !== undefined) {
nativeArgs = {
Expand Down
24 changes: 24 additions & 0 deletions src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import { applyPatchTool } from "../tools/ApplyPatchTool"
import { searchFilesTool } from "../tools/SearchFilesTool"
import { browserActionTool } from "../tools/BrowserActionTool"
import { executeCommandTool } from "../tools/ExecuteCommandTool"
import { writeStdinTool } from "../tools/WriteStdinTool"
import { terminateSessionTool } from "../tools/TerminateSessionTool"
import { listSessionsTool } from "../tools/ListSessionsTool"
import { useMcpToolTool } from "../tools/UseMcpToolTool"
import { accessMcpResourceTool } from "../tools/accessMcpResourceTool"
import { askFollowupQuestionTool } from "../tools/AskFollowupQuestionTool"
Expand Down Expand Up @@ -856,6 +859,27 @@ export async function presentAssistantMessage(cline: Task) {
pushToolResult,
})
break
case "write_stdin":
await writeStdinTool.handle(cline, block as ToolUse<"write_stdin">, {
askApproval,
handleError,
pushToolResult,
})
break
case "terminate_session":
await terminateSessionTool.handle(cline, block as ToolUse<"terminate_session">, {
askApproval,
handleError,
pushToolResult,
})
break
case "list_sessions":
await listSessionsTool.handle(cline, block as ToolUse<"list_sessions">, {
askApproval,
handleError,
pushToolResult,
})
break
case "use_mcp_tool":
await useMcpToolTool.handle(cline, block as ToolUse<"use_mcp_tool">, {
askApproval,
Expand Down
6 changes: 6 additions & 0 deletions src/core/prompts/tools/native-tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import executeCommand from "./execute_command"
import fetchInstructions from "./fetch_instructions"
import generateImage from "./generate_image"
import listFiles from "./list_files"
import listSessions from "./list_sessions"
import newTask from "./new_task"
import readCommandOutput from "./read_command_output"
import { createReadFileTool, type ReadFileToolOptions } from "./read_file"
Expand All @@ -19,7 +20,9 @@ import searchReplace from "./search_replace"
import edit_file from "./edit_file"
import searchFiles from "./search_files"
import switchMode from "./switch_mode"
import terminateSession from "./terminate_session"
import updateTodoList from "./update_todo_list"
import writeStdin from "./write_stdin"
import writeToFile from "./write_to_file"

export { getMcpServerTools } from "./mcp_server"
Expand Down Expand Up @@ -65,6 +68,7 @@ export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.Ch
fetchInstructions,
generateImage,
listFiles,
listSessions,
newTask,
readCommandOutput,
createReadFileTool(readFileOptions),
Expand All @@ -74,7 +78,9 @@ export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.Ch
edit_file,
searchFiles,
switchMode,
terminateSession,
updateTodoList,
writeStdin,
writeToFile,
] satisfies OpenAI.Chat.ChatCompletionTool[]
}
Expand Down
47 changes: 47 additions & 0 deletions src/core/prompts/tools/native-tools/list_sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type OpenAI from "openai"

/**
* Native tool definition for list_sessions.
*
* This tool allows the LLM to see all active terminal sessions
* that can be interacted with using write_stdin or terminated.
*/

const LIST_SESSIONS_DESCRIPTION = `Lists all active terminal sessions that were started by execute_command.

Use this tool when:
1. You need to know which sessions are still running
2. You forgot or lost track of a session_id
3. You want to see the status of multiple background processes
4. Before using write_stdin or terminate_session when unsure of the session_id

Returns a list of sessions with:
- session_id: The identifier to use with write_stdin or terminate_session
- command: The original command that was executed
- running: Whether the process is still actively running
- last_used: Relative time since last interaction

Example response:
┌──────────┬─────────────────────────────────┬─────────┬──────────────┐
│ Session │ Command │ Status │ Last Used │
├──────────┼─────────────────────────────────┼─────────┼──────────────┤
│ 1 │ npm run dev │ Running │ 30 seconds │
│ 2 │ python manage.py runserver │ Running │ 2 minutes │
│ 3 │ tail -f /var/log/syslog │ Stopped │ 5 minutes │
└──────────┴─────────────────────────────────┴─────────┴──────────────┘

This tool takes no parameters.`

export default {
type: "function",
function: {
name: "list_sessions",
description: LIST_SESSIONS_DESCRIPTION,
parameters: {
type: "object",
properties: {},
required: [],
additionalProperties: false,
},
},
} satisfies OpenAI.Chat.ChatCompletionTool
4 changes: 2 additions & 2 deletions src/core/prompts/tools/native-tools/read_command_output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The tool supports two modes:

Parameters:
- artifact_id: (required) The artifact filename from the truncated output message (e.g., "cmd-1706119234567.txt")
- search: (optional) Pattern to filter lines. Supports regex or literal strings. Case-insensitive. **Omit this parameter entirely if you don't need to filter - do not pass null or empty string.**
- search: (optional) Pattern to filter lines. Supports regex or literal strings. Case-insensitive.
- offset: (optional) Byte offset to start reading from. Default: 0. Use for pagination.
- limit: (optional) Maximum bytes to return. Default: 40KB.

Expand All @@ -38,7 +38,7 @@ Example: Finding specific test failures

const ARTIFACT_ID_DESCRIPTION = `The artifact filename from the truncated command output (e.g., "cmd-1706119234567.txt")`

const SEARCH_DESCRIPTION = `Optional regex or literal pattern to filter lines (case-insensitive, like grep). Omit this parameter if not searching - do not pass null or empty string.`
const SEARCH_DESCRIPTION = `Optional regex or literal pattern to filter lines (case-insensitive, like grep)`

const OFFSET_DESCRIPTION = `Byte offset to start reading from (default: 0, for pagination)`

Expand Down
47 changes: 47 additions & 0 deletions src/core/prompts/tools/native-tools/terminate_session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type OpenAI from "openai"

/**
* Native tool definition for terminate_session.
*
* This tool allows the LLM to terminate a running terminal session
* that was started by execute_command and is still active.
*/

const TERMINATE_SESSION_DESCRIPTION = `Terminates a running terminal session by sending an abort signal to the process.

Use this tool when:
1. A long-running command needs to be stopped (e.g., a server, watch process)
2. A command is stuck or unresponsive
3. You no longer need a background process that was started earlier
4. You want to free up resources from idle sessions

The session_id is returned by execute_command when a process is still running.

Parameters:
- session_id: (required) Identifier of the running exec session to terminate

Example: Terminating a development server
{ "session_id": 1234 }

Note: After termination, the session_id is no longer valid. Use list_sessions to see remaining active sessions.`

const SESSION_ID_DESCRIPTION = `Identifier of the running exec session to terminate (returned by execute_command)`

export default {
type: "function",
function: {
name: "terminate_session",
description: TERMINATE_SESSION_DESCRIPTION,
parameters: {
type: "object",
properties: {
session_id: {
type: "number",
description: SESSION_ID_DESCRIPTION,
},
},
required: ["session_id"],
additionalProperties: false,
},
},
} satisfies OpenAI.Chat.ChatCompletionTool
87 changes: 87 additions & 0 deletions src/core/prompts/tools/native-tools/write_stdin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type OpenAI from "openai"

/**
* Native tool definition for write_stdin.
*
* This tool allows the LLM to write characters to an existing terminal session
* and receive the resulting output. It enables interactive terminal workflows
* where the LLM can respond to prompts, provide input to running processes,
* and monitor long-running commands.
*/

const WRITE_STDIN_DESCRIPTION = `Writes characters to an existing exec session and returns recent output.

Use this tool when:
1. A command started with execute_command is still running and waiting for input
2. You need to respond to an interactive prompt (e.g., "Press y to continue", password prompts)
3. You want to poll a long-running process for new output without sending input

The session_id is returned by execute_command when a process is still running.

Parameters:
- session_id: (required) Identifier of the running exec session (returned by execute_command)
- chars: (optional) Characters to write to stdin. Use empty string or omit to just poll for output.
- yield_time_ms: (optional) Milliseconds to wait for output after writing (default: 250, min: 250, max: 30000)
- max_output_tokens: (optional) Maximum tokens to return in the response

Common use cases:
- Sending 'y' or 'n' to confirmation prompts
- Providing input to interactive CLI tools
- Sending Ctrl+C (\\x03) to terminate a process
- Polling for output from a long-running process

Example: Responding to a confirmation prompt
{ "session_id": 1234, "chars": "y\\n" }

Example: Sending Ctrl+C to stop a process
{ "session_id": 1234, "chars": "\\x03" }

Example: Polling for new output (no input)
{ "session_id": 1234, "chars": "", "yield_time_ms": 2000 }

Example: Providing password (note: prefer non-interactive approaches when possible)
{ "session_id": 1234, "chars": "password\\n" }`

const SESSION_ID_DESCRIPTION = `Identifier of the running exec session (returned by execute_command when a process is still running)`

const CHARS_DESCRIPTION = `Characters to write to stdin. May be empty to just poll for output. Supports escape sequences like \\n (newline) and \\x03 (Ctrl+C).`

const YIELD_TIME_MS_DESCRIPTION = `Milliseconds to wait for output after writing (default: 250, range: 250-30000). Use higher values when expecting delayed output.`

const MAX_OUTPUT_TOKENS_DESCRIPTION = `Maximum tokens to return in the response. Excess output will be truncated with head/tail preservation.`

export default {
type: "function",
function: {
name: "write_stdin",
description: WRITE_STDIN_DESCRIPTION,
// Note: strict mode is intentionally disabled for this tool.
// With strict: true, OpenAI requires ALL properties to be in the 'required' array,
// which forces the LLM to always provide explicit values (even null) for optional params.
// This creates verbose tool calls and poor UX. By disabling strict mode, the LLM can
// omit optional parameters entirely, making the tool easier to use.
parameters: {
type: "object",
properties: {
session_id: {
type: "number",
description: SESSION_ID_DESCRIPTION,
},
chars: {
type: "string",
description: CHARS_DESCRIPTION,
},
yield_time_ms: {
type: "number",
description: YIELD_TIME_MS_DESCRIPTION,
},
max_output_tokens: {
type: "number",
description: MAX_OUTPUT_TOKENS_DESCRIPTION,
},
},
required: ["session_id"],
additionalProperties: false,
},
},
} satisfies OpenAI.Chat.ChatCompletionTool
Loading
Loading