Skip to content

Commit 58cbc69

Browse files
committed
refactor: centralize abort/streaming state reset logic and use safe JSON parsing
- Extract duplicated abort/streaming state reset logic into private helper method resetAbortAndStreamingState() - Update resumeTaskFromHistory() and presentResumableAsk() to use the helper method - Replace unsafe JSON.parse() with safeJsonParse() in ClineProvider.ts for better error handling Addresses review feedback from @mrubens on PR #8986
1 parent ac57bd0 commit 58cbc69

File tree

2 files changed

+35
-38
lines changed

2 files changed

+35
-38
lines changed

src/core/task/Task.ts

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,24 +1353,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
13531353
// (abandoned = false), we keep the instance alive but set abort = true.
13541354
// We only clear these flags after the user confirms they want to resume,
13551355
// preventing the old stream from continuing if abort was set.
1356-
this.abort = false
1357-
this.abandoned = false
1358-
this.abortReason = undefined
1359-
this.didFinishAbortingStream = false
1360-
this.isStreaming = false
1361-
1362-
// Reset streaming-local fields to avoid stale state from previous stream
1363-
this.currentStreamingContentIndex = 0
1364-
this.currentStreamingDidCheckpoint = false
1365-
this.assistantMessageContent = []
1366-
this.didCompleteReadingStream = false
1367-
this.userMessageContent = []
1368-
this.userMessageContentReady = false
1369-
this.didRejectTool = false
1370-
this.didAlreadyUseTool = false
1371-
this.presentAssistantMessageLocked = false
1372-
this.presentAssistantMessageHasPendingUpdates = false
1373-
this.assistantMessageParser.reset()
1356+
this.resetAbortAndStreamingState()
13741357

13751358
let responseText: string | undefined
13761359
let responseImages: string[] | undefined
@@ -1550,6 +1533,33 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
15501533
await this.initiateTaskLoop(newUserContent)
15511534
}
15521535

1536+
/**
1537+
* Resets abort flags and streaming state to allow task resumption.
1538+
* Centralizes the state reset logic used after user confirms task resumption.
1539+
*
1540+
* @private
1541+
*/
1542+
private resetAbortAndStreamingState(): void {
1543+
this.abort = false
1544+
this.abandoned = false
1545+
this.abortReason = undefined
1546+
this.didFinishAbortingStream = false
1547+
this.isStreaming = false
1548+
1549+
// Reset streaming-local fields to avoid stale state from previous stream
1550+
this.currentStreamingContentIndex = 0
1551+
this.currentStreamingDidCheckpoint = false
1552+
this.assistantMessageContent = []
1553+
this.didCompleteReadingStream = false
1554+
this.userMessageContent = []
1555+
this.userMessageContentReady = false
1556+
this.didRejectTool = false
1557+
this.didAlreadyUseTool = false
1558+
this.presentAssistantMessageLocked = false
1559+
this.presentAssistantMessageHasPendingUpdates = false
1560+
this.assistantMessageParser.reset()
1561+
}
1562+
15531563
/**
15541564
* Present a resumable ask on an aborted task without rehydrating.
15551565
* Used by soft-interrupt (cancelTask) to show Resume/Terminate UI.
@@ -1574,24 +1584,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
15741584
// If user clicked Resume (not Terminate), reset abort flags and continue
15751585
if (response === "yesButtonClicked" || response === "messageResponse") {
15761586
// Reset abort flags to allow the loop to continue
1577-
this.abort = false
1578-
this.abandoned = false
1579-
this.abortReason = undefined
1580-
this.didFinishAbortingStream = false
1581-
this.isStreaming = false
1582-
1583-
// Reset streaming-local fields to avoid stale state from previous stream
1584-
this.currentStreamingContentIndex = 0
1585-
this.currentStreamingDidCheckpoint = false
1586-
this.assistantMessageContent = []
1587-
this.didCompleteReadingStream = false
1588-
this.userMessageContent = []
1589-
this.userMessageContentReady = false
1590-
this.didRejectTool = false
1591-
this.didAlreadyUseTool = false
1592-
this.presentAssistantMessageLocked = false
1593-
this.presentAssistantMessageHasPendingUpdates = false
1594-
this.assistantMessageParser.reset()
1587+
this.resetAbortAndStreamingState()
15951588

15961589
// Prepare content for resuming the task loop
15971590
let userContent: Anthropic.Messages.ContentBlockParam[] = []

src/core/webview/ClineProvider.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { Package } from "../../shared/package"
5050
import { findLast } from "../../shared/array"
5151
import { supportPrompt } from "../../shared/support-prompt"
5252
import { GlobalFileNames } from "../../shared/globalFileNames"
53+
import { safeJsonParse } from "../../shared/safeJsonParse"
5354
import type { ExtensionMessage, ExtensionState, MarketplaceInstalledMetadata } from "../../shared/ExtensionMessage"
5455
import { Mode, defaultModeSlug, getModeBySlug } from "../../shared/modes"
5556
import { experimentDefault } from "../../shared/experiments"
@@ -2665,9 +2666,12 @@ export class ClineProvider
26652666

26662667
if (lastApiReqStartedIndex !== -1) {
26672668
const lastApiReqStarted = task.clineMessages[lastApiReqStartedIndex]
2668-
const apiReqInfo = JSON.parse(lastApiReqStarted.text || "{}")
2669+
const apiReqInfo = safeJsonParse<{ cost?: number; cancelReason?: string }>(
2670+
lastApiReqStarted.text || "{}",
2671+
{},
2672+
)
26692673

2670-
if (apiReqInfo.cost === undefined && apiReqInfo.cancelReason === undefined) {
2674+
if (apiReqInfo && apiReqInfo.cost === undefined && apiReqInfo.cancelReason === undefined) {
26712675
apiReqInfo.cancelReason = "user_cancelled"
26722676
lastApiReqStarted.text = JSON.stringify(apiReqInfo)
26732677
await task.overwriteClineMessages([...task.clineMessages])

0 commit comments

Comments
 (0)