|
|
@@ -164,7 +164,7 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
consecutiveMistakeCount: number = 0
|
|
|
consecutiveMistakeLimit: number
|
|
|
consecutiveMistakeCountForApplyDiff: Map<string, number> = new Map()
|
|
|
- private toolUsage: ToolUsage = {}
|
|
|
+ toolUsage: ToolUsage = {}
|
|
|
|
|
|
// Checkpoints
|
|
|
enableCheckpoints: boolean
|
|
|
@@ -488,6 +488,9 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
partial?: boolean,
|
|
|
checkpoint?: Record<string, unknown>,
|
|
|
progressStatus?: ToolProgressStatus,
|
|
|
+ options: {
|
|
|
+ isNonInteractive?: boolean
|
|
|
+ } = {},
|
|
|
): Promise<undefined> {
|
|
|
if (this.abort) {
|
|
|
throw new Error(`[Cline#say] task ${this.taskId}.${this.instanceId} aborted`)
|
|
|
@@ -495,49 +498,71 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
|
|
|
if (partial !== undefined) {
|
|
|
const lastMessage = this.clineMessages.at(-1)
|
|
|
+
|
|
|
const isUpdatingPreviousPartial =
|
|
|
lastMessage && lastMessage.partial && lastMessage.type === "say" && lastMessage.say === type
|
|
|
+
|
|
|
if (partial) {
|
|
|
if (isUpdatingPreviousPartial) {
|
|
|
- // existing partial message, so update it
|
|
|
+ // Existing partial message, so update it.
|
|
|
lastMessage.text = text
|
|
|
lastMessage.images = images
|
|
|
lastMessage.partial = partial
|
|
|
lastMessage.progressStatus = progressStatus
|
|
|
this.updateClineMessage(lastMessage)
|
|
|
} else {
|
|
|
- // this is a new partial message, so add it with partial state
|
|
|
+ // This is a new partial message, so add it with partial state.
|
|
|
const sayTs = Date.now()
|
|
|
- this.lastMessageTs = sayTs
|
|
|
+
|
|
|
+ if (!options.isNonInteractive) {
|
|
|
+ this.lastMessageTs = sayTs
|
|
|
+ }
|
|
|
+
|
|
|
await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images, partial })
|
|
|
}
|
|
|
} else {
|
|
|
// New now have a complete version of a previously partial message.
|
|
|
+ // This is the complete version of a previously partial
|
|
|
+ // message, so replace the partial with the complete version.
|
|
|
if (isUpdatingPreviousPartial) {
|
|
|
- // This is the complete version of a previously partial
|
|
|
- // message, so replace the partial with the complete version.
|
|
|
- this.lastMessageTs = lastMessage.ts
|
|
|
- // lastMessage.ts = sayTs
|
|
|
+ if (!options.isNonInteractive) {
|
|
|
+ this.lastMessageTs = lastMessage.ts
|
|
|
+ }
|
|
|
+
|
|
|
lastMessage.text = text
|
|
|
lastMessage.images = images
|
|
|
lastMessage.partial = false
|
|
|
lastMessage.progressStatus = progressStatus
|
|
|
+
|
|
|
// Instead of streaming partialMessage events, we do a save
|
|
|
// and post like normal to persist to disk.
|
|
|
await this.saveClineMessages()
|
|
|
- // More performant than an entire postStateToWebview.
|
|
|
+
|
|
|
+ // More performant than an entire `postStateToWebview`.
|
|
|
this.updateClineMessage(lastMessage)
|
|
|
} else {
|
|
|
// This is a new and complete message, so add it like normal.
|
|
|
const sayTs = Date.now()
|
|
|
- this.lastMessageTs = sayTs
|
|
|
+
|
|
|
+ if (!options.isNonInteractive) {
|
|
|
+ this.lastMessageTs = sayTs
|
|
|
+ }
|
|
|
+
|
|
|
await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images })
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- // this is a new non-partial message, so add it like normal
|
|
|
+ // This is a new non-partial message, so add it like normal.
|
|
|
const sayTs = Date.now()
|
|
|
- this.lastMessageTs = sayTs
|
|
|
+
|
|
|
+ // A "non-interactive" message is a message is one that the user
|
|
|
+ // does not need to respond to. We don't want these message types
|
|
|
+ // to trigger an update to `lastMessageTs` since they can be created
|
|
|
+ // asynchronously and could interrupt a pending ask.
|
|
|
+ if (!options.isNonInteractive) {
|
|
|
+ this.lastMessageTs = sayTs
|
|
|
+ }
|
|
|
+
|
|
|
await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images, checkpoint })
|
|
|
}
|
|
|
}
|
|
|
@@ -555,8 +580,12 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
// Start / Abort / Resume
|
|
|
|
|
|
private async startTask(task?: string, images?: string[]): Promise<void> {
|
|
|
- // conversationHistory (for API) and clineMessages (for webview) need to be in sync
|
|
|
- // if the extension process were killed, then on restart the clineMessages might not be empty, so we need to set it to [] when we create a new Cline client (otherwise webview would show stale messages from previous session)
|
|
|
+ // `conversationHistory` (for API) and `clineMessages` (for webview)
|
|
|
+ // need to be in sync.
|
|
|
+ // If the extension process were killed, then on restart the
|
|
|
+ // `clineMessages` might not be empty, so we need to set it to [] when
|
|
|
+ // we create a new Cline client (otherwise webview would show stale
|
|
|
+ // messages from previous session).
|
|
|
this.clineMessages = []
|
|
|
this.apiConversationHistory = []
|
|
|
await this.providerRef.deref()?.postStateToWebview()
|
|
|
@@ -578,28 +607,25 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
}
|
|
|
|
|
|
public async resumePausedTask(lastMessage: string) {
|
|
|
- // release this Cline instance from paused state
|
|
|
+ // Release this Cline instance from paused state.
|
|
|
this.isPaused = false
|
|
|
this.emit("taskUnpaused")
|
|
|
|
|
|
- // fake an answer from the subtask that it has completed running and this is the result of what it has done
|
|
|
- // add the message to the chat history and to the webview ui
|
|
|
+ // Fake an answer from the subtask that it has completed running and
|
|
|
+ // this is the result of what it has done add the message to the chat
|
|
|
+ // history and to the webview ui.
|
|
|
try {
|
|
|
await this.say("subtask_result", lastMessage)
|
|
|
|
|
|
await this.addToApiConversationHistory({
|
|
|
role: "user",
|
|
|
- content: [
|
|
|
- {
|
|
|
- type: "text",
|
|
|
- text: `[new_task completed] Result: ${lastMessage}`,
|
|
|
- },
|
|
|
- ],
|
|
|
+ content: [{ type: "text", text: `[new_task completed] Result: ${lastMessage}` }],
|
|
|
})
|
|
|
} catch (error) {
|
|
|
this.providerRef
|
|
|
.deref()
|
|
|
?.log(`Error failed to add reply from subtast into conversation of parent task, error: ${error}`)
|
|
|
+
|
|
|
throw error
|
|
|
}
|
|
|
}
|
|
|
@@ -1629,17 +1655,9 @@ export class Task extends EventEmitter<ClineEvents> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public getToolUsage() {
|
|
|
- return this.toolUsage
|
|
|
- }
|
|
|
-
|
|
|
// Getters
|
|
|
|
|
|
public get cwd() {
|
|
|
return this.workspacePath
|
|
|
}
|
|
|
-
|
|
|
- public getFileContextTracker(): FileContextTracker {
|
|
|
- return this.fileContextTracker
|
|
|
- }
|
|
|
}
|