Skip to content

Commit 12d041a

Browse files
committed
Different approach
1 parent d5fc419 commit 12d041a

File tree

7 files changed

+79
-36
lines changed

7 files changed

+79
-36
lines changed

packages/types/src/events.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from "zod"
22

3-
import { clineMessageSchema, tokenUsageSchema, clineAskSchema } from "./message.js"
3+
import { clineMessageSchema, tokenUsageSchema } from "./message.js"
44
import { toolNamesSchema, toolUsageSchema } from "./tool.js"
55

66
/**
@@ -75,7 +75,7 @@ export const rooCodeEventsSchema = z.object({
7575
]),
7676
[RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]),
7777
[RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]),
78-
[RooCodeEventName.TaskAskRequiresInteraction]: z.tuple([z.string(), clineAskSchema]),
78+
[RooCodeEventName.TaskAskRequiresInteraction]: z.tuple([z.string(), clineMessageSchema]),
7979

8080
[RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]),
8181
[RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]),

packages/types/src/message.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ export const clineAskSchema = z.enum(clineAsks)
4545
export type ClineAsk = z.infer<typeof clineAskSchema>
4646

4747
/**
48-
* BlockingAsk
48+
* IdleAsk
49+
*
50+
* Asks that put the task into an "idle" state.
4951
*/
5052

51-
export const blockingAsks: ClineAsk[] = [
53+
export const idleAsks: ClineAsk[] = [
5254
"api_req_failed",
5355
"mistake_limit_reached",
5456
"completion_result",
@@ -58,10 +60,24 @@ export const blockingAsks: ClineAsk[] = [
5860
"auto_approval_max_req_reached",
5961
] as const
6062

61-
export type BlockingAsk = (typeof blockingAsks)[number]
63+
export type IdleAsk = (typeof idleAsks)[number]
64+
65+
export function isIdleAsk(ask: ClineAsk): ask is IdleAsk {
66+
return idleAsks.includes(ask)
67+
}
68+
69+
/**
70+
* InteractiveAsk
71+
*
72+
* Asks that put the task into an "user interaction required" state.
73+
*/
74+
75+
export const interactiveAsks: ClineAsk[] = ["command"] as const
76+
77+
export type InteractiveAsk = (typeof interactiveAsks)[number]
6278

63-
export function isBlockingAsk(ask: ClineAsk): ask is BlockingAsk {
64-
return blockingAsks.includes(ask)
79+
export function isInteractiveAsk(ask: ClineAsk): ask is InteractiveAsk {
80+
return interactiveAsks.includes(ask)
6581
}
6682

6783
/**

packages/types/src/task.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod"
22

33
import { RooCodeEventName } from "./events.js"
4-
import { type ClineMessage, type BlockingAsk, type TokenUsage, type ClineAsk } from "./message.js"
4+
import { type ClineMessage, type IdleAsk, type TokenUsage } from "./message.js"
55
import { type ToolUsage, type ToolName } from "./tool.js"
66
import type { StaticAppProperties, GitProperties, TelemetryProperties } from "./telemetry.js"
77

@@ -71,7 +71,7 @@ export type TaskMetadata = z.infer<typeof taskMetadataSchema>
7171
export interface TaskLike {
7272
readonly taskId: string
7373
readonly rootTask?: TaskLike
74-
readonly blockingAsk?: BlockingAsk
74+
readonly blockingAsk?: IdleAsk
7575
readonly metadata: TaskMetadata
7676

7777
on<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
@@ -101,7 +101,7 @@ export type TaskEvents = {
101101
[RooCodeEventName.Message]: [{ action: "created" | "updated"; message: ClineMessage }]
102102
[RooCodeEventName.TaskModeSwitched]: [taskId: string, mode: string]
103103
[RooCodeEventName.TaskAskResponded]: []
104-
[RooCodeEventName.TaskAskRequiresInteraction]: [taskId: string, askType: ClineAsk]
104+
[RooCodeEventName.TaskAskRequiresInteraction]: [taskId: string, askMessage: ClineMessage]
105105

106106
// Task Analytics
107107
[RooCodeEventName.TaskToolFailed]: [taskId: string, tool: ToolName, error: string]

src/core/task/Task.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@ import {
2121
type ClineMessage,
2222
type ClineSay,
2323
type ClineAsk,
24-
type BlockingAsk,
24+
type IdleAsk,
25+
type InteractiveAsk,
2526
type ToolProgressStatus,
2627
type HistoryItem,
2728
RooCodeEventName,
2829
TelemetryEventName,
2930
TodoItem,
31+
DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
3032
getApiProtocol,
3133
getModelId,
32-
DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
33-
isBlockingAsk,
34+
isIdleAsk,
35+
isInteractiveAsk,
3436
} from "@roo-code/types"
3537
import { TelemetryService } from "@roo-code/telemetry"
3638
import { CloudService, ExtensionBridgeService } from "@roo-code/cloud"
@@ -182,7 +184,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
182184
providerRef: WeakRef<ClineProvider>
183185
private readonly globalStoragePath: string
184186
abort: boolean = false
185-
blockingAsk?: BlockingAsk
187+
idleAsk?: IdleAsk
188+
interactiveAsk?: InteractiveAsk
186189
didFinishAbortingStream = false
187190
abandoned = false
188191
isInitialized = false
@@ -713,12 +716,29 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
713716
await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, isProtected })
714717
}
715718

716-
// Detect if the task will enter an idle state.
717-
const isReady = this.askResponse !== undefined || this.lastMessageTs !== askTs
719+
// The state is mutable if the message is complete and the task will
720+
// block (via the `pWaitFor`).
721+
const isBlocking = !(this.askResponse !== undefined || this.lastMessageTs !== askTs)
722+
const isStateMutable = !partial && isBlocking
718723

719-
if (!partial && !isReady && isBlockingAsk(type)) {
720-
this.blockingAsk = type
721-
this.emit(RooCodeEventName.TaskIdle, this.taskId)
724+
// The task will enter an idle state.
725+
let goIdleTimeout: NodeJS.Timeout | undefined
726+
727+
if (isStateMutable && isIdleAsk(type)) {
728+
goIdleTimeout = setTimeout(() => {
729+
this.idleAsk = undefined
730+
this.emit(RooCodeEventName.TaskActive, this.taskId)
731+
}, 1000)
732+
}
733+
734+
// The task will enter a "user interaction required" state.
735+
let goInteractiveTimeout: NodeJS.Timeout | undefined
736+
737+
if (isStateMutable && !isInteractiveAsk(type)) {
738+
goInteractiveTimeout = setTimeout(() => {
739+
this.interactiveAsk = undefined
740+
this.emit(RooCodeEventName.TaskActive, this.taskId)
741+
}, 1000)
722742
}
723743

724744
console.log(`[Task#${this.taskId}] pWaitFor askResponse(${type}) -> blocking`)
@@ -737,9 +757,22 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
737757
this.askResponseText = undefined
738758
this.askResponseImages = undefined
739759

760+
// Cancel the timeouts if they are still running.
761+
if (goIdleTimeout) {
762+
clearTimeout(goIdleTimeout)
763+
}
764+
765+
if (goInteractiveTimeout) {
766+
clearTimeout(goInteractiveTimeout)
767+
}
768+
769+
goIdleTimeout = undefined
770+
goInteractiveTimeout = undefined
771+
740772
// Switch back to an active state.
741-
if (this.blockingAsk) {
742-
this.blockingAsk = undefined
773+
if (this.idleAsk || this.interactiveAsk) {
774+
this.idleAsk = undefined
775+
this.interactiveAsk = undefined
743776
this.emit(RooCodeEventName.TaskActive, this.taskId)
744777
}
745778

src/core/webview/webviewMessageHandler.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,13 +348,6 @@ export const webviewMessageHandler = async (
348348
break
349349
case "askResponse":
350350
provider.getCurrentTask()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
351-
break
352-
case "askRequiresInteraction":
353-
if (message.askType) {
354-
const task = provider.getCurrentTask()
355-
task?.emit(RooCodeEventName.TaskAskRequiresInteraction, task.taskId, message.askType as ClineAsk)
356-
}
357-
358351
break
359352
case "autoCondenseContext":
360353
await updateGlobalState("autoCondenseContext", message.bool)

src/shared/WebviewMessage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type ModeConfig,
77
type InstallMarketplaceItemOptions,
88
type MarketplaceItem,
9+
type ClineMessage,
910
marketplaceItemSchema,
1011
} from "@roo-code/types"
1112
import type { ShareVisibility } from "@roo-code/cloud"
@@ -49,7 +50,6 @@ export interface WebviewMessage {
4950
| "webviewDidLaunch"
5051
| "newTask"
5152
| "askResponse"
52-
| "askRequiresInteraction"
5353
| "terminalOperation"
5454
| "clearTask"
5555
| "didShowAnnouncement"
@@ -218,8 +218,8 @@ export interface WebviewMessage {
218218
disabled?: boolean
219219
context?: string
220220
dataUri?: string
221+
askMessage?: ClineMessage
221222
askResponse?: ClineAskResponse
222-
askType?: string
223223
apiConfiguration?: ProviderSettings
224224
images?: string[]
225225
bool?: boolean

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
265265
if (lastMessage) {
266266
switch (lastMessage.type) {
267267
case "ask":
268-
// Reset user response flag when a new ask arrives to allow auto-approval
268+
// Reset user response flag when a new ask arrives to allow
269+
// auto-approval.
269270
userRespondedRef.current = false
270271
const isPartial = lastMessage.partial === true
272+
271273
switch (lastMessage.ask) {
272274
case "api_req_failed":
273275
playSound("progress_loop")
@@ -555,6 +557,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
555557
clearTimeout(autoApproveTimeoutRef.current)
556558
autoApproveTimeoutRef.current = null
557559
}
560+
558561
// Reset user response flag for new message
559562
userRespondedRef.current = false
560563

@@ -591,7 +594,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
591594
return
592595
}
593596

594-
// Mark that user has responded - this prevents any pending auto-approvals
597+
// Mark that user has responded - this prevents any pending
598+
// auto-approvals.
595599
userRespondedRef.current = true
596600

597601
if (messagesRef.current.length === 0) {
@@ -1593,7 +1597,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
15931597
return
15941598
}
15951599

1596-
// Exit early if user has already responded
1600+
// Exit early if user has already responded.
15971601
if (userRespondedRef.current) {
15981602
return
15991603
}
@@ -1671,9 +1675,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
16711675
setSendingDisabled(true)
16721676
setClineAsk(undefined)
16731677
setEnableButtons(false)
1674-
} else if (lastMessage?.ask) {
1675-
// Notify the extension host that this ask requires user interaction.
1676-
vscode.postMessage({ type: "askRequiresInteraction", askType: lastMessage.ask })
16771678
}
16781679
}
16791680

0 commit comments

Comments
 (0)