Skip to content

Commit cd86306

Browse files
committed
More progress
1 parent 12d041a commit cd86306

File tree

6 files changed

+126
-59
lines changed

6 files changed

+126
-59
lines changed

packages/types/src/events.ts

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

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

66
/**
@@ -18,6 +18,8 @@ export enum RooCodeEventName {
1818
TaskFocused = "taskFocused",
1919
TaskUnfocused = "taskUnfocused",
2020
TaskActive = "taskActive",
21+
TaskInteractive = "taskInteractive",
22+
TaskResumable = "taskResumable",
2123
TaskIdle = "taskIdle",
2224

2325
// Subtask Lifecycle
@@ -29,7 +31,6 @@ export enum RooCodeEventName {
2931
Message = "message",
3032
TaskModeSwitched = "taskModeSwitched",
3133
TaskAskResponded = "taskAskResponded",
32-
TaskAskRequiresInteraction = "taskAskRequiresInteraction",
3334

3435
// Task Analytics
3536
TaskTokenUsageUpdated = "taskTokenUsageUpdated",
@@ -60,6 +61,8 @@ export const rooCodeEventsSchema = z.object({
6061
[RooCodeEventName.TaskFocused]: z.tuple([z.string()]),
6162
[RooCodeEventName.TaskUnfocused]: z.tuple([z.string()]),
6263
[RooCodeEventName.TaskActive]: z.tuple([z.string()]),
64+
[RooCodeEventName.TaskInteractive]: z.tuple([z.string(), clineAskSchema, z.number()]),
65+
[RooCodeEventName.TaskResumable]: z.tuple([z.string()]),
6366
[RooCodeEventName.TaskIdle]: z.tuple([z.string()]),
6467

6568
[RooCodeEventName.TaskPaused]: z.tuple([z.string()]),
@@ -75,7 +78,6 @@ export const rooCodeEventsSchema = z.object({
7578
]),
7679
[RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]),
7780
[RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]),
78-
[RooCodeEventName.TaskAskRequiresInteraction]: z.tuple([z.string(), clineMessageSchema]),
7981

8082
[RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]),
8183
[RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]),
@@ -126,6 +128,16 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [
126128
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskActive],
127129
taskId: z.number().optional(),
128130
}),
131+
z.object({
132+
eventName: z.literal(RooCodeEventName.TaskInteractive),
133+
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskInteractive],
134+
taskId: z.number().optional(),
135+
}),
136+
z.object({
137+
eventName: z.literal(RooCodeEventName.TaskResumable),
138+
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskResumable],
139+
taskId: z.number().optional(),
140+
}),
129141
z.object({
130142
eventName: z.literal(RooCodeEventName.TaskIdle),
131143
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskIdle],
@@ -165,11 +177,6 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [
165177
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskResponded],
166178
taskId: z.number().optional(),
167179
}),
168-
z.object({
169-
eventName: z.literal(RooCodeEventName.TaskAskRequiresInteraction),
170-
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskRequiresInteraction],
171-
taskId: z.number().optional(),
172-
}),
173180

174181
// Task Analytics
175182
z.object({

packages/types/src/message.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,32 @@ export type ClineAsk = z.infer<typeof clineAskSchema>
5050
* Asks that put the task into an "idle" state.
5151
*/
5252

53-
export const idleAsks: ClineAsk[] = [
53+
export const idleAsks = [
5454
"api_req_failed",
55+
"auto_approval_max_req_reached",
5556
"mistake_limit_reached",
57+
"command_output", // Not sure about this one.
5658
"completion_result",
57-
"resume_task",
58-
"resume_completed_task",
59-
"command_output",
60-
"auto_approval_max_req_reached",
61-
] as const
59+
] as const satisfies readonly ClineAsk[]
6260

6361
export type IdleAsk = (typeof idleAsks)[number]
6462

6563
export function isIdleAsk(ask: ClineAsk): ask is IdleAsk {
66-
return idleAsks.includes(ask)
64+
return (idleAsks as readonly ClineAsk[]).includes(ask)
65+
}
66+
67+
/**
68+
* IdleAsk
69+
*
70+
* Asks that put the task into an "resumable" state.
71+
*/
72+
73+
export const resumableAsks = ["resume_task", "resume_completed_task"] as const satisfies readonly ClineAsk[]
74+
75+
export type ResumableAsk = (typeof resumableAsks)[number]
76+
77+
export function isResumableAsk(ask: ClineAsk): ask is ResumableAsk {
78+
return (resumableAsks as readonly ClineAsk[]).includes(ask)
6779
}
6880

6981
/**
@@ -72,12 +84,12 @@ export function isIdleAsk(ask: ClineAsk): ask is IdleAsk {
7284
* Asks that put the task into an "user interaction required" state.
7385
*/
7486

75-
export const interactiveAsks: ClineAsk[] = ["command"] as const
87+
export const interactiveAsks = ["command"] as const satisfies readonly ClineAsk[]
7688

7789
export type InteractiveAsk = (typeof interactiveAsks)[number]
7890

7991
export function isInteractiveAsk(ask: ClineAsk): ask is InteractiveAsk {
80-
return interactiveAsks.includes(ask)
92+
return (interactiveAsks as readonly ClineAsk[]).includes(ask)
8193
}
8294

8395
/**

packages/types/src/task.ts

Lines changed: 19 additions & 7 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 IdleAsk, type TokenUsage } from "./message.js"
4+
import { type ClineMessage, type ClineAsk, type TokenUsage } from "./message.js"
55
import { type ToolUsage, type ToolName } from "./tool.js"
66
import type { StaticAppProperties, GitProperties, TelemetryProperties } from "./telemetry.js"
77

@@ -53,15 +53,24 @@ export type TaskProviderEvents = {
5353
[RooCodeEventName.TaskFocused]: [taskId: string]
5454
[RooCodeEventName.TaskUnfocused]: [taskId: string]
5555
[RooCodeEventName.TaskActive]: [taskId: string]
56+
[RooCodeEventName.TaskInteractive]: [taskId: string, ask: ClineAsk, askTs: number]
57+
[RooCodeEventName.TaskResumable]: [taskId: string]
5658
[RooCodeEventName.TaskIdle]: [taskId: string]
5759
}
5860

5961
/**
6062
* TaskLike
6163
*/
6264

65+
export enum TaskStatus {
66+
Running = "running",
67+
Interactive = "interactive",
68+
Resumable = "resumable",
69+
Idle = "idle",
70+
None = "none",
71+
}
72+
6373
export const taskMetadataSchema = z.object({
64-
taskId: z.string(),
6574
task: z.string().optional(),
6675
images: z.array(z.string()).optional(),
6776
})
@@ -70,10 +79,12 @@ export type TaskMetadata = z.infer<typeof taskMetadataSchema>
7079

7180
export interface TaskLike {
7281
readonly taskId: string
73-
readonly rootTask?: TaskLike
74-
readonly blockingAsk?: IdleAsk
82+
readonly taskStatus: TaskStatus
83+
readonly taskAsk: ClineAsk | undefined
7584
readonly metadata: TaskMetadata
7685

86+
readonly rootTask?: TaskLike
87+
7788
on<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
7889
off<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
7990

@@ -90,18 +101,19 @@ export type TaskEvents = {
90101
[RooCodeEventName.TaskFocused]: []
91102
[RooCodeEventName.TaskUnfocused]: []
92103
[RooCodeEventName.TaskActive]: [taskId: string]
104+
[RooCodeEventName.TaskInteractive]: [taskId: string, ask: ClineAsk, askTs: number]
105+
[RooCodeEventName.TaskResumable]: [taskId: string]
93106
[RooCodeEventName.TaskIdle]: [taskId: string]
94107

95108
// Subtask Lifecycle
96-
[RooCodeEventName.TaskPaused]: []
97-
[RooCodeEventName.TaskUnpaused]: []
109+
[RooCodeEventName.TaskPaused]: [taskId: string]
110+
[RooCodeEventName.TaskUnpaused]: [taskId: string]
98111
[RooCodeEventName.TaskSpawned]: [taskId: string]
99112

100113
// Task Execution
101114
[RooCodeEventName.Message]: [{ action: "created" | "updated"; message: ClineMessage }]
102115
[RooCodeEventName.TaskModeSwitched]: [taskId: string, mode: string]
103116
[RooCodeEventName.TaskAskResponded]: []
104-
[RooCodeEventName.TaskAskRequiresInteraction]: [taskId: string, askMessage: ClineMessage]
105117

106118
// Task Analytics
107119
[RooCodeEventName.TaskToolFailed]: [taskId: string, tool: ToolName, error: string]

src/core/task/Task.ts

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@ import {
2222
type ClineSay,
2323
type ClineAsk,
2424
type IdleAsk,
25+
type ResumableAsk,
2526
type InteractiveAsk,
2627
type ToolProgressStatus,
2728
type HistoryItem,
2829
RooCodeEventName,
2930
TelemetryEventName,
31+
TaskStatus,
3032
TodoItem,
3133
DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
3234
getApiProtocol,
3335
getModelId,
3436
isIdleAsk,
3537
isInteractiveAsk,
38+
isResumableAsk,
3639
} from "@roo-code/types"
3740
import { TelemetryService } from "@roo-code/telemetry"
3841
import { CloudService, ExtensionBridgeService } from "@roo-code/cloud"
@@ -184,8 +187,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
184187
providerRef: WeakRef<ClineProvider>
185188
private readonly globalStoragePath: string
186189
abort: boolean = false
190+
191+
// TaskStatus
187192
idleAsk?: IdleAsk
193+
resumableAsk?: ResumableAsk
188194
interactiveAsk?: InteractiveAsk
195+
189196
didFinishAbortingStream = false
190197
abandoned = false
191198
isInitialized = false
@@ -293,7 +300,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
293300
this.taskId = historyItem ? historyItem.id : crypto.randomUUID()
294301

295302
this.metadata = {
296-
taskId: this.taskId,
297303
task: historyItem ? historyItem.task : task,
298304
images: historyItem ? [] : images,
299305
}
@@ -500,6 +506,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
500506
if (this._taskMode === undefined) {
501507
throw new Error("Task mode accessed before initialization. Use getTaskMode() or wait for taskModeReady.")
502508
}
509+
503510
return this._taskMode
504511
}
505512

@@ -720,29 +727,39 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
720727
// block (via the `pWaitFor`).
721728
const isBlocking = !(this.askResponse !== undefined || this.lastMessageTs !== askTs)
722729
const isStateMutable = !partial && isBlocking
723-
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)
730+
let stateMutationTimeouts: NodeJS.Timeout[] = []
731+
732+
if (isStateMutable) {
733+
if (isInteractiveAsk(type)) {
734+
stateMutationTimeouts.push(
735+
setTimeout(() => {
736+
this.interactiveAsk = type
737+
this.emit(RooCodeEventName.TaskInteractive, this.taskId, type, askTs)
738+
}, 1_000),
739+
)
740+
} else if (isResumableAsk(type)) {
741+
stateMutationTimeouts.push(
742+
setTimeout(() => {
743+
this.resumableAsk = type
744+
this.emit(RooCodeEventName.TaskResumable, this.taskId)
745+
}, 1_000),
746+
)
747+
} else if (isIdleAsk(type)) {
748+
stateMutationTimeouts.push(
749+
setTimeout(() => {
750+
this.idleAsk = type
751+
this.emit(RooCodeEventName.TaskIdle, this.taskId)
752+
}, 1_000),
753+
)
754+
}
732755
}
733756

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)
742-
}
757+
console.log(
758+
`[Task#${this.taskId}] pWaitFor askResponse(${type}) -> blocking (isStateMutable = ${isStateMutable}, stateMutationTimeouts = ${stateMutationTimeouts.length})`,
759+
)
743760

744-
console.log(`[Task#${this.taskId}] pWaitFor askResponse(${type}) -> blocking`)
745761
await pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 })
762+
746763
console.log(`[Task#${this.taskId}] pWaitFor askResponse(${type}) -> unblocked (${this.askResponse})`)
747764

748765
if (this.lastMessageTs !== askTs) {
@@ -758,20 +775,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
758775
this.askResponseImages = undefined
759776

760777
// 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
778+
stateMutationTimeouts.forEach((timeout) => clearTimeout(timeout))
771779

772780
// Switch back to an active state.
773-
if (this.idleAsk || this.interactiveAsk) {
781+
if (this.idleAsk || this.resumableAsk || this.interactiveAsk) {
774782
this.idleAsk = undefined
783+
this.resumableAsk = undefined
775784
this.interactiveAsk = undefined
776785
this.emit(RooCodeEventName.TaskActive, this.taskId)
777786
}
@@ -1066,12 +1075,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
10661075
}
10671076

10681077
public async resumePausedTask(lastMessage: string) {
1069-
// Release this Cline instance from paused state.
10701078
this.isPaused = false
1071-
this.emit(RooCodeEventName.TaskUnpaused)
1079+
this.emit(RooCodeEventName.TaskUnpaused, this.taskId)
10721080

10731081
// Fake an answer from the subtask that it has completed running and
1074-
// this is the result of what it has done add the message to the chat
1082+
// this is the result of what it has done add the message to the chat
10751083
// history and to the webview ui.
10761084
try {
10771085
await this.say("subtask_result", lastMessage)
@@ -2556,4 +2564,24 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
25562564
public get cwd() {
25572565
return this.workspacePath
25582566
}
2567+
2568+
public get taskStatus(): TaskStatus {
2569+
if (this.interactiveAsk) {
2570+
return TaskStatus.Interactive
2571+
}
2572+
2573+
if (this.resumableAsk) {
2574+
return TaskStatus.Resumable
2575+
}
2576+
2577+
if (this.idleAsk) {
2578+
return TaskStatus.Idle
2579+
}
2580+
2581+
return TaskStatus.Running
2582+
}
2583+
2584+
public get taskAsk(): ClineAsk | undefined {
2585+
return this.idleAsk || this.resumableAsk || this.interactiveAsk
2586+
}
25592587
}

src/core/tools/newTaskTool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export async function newTaskTool(
103103
// Set the isPaused flag to true so the parent
104104
// task can wait for the sub-task to finish.
105105
cline.isPaused = true
106-
cline.emit(RooCodeEventName.TaskPaused)
106+
cline.emit(RooCodeEventName.TaskPaused, cline.taskId)
107107

108108
return
109109
}

0 commit comments

Comments
 (0)