Skip to content

Commit 0cb3e23

Browse files
committed
Merge cte/remove-command-output-ask
1 parent 4a36340 commit 0cb3e23

File tree

11 files changed

+34
-42
lines changed

11 files changed

+34
-42
lines changed

evals/packages/types/src/roo-code.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,6 @@ export const isGlobalStateKey = (key: string): key is Keys<GlobalState> =>
758758
export const clineAsks = [
759759
"followup",
760760
"command",
761-
"command_output",
762761
"completion_result",
763762
"tool",
764763
"api_req_failed",

src/core/Cline.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class Cline extends EventEmitter<ClineEvents> {
161161
private askResponse?: ClineAskResponse
162162
private askResponseText?: string
163163
private askResponseImages?: string[]
164-
private lastMessageTs?: number
164+
public lastMessageTs?: number
165165

166166
// Not private since it needs to be accessible by tools.
167167
consecutiveMistakeCount: number = 0
@@ -440,7 +440,6 @@ export class Cline extends EventEmitter<ClineEvents> {
440440
*/
441441
askTs = lastMessage.ts
442442
this.lastMessageTs = askTs
443-
// lastMessage.ts = askTs
444443
lastMessage.text = text
445444
lastMessage.partial = false
446445
lastMessage.progressStatus = progressStatus
@@ -466,12 +465,20 @@ export class Cline extends EventEmitter<ClineEvents> {
466465
await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text })
467466
}
468467

468+
console.log(`[ask / ${type} / ${askTs}] waiting for ask response`)
469+
469470
await pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 })
470471

472+
const isTsMismatch = this.lastMessageTs !== askTs
473+
474+
console.log(
475+
`[ask / ${type} / ${askTs}] pWaitFor returned, askResponse = ${this.askResponse}, isTsMismatch = ${isTsMismatch}`,
476+
)
477+
471478
if (this.lastMessageTs !== askTs) {
472-
// Could happen if we send multiple asks in a row i.e. with
473-
// command_output. It's important that when we know an ask could
474-
// fail, it is handled gracefully.
479+
// Could happen if we send multiple asks in a row.
480+
// It's important that when we know an ask could fail it is handled
481+
// gracefully.
475482
throw new Error("Current ask promise was ignored")
476483
}
477484

src/core/tools/executeCommandTool.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ export async function executeCommandTool(
4848

4949
cline.consecutiveMistakeCount = 0
5050

51-
const executionId = Date.now().toString()
5251
command = unescapeHtmlEntities(command) // Unescape HTML entities.
53-
const didApprove = await askApproval("command", command, { id: executionId })
52+
const didApprove = await askApproval("command", command)
5453

5554
if (!didApprove) {
5655
return
5756
}
5857

58+
const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString()
59+
5960
const clineProvider = await cline.providerRef.deref()
6061
const clineProviderState = await clineProvider?.getState()
6162
const { terminalOutputLineLimit = 500, terminalShellIntegrationDisabled = false } = clineProviderState ?? {}
@@ -162,6 +163,9 @@ export async function executeCommand(
162163
console.log(`[executeCommand] onShellExecutionStarted: ${pid}`)
163164
const status: CommandExecutionStatus = { executionId, status: "started", pid, command }
164165
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
166+
// This `command_output` message tells the webview to render the
167+
// appropriate primary and secondary buttons.
168+
cline.say("command_output", "")
165169
},
166170
onShellExecutionComplete: (details: ExitCodeDetails) => {
167171
const status: CommandExecutionStatus = { executionId, status: "exited", exitCode: details.exitCode }
@@ -201,8 +205,7 @@ export async function executeCommand(
201205
// Wait for a short delay to ensure all messages are sent to the webview.
202206
// This delay allows time for non-awaited promises to be created and
203207
// for their associated messages to be sent to the webview, maintaining
204-
// the correct order of messages (although the webview is smart about
205-
// grouping command_output messages despite any gaps anyways).
208+
// the correct order of messages.
206209
await delay(50)
207210

208211
if (message) {

src/exports/roo-code.d.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ type ClineMessage = {
305305
| (
306306
| "followup"
307307
| "command"
308-
| "command_output"
309308
| "completion_result"
310309
| "tool"
311310
| "api_req_failed"
@@ -381,7 +380,6 @@ type RooCodeEvents = {
381380
| (
382381
| "followup"
383382
| "command"
384-
| "command_output"
385383
| "completion_result"
386384
| "tool"
387385
| "api_req_failed"

src/exports/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,6 @@ type ClineMessage = {
310310
| (
311311
| "followup"
312312
| "command"
313-
| "command_output"
314313
| "completion_result"
315314
| "tool"
316315
| "api_req_failed"
@@ -390,7 +389,6 @@ type RooCodeEvents = {
390389
| (
391390
| "followup"
392391
| "command"
393-
| "command_output"
394392
| "completion_result"
395393
| "tool"
396394
| "api_req_failed"

src/schemas/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,6 @@ export const isGlobalStateKey = (key: string): key is Keys<GlobalState> =>
767767
export const clineAsks = [
768768
"followup",
769769
"command",
770-
"command_output",
771770
"completion_result",
772771
"tool",
773772
"api_req_failed",

src/shared/__tests__/combineCommandSequences.test.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,13 @@ const messages: ClineMessage[] = [
2121
},
2222
{ ts: 1745710930748, type: "ask", ask: "command", text: "ping www.google.com", partial: false },
2323
{ ts: 1745710930894, type: "say", say: "command_output", text: "", images: undefined },
24-
{ ts: 1745710930894, type: "ask", ask: "command_output", text: "" },
2524
{
2625
ts: 1745710930954,
2726
type: "say",
2827
say: "command_output",
2928
text: "PING www.google.com (142.251.46.228): 56 data bytes\n",
3029
images: undefined,
3130
},
32-
{
33-
ts: 1745710930954,
34-
type: "ask",
35-
ask: "command_output",
36-
text: "PING www.google.com (142.251.46.228): 56 data bytes\n",
37-
},
3831
]
3932

4033
describe("combineCommandSequences", () => {

src/shared/combineCommandSequences.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[
3939
break // Stop if we encounter the next command.
4040
}
4141

42-
if (ask === "command_output" || say === "command_output") {
42+
if (say === "command_output") {
4343
if (!previous) {
4444
combinedText += `\n${COMMAND_OUTPUT_STRING}`
4545
}
@@ -68,7 +68,7 @@ export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[
6868
// Second pass: remove command_outputs and replace original commands with
6969
// combined ones.
7070
return messages
71-
.filter((msg) => !(msg.ask === "command_output" || msg.say === "command_output"))
71+
.filter((msg) => msg.say !== "command_output")
7272
.map((msg) => {
7373
if (msg.type === "ask" && msg.ask === "command") {
7474
return combinedCommands.find((cmd) => cmd.ts === msg.ts) || msg

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ export const ChatRowContent = ({
985985
{icon}
986986
{title}
987987
</div>
988-
<CommandExecution executionId={message.progressStatus?.id} text={message.text} />
988+
<CommandExecution executionId={message.ts.toString()} text={message.text} />
989989
</>
990990
)
991991
case "use_mcp_server":

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
116116
const [selectedImages, setSelectedImages] = useState<string[]>([])
117117

118118
// we need to hold on to the ask because useEffect > lastMessage will always let us know when an ask comes in and handle it, but by the time handleMessage is called, the last message might not be the ask anymore (it could be a say that followed)
119-
const [clineAsk, setClineAsk] = useState<ClineAsk | undefined>(undefined)
119+
const [clineAsk, setClineAsk] = useState<ClineAsk | "command_output" | undefined>(undefined)
120120
const [enableButtons, setEnableButtons] = useState<boolean>(false)
121121
const [primaryButtonText, setPrimaryButtonText] = useState<string | undefined>(undefined)
122122
const [secondaryButtonText, setSecondaryButtonText] = useState<string | undefined>(undefined)
@@ -229,13 +229,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
229229
setPrimaryButtonText(t("chat:runCommand.title"))
230230
setSecondaryButtonText(t("chat:reject.title"))
231231
break
232-
case "command_output":
233-
setTextAreaDisabled(false)
234-
setClineAsk("command_output")
235-
setEnableButtons(true)
236-
setPrimaryButtonText(t("chat:proceedWhileRunning.title"))
237-
setSecondaryButtonText(t("chat:killCommand.title"))
238-
break
239232
case "use_mcp_server":
240233
if (!isAutoApproved(lastMessage) && !isPartial) {
241234
playSound("notification")
@@ -289,8 +282,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
289282
setTextAreaDisabled(true)
290283
break
291284
case "api_req_started":
292-
if (secondLastMessage?.ask === "command_output") {
293-
// If the last ask is a command_output, and we
285+
if (secondLastMessage?.ask === "command") {
286+
// If the last ask is a command, and we
294287
// receive an api_req_started, then that means
295288
// the command has finished and we don't need
296289
// input from the user anymore (in every other
@@ -304,12 +297,18 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
304297
setEnableButtons(false)
305298
}
306299
break
300+
case "command_output":
301+
setTextAreaDisabled(false)
302+
setClineAsk("command_output")
303+
setEnableButtons(true)
304+
setPrimaryButtonText(t("chat:proceedWhileRunning.title"))
305+
setSecondaryButtonText(t("chat:killCommand.title"))
306+
break
307307
case "api_req_finished":
308308
case "error":
309309
case "text":
310310
case "browser_action":
311311
case "browser_action_result":
312-
case "command_output":
313312
case "mcp_server_request_started":
314313
case "mcp_server_response":
315314
case "completion_result":
@@ -399,7 +398,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
399398
case "tool":
400399
case "browser_action_launch":
401400
case "command": // User can provide feedback to a tool or command use.
402-
case "command_output": // User can send input to command stdin.
403401
case "use_mcp_server":
404402
case "completion_result": // If this happens then the user has feedback for the completion result.
405403
case "resume_task":
@@ -521,6 +519,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
521519
vscode.postMessage({ type: "terminalOperation", terminalOperation: "abort" })
522520
break
523521
}
522+
524523
setTextAreaDisabled(true)
525524
setClineAsk(undefined)
526525
setEnableButtons(false)

0 commit comments

Comments
 (0)