Skip to content

Commit a949b82

Browse files
committed
chore: enforce no-floating-promises in core/task/
Next slice of the no-floating-promises ratchet — widens the rule's scope in eslint.config.mjs to core/task/**. core/task/ had 25 un-awaited promises. Task.ts (19): all genuine fire-and-forget, marked void — task-loop kickoffs in the constructor and start(), UI posts in synchronous/streaming/setTimeout paths, the explicitly-background checkpoint init, and 9 presentAssistantMessage calls (a self-locking streaming presenter designed for fire-and-forget). void preserves current behavior, and the file already uses void this.updateClineMessage(...) elsewhere. Task.spec.ts (6): un-awaited task.submitUserMessage(...) calls in async tests, now awaited.
1 parent 17dc763 commit a949b82

3 files changed

Lines changed: 26 additions & 26 deletions

File tree

src/core/task/Task.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
524524
this.messageQueueStateChangedHandler = () => {
525525
this.emit(RooCodeEventName.TaskUserMessage, this.taskId)
526526
this.emit(RooCodeEventName.QueuedMessagesUpdated, this.taskId, this.messageQueueService.messages)
527-
this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
527+
void this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
528528
}
529529

530530
this.messageQueueService.on("stateChanged", this.messageQueueStateChangedHandler)
@@ -569,9 +569,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
569569
if (startTask) {
570570
this._started = true
571571
if (task || images) {
572-
this.startTask(task, images)
572+
void this.startTask(task, images)
573573
} else if (historyItem) {
574-
this.resumeTaskFromHistory()
574+
void this.resumeTaskFromHistory()
575575
} else {
576576
throw new Error("Either historyItem or task/images must be provided")
577577
}
@@ -1153,7 +1153,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11531153
// data or one whole message at a time so ignore partial for
11541154
// saves, and only post parts of partial message instead of
11551155
// whole array in new listener.
1156-
this.updateClineMessage(lastMessage)
1156+
void this.updateClineMessage(lastMessage)
11571157
// console.log("Task#ask: current ask promise was ignored (#1)")
11581158
throw new AskIgnoredError("updating existing partial")
11591159
} else {
@@ -1191,7 +1191,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11911191
lastMessage.progressStatus = progressStatus
11921192
lastMessage.isProtected = isProtected
11931193
await this.saveClineMessages()
1194-
this.updateClineMessage(lastMessage)
1194+
void this.updateClineMessage(lastMessage)
11951195
} else {
11961196
// This is a new and complete message, so add it like normal.
11971197
this.askResponse = undefined
@@ -1253,7 +1253,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
12531253
if (message) {
12541254
this.interactiveAsk = message
12551255
this.emit(RooCodeEventName.TaskInteractive, this.taskId)
1256-
provider?.postMessageToWebview({ type: "interactionRequired" })
1256+
void provider?.postMessageToWebview({ type: "interactionRequired" })
12571257
}
12581258
}, statusMutationTimeout),
12591259
)
@@ -1636,7 +1636,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
16361636
lastMessage.images = images
16371637
lastMessage.partial = partial
16381638
lastMessage.progressStatus = progressStatus
1639-
this.updateClineMessage(lastMessage)
1639+
void this.updateClineMessage(lastMessage)
16401640
} else {
16411641
// This is a new partial message, so add it with partial state.
16421642
const sayTs = Date.now()
@@ -1675,7 +1675,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
16751675
await this.saveClineMessages()
16761676

16771677
// More performant than an entire `postStateToWebview`.
1678-
this.updateClineMessage(lastMessage)
1678+
void this.updateClineMessage(lastMessage)
16791679
} else {
16801680
// This is a new and complete message, so add it like normal.
16811681
const sayTs = Date.now()
@@ -1784,7 +1784,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
17841784
const { task, images } = this.metadata
17851785

17861786
if (task || images) {
1787-
this.startTask(task ?? undefined, images ?? undefined)
1787+
void this.startTask(task ?? undefined, images ?? undefined)
17881788
}
17891789
}
17901790

@@ -2325,7 +2325,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
23252325

23262326
private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise<void> {
23272327
// Kicks off the checkpoints initialization process in the background.
2328-
getCheckpointService(this)
2328+
void getCheckpointService(this)
23292329

23302330
let nextUserContent = userContent
23312331
let includeFileDetails = true
@@ -2768,7 +2768,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
27682768
// Add to content and present
27692769
this.assistantMessageContent.push(partialToolUse)
27702770
this.userMessageContentReady = false
2771-
presentAssistantMessage(this)
2771+
void presentAssistantMessage(this)
27722772
} else if (event.type === "tool_call_delta") {
27732773
// Process chunk using streaming JSON parser
27742774
const partialToolUse = NativeToolCallParser.processStreamingChunk(
@@ -2787,7 +2787,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
27872787
this.assistantMessageContent[toolUseIndex] = partialToolUse
27882788

27892789
// Present updated tool use
2790-
presentAssistantMessage(this)
2790+
void presentAssistantMessage(this)
27912791
}
27922792
}
27932793
} else if (event.type === "tool_call_end") {
@@ -2813,7 +2813,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28132813
this.userMessageContentReady = false
28142814

28152815
// Present the finalized tool call
2816-
presentAssistantMessage(this)
2816+
void presentAssistantMessage(this)
28172817
} else if (toolUseIndex !== undefined) {
28182818
// finalizeStreamingToolCall returned null (malformed JSON or missing args)
28192819
// Mark the tool as non-partial so it's presented as complete, but execution
@@ -2832,7 +2832,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28322832
this.userMessageContentReady = false
28332833

28342834
// Present the tool call - validation will handle missing params
2835-
presentAssistantMessage(this)
2835+
void presentAssistantMessage(this)
28362836
}
28372837
}
28382838
}
@@ -2865,7 +2865,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28652865

28662866
// Present the tool call to user - presentAssistantMessage will execute
28672867
// tools sequentially and accumulate all results in userMessageContent
2868-
presentAssistantMessage(this)
2868+
void presentAssistantMessage(this)
28692869
break
28702870
}
28712871
case "text": {
@@ -2884,7 +2884,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28842884
})
28852885
this.userMessageContentReady = false
28862886
}
2887-
presentAssistantMessage(this)
2887+
void presentAssistantMessage(this)
28882888
break
28892889
}
28902890
}
@@ -3232,7 +3232,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32323232
this.userMessageContentReady = false
32333233

32343234
// Present the finalized tool call
3235-
presentAssistantMessage(this)
3235+
void presentAssistantMessage(this)
32363236
} else if (toolUseIndex !== undefined) {
32373237
// finalizeStreamingToolCall returned null (malformed JSON or missing args)
32383238
// We still need to mark the tool as non-partial so it gets executed
@@ -3251,7 +3251,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32513251
this.userMessageContentReady = false
32523252

32533253
// Present the tool call - validation will handle missing params
3254-
presentAssistantMessage(this)
3254+
void presentAssistantMessage(this)
32553255
}
32563256
}
32573257
}
@@ -3450,7 +3450,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
34503450
// If there is content to update then it will complete and
34513451
// update `this.userMessageContentReady` to true, which we
34523452
// `pWaitFor` before making the next request.
3453-
presentAssistantMessage(this)
3453+
void presentAssistantMessage(this)
34543454
}
34553455

34563456
if (hasTextContent || hasToolUses) {

src/core/task/__tests__/Task.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,7 +1391,7 @@ describe("Cline", () => {
13911391
]
13921392

13931393
// Call submitUserMessage
1394-
task.submitUserMessage("test message", ["image1.png"])
1394+
await task.submitUserMessage("test message", ["image1.png"])
13951395

13961396
// Verify handleWebviewAskResponse was called directly (not webview)
13971397
expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "test message", ["image1.png"])
@@ -1411,13 +1411,13 @@ describe("Cline", () => {
14111411
const handleResponseSpy = vi.spyOn(task, "handleWebviewAskResponse")
14121412

14131413
// Call with empty text and no images
1414-
task.submitUserMessage("", [])
1414+
await task.submitUserMessage("", [])
14151415

14161416
// Should not call handleWebviewAskResponse for empty messages
14171417
expect(handleResponseSpy).not.toHaveBeenCalled()
14181418

14191419
// Call with whitespace only
1420-
task.submitUserMessage(" ", [])
1420+
await task.submitUserMessage(" ", [])
14211421
expect(handleResponseSpy).not.toHaveBeenCalled()
14221422
})
14231423

@@ -1434,7 +1434,7 @@ describe("Cline", () => {
14341434

14351435
// Test with no messages (new task scenario)
14361436
task.clineMessages = []
1437-
task.submitUserMessage("new task", ["image1.png"])
1437+
await task.submitUserMessage("new task", ["image1.png"])
14381438

14391439
expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "new task", ["image1.png"])
14401440

@@ -1450,7 +1450,7 @@ describe("Cline", () => {
14501450
text: "Initial message",
14511451
},
14521452
]
1453-
task.submitUserMessage("follow-up message", ["image2.png"])
1453+
await task.submitUserMessage("follow-up message", ["image2.png"])
14541454

14551455
expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "follow-up message", ["image2.png"])
14561456
})
@@ -1477,7 +1477,7 @@ describe("Cline", () => {
14771477
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
14781478

14791479
// Should log error but not throw
1480-
task.submitUserMessage("test message")
1480+
await task.submitUserMessage("test message")
14811481

14821482
expect(consoleErrorSpy).toHaveBeenCalledWith("[Task#submitUserMessage] Provider reference lost")
14831483
expect(handleResponseSpy).not.toHaveBeenCalled()

src/eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default [
3232
{
3333
// Ratchet: enforce no-floating-promises directory by directory. Each
3434
// directory is added here once its floating promises are resolved.
35-
files: ["activate/**/*.ts"],
35+
files: ["activate/**/*.ts", "core/task/**/*.ts"],
3636
languageOptions: {
3737
parserOptions: {
3838
project: true,

0 commit comments

Comments
 (0)