mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add support for task page event population (#7117)
This commit is contained in:
parent
0d0bba2eb7
commit
44086e4a86
18 changed files with 276 additions and 167 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@roo-code/types",
|
||||
"version": "1.48.0",
|
||||
"version": "1.49.0",
|
||||
"description": "TypeScript type definitions for Roo Code.",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { z } from "zod"
|
|||
import { RooCodeEventName } from "./events.js"
|
||||
import { type ClineMessage, type BlockingAsk, type TokenUsage } from "./message.js"
|
||||
import { type ToolUsage, type ToolName } from "./tool.js"
|
||||
import type { StaticAppProperties, GitProperties, TelemetryProperties } from "./telemetry.js"
|
||||
|
||||
/**
|
||||
* TaskProviderLike
|
||||
|
|
@ -14,19 +15,23 @@ export interface TaskProviderState {
|
|||
|
||||
export interface TaskProviderLike {
|
||||
readonly cwd: string
|
||||
readonly appProperties: StaticAppProperties
|
||||
readonly gitProperties: GitProperties | undefined
|
||||
|
||||
getCurrentCline(): TaskLike | undefined
|
||||
getCurrentTask(): TaskLike | undefined
|
||||
getCurrentTaskStack(): string[]
|
||||
getRecentTasks(): string[]
|
||||
|
||||
initClineWithTask(text?: string, images?: string[], parentTask?: TaskLike): Promise<TaskLike>
|
||||
createTask(text?: string, images?: string[], parentTask?: TaskLike): Promise<TaskLike>
|
||||
cancelTask(): Promise<void>
|
||||
clearTask(): Promise<void>
|
||||
postStateToWebview(): Promise<void>
|
||||
|
||||
getState(): Promise<TaskProviderState>
|
||||
|
||||
postStateToWebview(): Promise<void>
|
||||
postMessageToWebview(message: unknown): Promise<void>
|
||||
|
||||
getTelemetryProperties(): Promise<TelemetryProperties>
|
||||
|
||||
on<K extends keyof TaskProviderEvents>(
|
||||
event: K,
|
||||
listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
|
||||
|
|
@ -36,14 +41,6 @@ export interface TaskProviderLike {
|
|||
event: K,
|
||||
listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
|
||||
): this
|
||||
|
||||
context: {
|
||||
extension?: {
|
||||
packageJSON?: {
|
||||
version?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type TaskProviderEvents = {
|
||||
|
|
|
|||
|
|
@ -72,17 +72,37 @@ export enum TelemetryEventName {
|
|||
* TelemetryProperties
|
||||
*/
|
||||
|
||||
export const appPropertiesSchema = z.object({
|
||||
export const staticAppPropertiesSchema = z.object({
|
||||
appName: z.string(),
|
||||
appVersion: z.string(),
|
||||
vscodeVersion: z.string(),
|
||||
platform: z.string(),
|
||||
editorName: z.string(),
|
||||
})
|
||||
|
||||
export type StaticAppProperties = z.infer<typeof staticAppPropertiesSchema>
|
||||
|
||||
export const dynamicAppPropertiesSchema = z.object({
|
||||
language: z.string(),
|
||||
mode: z.string(),
|
||||
})
|
||||
|
||||
export type DynamicAppProperties = z.infer<typeof dynamicAppPropertiesSchema>
|
||||
|
||||
export const cloudAppPropertiesSchema = z.object({
|
||||
cloudIsAuthenticated: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type CloudAppProperties = z.infer<typeof cloudAppPropertiesSchema>
|
||||
|
||||
export const appPropertiesSchema = z.object({
|
||||
...staticAppPropertiesSchema.shape,
|
||||
...dynamicAppPropertiesSchema.shape,
|
||||
...cloudAppPropertiesSchema.shape,
|
||||
})
|
||||
|
||||
export type AppProperties = z.infer<typeof appPropertiesSchema>
|
||||
|
||||
export const taskPropertiesSchema = z.object({
|
||||
taskId: z.string().optional(),
|
||||
apiProvider: z.enum(providerNames).optional(),
|
||||
|
|
@ -99,12 +119,16 @@ export const taskPropertiesSchema = z.object({
|
|||
.optional(),
|
||||
})
|
||||
|
||||
export type TaskProperties = z.infer<typeof taskPropertiesSchema>
|
||||
|
||||
export const gitPropertiesSchema = z.object({
|
||||
repositoryUrl: z.string().optional(),
|
||||
repositoryName: z.string().optional(),
|
||||
defaultBranch: z.string().optional(),
|
||||
})
|
||||
|
||||
export type GitProperties = z.infer<typeof gitPropertiesSchema>
|
||||
|
||||
export const telemetryPropertiesSchema = z.object({
|
||||
...appPropertiesSchema.shape,
|
||||
...taskPropertiesSchema.shape,
|
||||
|
|
@ -112,7 +136,6 @@ export const telemetryPropertiesSchema = z.object({
|
|||
})
|
||||
|
||||
export type TelemetryProperties = z.infer<typeof telemetryPropertiesSchema>
|
||||
export type GitProperties = z.infer<typeof gitPropertiesSchema>
|
||||
|
||||
/**
|
||||
* TelemetryEvent
|
||||
|
|
|
|||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
|
|
@ -584,8 +584,8 @@ importers:
|
|||
specifier: ^1.14.0
|
||||
version: 1.14.0(typescript@5.8.3)
|
||||
'@roo-code/cloud':
|
||||
specifier: ^0.14.0
|
||||
version: 0.14.0
|
||||
specifier: ^0.15.0
|
||||
version: 0.15.0
|
||||
'@roo-code/ipc':
|
||||
specifier: workspace:^
|
||||
version: link:../packages/ipc
|
||||
|
|
@ -3103,11 +3103,11 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@roo-code/cloud@0.14.0':
|
||||
resolution: {integrity: sha512-EKFw7sLSJcOpXQDjCMFL4EnE7OH3RhO00joteaysNYNpNe+js6kMFrBZY61rj1tZAXV/943RWeQptG4sSAfPFQ==}
|
||||
'@roo-code/cloud@0.15.0':
|
||||
resolution: {integrity: sha512-0DivOP5uUS9U6UKSxzoxZ4NxMCYUxA7wG72y3PBP91JhGzHaTqxi9WrvF61bo134dCnCktU9oTKIAD9AvFigzg==}
|
||||
|
||||
'@roo-code/types@1.48.0':
|
||||
resolution: {integrity: sha512-DJkA/310sLcs4vM4YLhJhc5A1JFYmdcOt+hRQMUPq+wF02yW9rc7ZZzHNDOOUZL+kk5naUd2nBRxLiJoh6o0Ng==}
|
||||
'@roo-code/types@1.49.0':
|
||||
resolution: {integrity: sha512-h7gbjfIxBN+fgFecQiZs3W+vjdUhZOvtjh4OqcpPGG1w8B1DlXPDV4L/+ARUeNzZxSOK5S5rNPy57jC35EaD/w==}
|
||||
|
||||
'@sec-ant/readable-stream@0.4.1':
|
||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||
|
|
@ -12305,9 +12305,9 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.40.2':
|
||||
optional: true
|
||||
|
||||
'@roo-code/cloud@0.14.0':
|
||||
'@roo-code/cloud@0.15.0':
|
||||
dependencies:
|
||||
'@roo-code/types': 1.48.0
|
||||
'@roo-code/types': 1.49.0
|
||||
ioredis: 5.6.1
|
||||
p-wait-for: 5.0.2
|
||||
socket.io-client: 4.8.1
|
||||
|
|
@ -12317,7 +12317,7 @@ snapshots:
|
|||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@roo-code/types@1.48.0':
|
||||
'@roo-code/types@1.49.0':
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ function startWatch(pkg: PackageConfig): WatcherResult {
|
|||
throw new Error(`Invalid watch command for ${pkg.name}`)
|
||||
}
|
||||
|
||||
console.log(`Watching for changes to ${pkg.sourcePath} with ${cmd} ${args.join(" ")}`)
|
||||
console.log(`👀 Watching for changes to ${pkg.sourcePath} with ${cmd} ${args.join(" ")}`)
|
||||
|
||||
const child = spawn(cmd, args, {
|
||||
cwd: path.resolve(__dirname, "..", pkg.sourcePath),
|
||||
|
|
@ -251,7 +251,7 @@ function startWatch(pkg: PackageConfig): WatcherResult {
|
|||
debounceTimer = setTimeout(() => {
|
||||
linkPackage(pkg)
|
||||
|
||||
console.log(`📋 Copied ${pkg.name} to ${pkg.targetPaths.length} paths\n`)
|
||||
console.log(`♻️ Copied ${pkg.name} to ${pkg.targetPaths.length} paths\n`)
|
||||
|
||||
debounceTimer = null
|
||||
}, DEBOUNCE_DELAY)
|
||||
|
|
|
|||
|
|
@ -1951,7 +1951,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
const history = await provider?.getTaskWithId(this.taskId)
|
||||
|
||||
if (history) {
|
||||
await provider?.initClineWithHistoryItem(history.historyItem)
|
||||
await provider?.createTaskWithHistoryItem(history.historyItem)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const mockAskApproval = vi.fn<AskApproval>()
|
|||
const mockHandleError = vi.fn<HandleError>()
|
||||
const mockPushToolResult = vi.fn()
|
||||
const mockRemoveClosingTag = vi.fn((_name: string, value: string | undefined) => value ?? "")
|
||||
const mockInitClineWithTask = vi.fn<() => Promise<MockClineInstance>>().mockResolvedValue({ taskId: "mock-subtask-id" })
|
||||
const mockCreateTask = vi.fn<() => Promise<MockClineInstance>>().mockResolvedValue({ taskId: "mock-subtask-id" })
|
||||
const mockEmit = vi.fn()
|
||||
const mockRecordToolError = vi.fn()
|
||||
const mockSayAndCreateMissingParamError = vi.fn()
|
||||
|
|
@ -40,7 +40,7 @@ const mockCline = {
|
|||
deref: vi.fn(() => ({
|
||||
getState: vi.fn(() => ({ customModes: [], mode: "ask" })),
|
||||
handleModeSwitch: vi.fn(),
|
||||
initClineWithTask: mockInitClineWithTask,
|
||||
createTask: mockCreateTask,
|
||||
})),
|
||||
},
|
||||
}
|
||||
|
|
@ -88,8 +88,8 @@ describe("newTaskTool", () => {
|
|||
// Verify askApproval was called
|
||||
expect(mockAskApproval).toHaveBeenCalled()
|
||||
|
||||
// Verify the message passed to initClineWithTask reflects the code's behavior in unit tests
|
||||
expect(mockInitClineWithTask).toHaveBeenCalledWith(
|
||||
// Verify the message passed to createTask reflects the code's behavior in unit tests
|
||||
expect(mockCreateTask).toHaveBeenCalledWith(
|
||||
"Review this: \\@file1.txt and also \\\\\\@file2.txt", // Unit Test Expectation: \\@ -> \@, \\\\@ -> \\\\@
|
||||
undefined,
|
||||
mockCline,
|
||||
|
|
@ -122,7 +122,7 @@ describe("newTaskTool", () => {
|
|||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockInitClineWithTask).toHaveBeenCalledWith(
|
||||
expect(mockCreateTask).toHaveBeenCalledWith(
|
||||
"This is already unescaped: \\@file1.txt", // Expected: \@ remains \@
|
||||
undefined,
|
||||
mockCline,
|
||||
|
|
@ -149,7 +149,7 @@ describe("newTaskTool", () => {
|
|||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockInitClineWithTask).toHaveBeenCalledWith(
|
||||
expect(mockCreateTask).toHaveBeenCalledWith(
|
||||
"A normal mention @file1.txt", // Expected: @ remains @
|
||||
undefined,
|
||||
mockCline,
|
||||
|
|
@ -176,7 +176,7 @@ describe("newTaskTool", () => {
|
|||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockInitClineWithTask).toHaveBeenCalledWith(
|
||||
expect(mockCreateTask).toHaveBeenCalledWith(
|
||||
"Mix: @file0.txt, \\@file1.txt, \\@file2.txt, \\\\\\@file3.txt", // Unit Test Expectation: @->@, \@->\@, \\@->\@, \\\\@->\\\\@
|
||||
undefined,
|
||||
mockCline,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ export async function newTaskTool(
|
|||
cline.pausedModeSlug = (await provider.getState()).mode ?? defaultModeSlug
|
||||
|
||||
// Create new task instance first (this preserves parent's current mode in its history)
|
||||
const newCline = await provider.initClineWithTask(unescapedMessage, undefined, cline)
|
||||
const newCline = await provider.createTask(unescapedMessage, undefined, cline)
|
||||
|
||||
if (!newCline) {
|
||||
pushToolResult(t("tools:newTask.errors.policy_restriction"))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ import {
|
|||
type ProviderSettings,
|
||||
type RooCodeSettings,
|
||||
type ProviderSettingsEntry,
|
||||
type StaticAppProperties,
|
||||
type DynamicAppProperties,
|
||||
type CloudAppProperties,
|
||||
type TaskProperties,
|
||||
type GitProperties,
|
||||
type TelemetryProperties,
|
||||
type TelemetryPropertiesProvider,
|
||||
type CodeActionId,
|
||||
|
|
@ -104,15 +109,14 @@ export class ClineProvider
|
|||
private codeIndexStatusSubscription?: vscode.Disposable
|
||||
private currentWorkspaceManager?: CodeIndexManager
|
||||
private _workspaceTracker?: WorkspaceTracker // workSpaceTracker read-only for access outside this class
|
||||
public get workspaceTracker(): WorkspaceTracker | undefined {
|
||||
return this._workspaceTracker
|
||||
}
|
||||
protected mcpHub?: McpHub // Change from private to protected
|
||||
private marketplaceManager: MarketplaceManager
|
||||
private mdmService?: MdmService
|
||||
private taskCreationCallback: (task: Task) => void
|
||||
private taskEventListeners: WeakMap<Task, Array<() => void>> = new WeakMap()
|
||||
|
||||
private recentTasksCache?: string[]
|
||||
|
||||
public isViewLaunched = false
|
||||
public settingsImportedAt?: number
|
||||
public readonly latestAnnouncementId = "jul-29-2025-3-25-0" // Update for v3.25.0 announcement
|
||||
|
|
@ -366,7 +370,7 @@ export class ClineProvider
|
|||
|
||||
// returns the current cline object in the stack (the top one)
|
||||
// if the stack is empty, returns undefined
|
||||
getCurrentCline(): Task | undefined {
|
||||
getCurrentTask(): Task | undefined {
|
||||
if (this.clineStack.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
|
@ -374,7 +378,7 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
// returns the current clineStack length (how many cline objects are in the stack)
|
||||
getClineStackSize(): number {
|
||||
getTaskStackSize(): number {
|
||||
return this.clineStack.length
|
||||
}
|
||||
|
||||
|
|
@ -390,7 +394,7 @@ export class ClineProvider
|
|||
// remove the last cline instance from the stack (this is the finished sub task)
|
||||
await this.removeClineFromStack()
|
||||
// resume the last cline instance in the stack (if it exists - this is the 'parent' calling task)
|
||||
await this.getCurrentCline()?.resumePausedTask(lastMessage)
|
||||
await this.getCurrentTask()?.resumePausedTask(lastMessage)
|
||||
}
|
||||
|
||||
// Clear the current task without treating it as a subtask
|
||||
|
|
@ -399,6 +403,51 @@ export class ClineProvider
|
|||
await this.removeClineFromStack()
|
||||
}
|
||||
|
||||
getRecentTasks(): string[] {
|
||||
if (this.recentTasksCache) {
|
||||
return this.recentTasksCache
|
||||
}
|
||||
|
||||
const history = this.getGlobalState("taskHistory") ?? []
|
||||
const workspaceTasks: HistoryItem[] = []
|
||||
|
||||
for (const item of history) {
|
||||
if (!item.ts || !item.task || item.workspace !== this.cwd) {
|
||||
continue
|
||||
}
|
||||
|
||||
workspaceTasks.push(item)
|
||||
}
|
||||
|
||||
if (workspaceTasks.length === 0) {
|
||||
this.recentTasksCache = []
|
||||
return this.recentTasksCache
|
||||
}
|
||||
|
||||
workspaceTasks.sort((a, b) => b.ts - a.ts)
|
||||
let recentTaskIds: string[] = []
|
||||
|
||||
if (workspaceTasks.length >= 100) {
|
||||
// If we have at least 100 tasks, return tasks from the last 7 days.
|
||||
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000
|
||||
|
||||
for (const item of workspaceTasks) {
|
||||
// Stop when we hit tasks older than 7 days.
|
||||
if (item.ts < sevenDaysAgo) {
|
||||
break
|
||||
}
|
||||
|
||||
recentTaskIds.push(item.id)
|
||||
}
|
||||
} else {
|
||||
// Otherwise, return the most recent 100 tasks (or all if less than 100).
|
||||
recentTaskIds = workspaceTasks.slice(0, Math.min(100, workspaceTasks.length)).map((item) => item.id)
|
||||
}
|
||||
|
||||
this.recentTasksCache = recentTaskIds
|
||||
return this.recentTasksCache
|
||||
}
|
||||
|
||||
/*
|
||||
VSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.
|
||||
- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/
|
||||
|
|
@ -489,7 +538,7 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
// Check if there is a cline instance in the stack (if this provider has an active task)
|
||||
if (visibleProvider.getCurrentCline()) {
|
||||
if (visibleProvider.getCurrentTask()) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -520,7 +569,7 @@ export class ClineProvider
|
|||
return
|
||||
}
|
||||
|
||||
await visibleProvider.initClineWithTask(prompt)
|
||||
await visibleProvider.createTask(prompt)
|
||||
}
|
||||
|
||||
public static async handleTerminalAction(
|
||||
|
|
@ -545,7 +594,7 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
try {
|
||||
await visibleProvider.initClineWithTask(prompt)
|
||||
await visibleProvider.createTask(prompt)
|
||||
} catch (error) {
|
||||
if (error instanceof OrganizationAllowListViolationError) {
|
||||
// Errors from terminal commands seem to get swallowed / ignored.
|
||||
|
|
@ -681,17 +730,13 @@ export class ClineProvider
|
|||
this.log("Webview view resolved")
|
||||
}
|
||||
|
||||
public async initClineWithSubTask(parent: Task, task?: string, images?: string[]) {
|
||||
return this.initClineWithTask(task, images, parent)
|
||||
}
|
||||
|
||||
// When initializing a new task, (not from history but from a tool command
|
||||
// new_task) there is no need to remove the previous task since the new
|
||||
// task is a subtask of the previous one, and when it finishes it is removed
|
||||
// from the stack and the caller is resumed in this way we can have a chain
|
||||
// of tasks, each one being a sub task of the previous one until the main
|
||||
// task is finished.
|
||||
public async initClineWithTask(
|
||||
public async createTask(
|
||||
text?: string,
|
||||
images?: string[],
|
||||
parentTask?: Task,
|
||||
|
|
@ -744,7 +789,7 @@ export class ClineProvider
|
|||
return task
|
||||
}
|
||||
|
||||
public async initClineWithHistoryItem(historyItem: HistoryItem & { rootTask?: Task; parentTask?: Task }) {
|
||||
public async createTaskWithHistoryItem(historyItem: HistoryItem & { rootTask?: Task; parentTask?: Task }) {
|
||||
await this.removeClineFromStack()
|
||||
|
||||
// If the history item has a saved mode, restore it and its associated API configuration
|
||||
|
|
@ -1020,7 +1065,7 @@ export class ClineProvider
|
|||
* @param newMode The mode to switch to
|
||||
*/
|
||||
public async handleModeSwitch(newMode: Mode) {
|
||||
const cline = this.getCurrentCline()
|
||||
const cline = this.getCurrentTask()
|
||||
|
||||
if (cline) {
|
||||
TelemetryService.instance.captureModeSwitch(cline.taskId, newMode)
|
||||
|
|
@ -1133,7 +1178,7 @@ export class ClineProvider
|
|||
|
||||
// Change the provider for the current task.
|
||||
// TODO: We should rename `buildApiHandler` for clarity (e.g. `getProviderClient`).
|
||||
const task = this.getCurrentCline()
|
||||
const task = this.getCurrentTask()
|
||||
|
||||
if (task) {
|
||||
task.api = buildApiHandler(providerSettings)
|
||||
|
|
@ -1194,7 +1239,7 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
// Change the provider for the current task.
|
||||
const task = this.getCurrentCline()
|
||||
const task = this.getCurrentTask()
|
||||
|
||||
if (task) {
|
||||
task.api = buildApiHandler(providerSettings)
|
||||
|
|
@ -1206,7 +1251,7 @@ export class ClineProvider
|
|||
// Task Management
|
||||
|
||||
async cancelTask() {
|
||||
const cline = this.getCurrentCline()
|
||||
const cline = this.getCurrentTask()
|
||||
|
||||
if (!cline) {
|
||||
return
|
||||
|
|
@ -1223,13 +1268,13 @@ export class ClineProvider
|
|||
|
||||
await pWaitFor(
|
||||
() =>
|
||||
this.getCurrentCline()! === undefined ||
|
||||
this.getCurrentCline()!.isStreaming === false ||
|
||||
this.getCurrentCline()!.didFinishAbortingStream ||
|
||||
this.getCurrentTask()! === undefined ||
|
||||
this.getCurrentTask()!.isStreaming === false ||
|
||||
this.getCurrentTask()!.didFinishAbortingStream ||
|
||||
// If only the first chunk is processed, then there's no
|
||||
// need to wait for graceful abort (closes edits, browser,
|
||||
// etc).
|
||||
this.getCurrentCline()!.isWaitingForFirstChunk,
|
||||
this.getCurrentTask()!.isWaitingForFirstChunk,
|
||||
{
|
||||
timeout: 3_000,
|
||||
},
|
||||
|
|
@ -1237,15 +1282,15 @@ export class ClineProvider
|
|||
console.error("Failed to abort task")
|
||||
})
|
||||
|
||||
if (this.getCurrentCline()) {
|
||||
if (this.getCurrentTask()) {
|
||||
// 'abandoned' will prevent this Cline instance from affecting
|
||||
// future Cline instances. This may happen if its hanging on a
|
||||
// streaming request.
|
||||
this.getCurrentCline()!.abandoned = true
|
||||
this.getCurrentTask()!.abandoned = true
|
||||
}
|
||||
|
||||
// Clears task again, so we need to abortTask manually above.
|
||||
await this.initClineWithHistoryItem({ ...historyItem, rootTask, parentTask })
|
||||
await this.createTaskWithHistoryItem({ ...historyItem, rootTask, parentTask })
|
||||
}
|
||||
|
||||
async updateCustomInstructions(instructions?: string) {
|
||||
|
|
@ -1403,10 +1448,10 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
async showTaskWithId(id: string) {
|
||||
if (id !== this.getCurrentCline()?.taskId) {
|
||||
if (id !== this.getCurrentTask()?.taskId) {
|
||||
// Non-current task.
|
||||
const { historyItem } = await this.getTaskWithId(id)
|
||||
await this.initClineWithHistoryItem(historyItem) // Clears existing task.
|
||||
await this.createTaskWithHistoryItem(historyItem) // Clears existing task.
|
||||
}
|
||||
|
||||
await this.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
|
|
@ -1440,7 +1485,7 @@ export class ClineProvider
|
|||
const { taskDirPath } = await this.getTaskWithId(id)
|
||||
|
||||
// remove task from stack if it's the current task
|
||||
if (id === this.getCurrentCline()?.taskId) {
|
||||
if (id === this.getCurrentTask()?.taskId) {
|
||||
// if we found the taskid to delete - call finish to abort this task and allow a new task to be started,
|
||||
// if we are deleting a subtask and parent task is still waiting for subtask to finish - it allows the parent to resume (this case should neve exist)
|
||||
await this.finishSubTask(t("common:tasks.deleted"))
|
||||
|
|
@ -1485,6 +1530,7 @@ export class ClineProvider
|
|||
const taskHistory = this.getGlobalState("taskHistory") ?? []
|
||||
const updatedTaskHistory = taskHistory.filter((task) => task.id !== id)
|
||||
await this.updateGlobalState("taskHistory", updatedTaskHistory)
|
||||
this.recentTasksCache = undefined
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
|
||||
|
|
@ -1726,10 +1772,10 @@ export class ClineProvider
|
|||
autoCondenseContext: autoCondenseContext ?? true,
|
||||
autoCondenseContextPercent: autoCondenseContextPercent ?? 100,
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
currentTaskItem: this.getCurrentCline()?.taskId
|
||||
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
|
||||
currentTaskItem: this.getCurrentTask()?.taskId
|
||||
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentTask()?.taskId)
|
||||
: undefined,
|
||||
clineMessages: this.getCurrentCline()?.clineMessages || [],
|
||||
clineMessages: this.getCurrentTask()?.clineMessages || [],
|
||||
taskHistory: (taskHistory || [])
|
||||
.filter((item: HistoryItem) => item.ts && item.task)
|
||||
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts),
|
||||
|
|
@ -2025,6 +2071,8 @@ export class ClineProvider
|
|||
}
|
||||
|
||||
await this.updateGlobalState("taskHistory", history)
|
||||
this.recentTasksCache = undefined
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
|
|
@ -2090,17 +2138,20 @@ export class ClineProvider
|
|||
console.log(message)
|
||||
}
|
||||
|
||||
// integration tests
|
||||
// getters
|
||||
|
||||
public get workspaceTracker(): WorkspaceTracker | undefined {
|
||||
return this._workspaceTracker
|
||||
}
|
||||
|
||||
get viewLaunched() {
|
||||
return this.isViewLaunched
|
||||
}
|
||||
|
||||
get messages() {
|
||||
return this.getCurrentCline()?.clineMessages || []
|
||||
return this.getCurrentTask()?.clineMessages || []
|
||||
}
|
||||
|
||||
// Add public getter
|
||||
public getMcpHub(): McpHub | undefined {
|
||||
return this.mcpHub
|
||||
}
|
||||
|
|
@ -2143,7 +2194,7 @@ export class ClineProvider
|
|||
)
|
||||
|
||||
if (isRemoteControlEnabled(userInfo, enabled)) {
|
||||
const currentTask = this.getCurrentCline()
|
||||
const currentTask = this.getCurrentTask()
|
||||
|
||||
if (currentTask && !currentTask.bridgeService) {
|
||||
try {
|
||||
|
|
@ -2176,18 +2227,29 @@ export class ClineProvider
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns properties to be included in every telemetry event
|
||||
* This method is called by the telemetry service to get context information
|
||||
* like the current mode, API provider, git repository information, etc.
|
||||
*/
|
||||
public async getTelemetryProperties(): Promise<TelemetryProperties> {
|
||||
const { mode, apiConfiguration, language } = await this.getState()
|
||||
const task = this.getCurrentCline()
|
||||
private _appProperties?: StaticAppProperties
|
||||
|
||||
const packageJSON = this.context.extension?.packageJSON
|
||||
private getAppProperties(): StaticAppProperties {
|
||||
if (!this._appProperties) {
|
||||
const packageJSON = this.context.extension?.packageJSON
|
||||
|
||||
// Get Roo Code Cloud authentication state
|
||||
this._appProperties = {
|
||||
appName: packageJSON?.name ?? Package.name,
|
||||
appVersion: packageJSON?.version ?? Package.version,
|
||||
vscodeVersion: vscode.version,
|
||||
platform: process.platform,
|
||||
editorName: vscode.env.appName,
|
||||
}
|
||||
}
|
||||
|
||||
return this._appProperties
|
||||
}
|
||||
|
||||
public get appProperties(): StaticAppProperties {
|
||||
return this._appProperties ?? this.getAppProperties()
|
||||
}
|
||||
|
||||
private getCloudProperties(): CloudAppProperties {
|
||||
let cloudIsAuthenticated: boolean | undefined
|
||||
|
||||
try {
|
||||
|
|
@ -2195,14 +2257,19 @@ export class ClineProvider
|
|||
cloudIsAuthenticated = CloudService.instance.isAuthenticated()
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently handle errors to avoid breaking telemetry collection
|
||||
// Silently handle errors to avoid breaking telemetry collection.
|
||||
this.log(`[getTelemetryProperties] Failed to get cloud auth state: ${error}`)
|
||||
}
|
||||
|
||||
// Get git repository information
|
||||
const gitInfo = await getWorkspaceGitInfo()
|
||||
return {
|
||||
cloudIsAuthenticated,
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate todo list statistics
|
||||
private async getTaskProperties(): Promise<DynamicAppProperties & TaskProperties> {
|
||||
const { language, mode, apiConfiguration } = await this.getState()
|
||||
|
||||
const task = this.getCurrentTask()
|
||||
const todoList = task?.todoList
|
||||
let todos: { total: number; completed: number; inProgress: number; pending: number } | undefined
|
||||
|
||||
|
|
@ -2215,22 +2282,38 @@ export class ClineProvider
|
|||
}
|
||||
}
|
||||
|
||||
// Return all properties including git info - clients will filter as needed
|
||||
return {
|
||||
appName: packageJSON?.name ?? Package.name,
|
||||
appVersion: packageJSON?.version ?? Package.version,
|
||||
vscodeVersion: vscode.version,
|
||||
platform: process.platform,
|
||||
editorName: vscode.env.appName,
|
||||
language,
|
||||
mode,
|
||||
taskId: task?.taskId,
|
||||
apiProvider: apiConfiguration?.apiProvider,
|
||||
modelId: task?.api?.getModel().id,
|
||||
diffStrategy: task?.diffStrategy?.getName(),
|
||||
isSubtask: task ? !!task.parentTask : undefined,
|
||||
cloudIsAuthenticated,
|
||||
...(todos && { todos }),
|
||||
...gitInfo,
|
||||
}
|
||||
}
|
||||
|
||||
private _gitProperties?: GitProperties
|
||||
|
||||
private async getGitProperties(): Promise<GitProperties> {
|
||||
if (!this._gitProperties) {
|
||||
this._gitProperties = await getWorkspaceGitInfo()
|
||||
}
|
||||
|
||||
return this._gitProperties
|
||||
}
|
||||
|
||||
public get gitProperties(): GitProperties | undefined {
|
||||
return this._gitProperties
|
||||
}
|
||||
|
||||
public async getTelemetryProperties(): Promise<TelemetryProperties> {
|
||||
return {
|
||||
...this.getAppProperties(),
|
||||
...this.getCloudProperties(),
|
||||
...(await this.getTaskProperties()),
|
||||
...(await this.getGitProperties()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -580,13 +580,13 @@ describe("ClineProvider", () => {
|
|||
await provider.addClineToStack(mockCline)
|
||||
|
||||
// get the stack size before the abort call
|
||||
const stackSizeBeforeAbort = provider.getClineStackSize()
|
||||
const stackSizeBeforeAbort = provider.getTaskStackSize()
|
||||
|
||||
// call the removeClineFromStack method so it will call the current cline abort and remove it from the stack
|
||||
await provider.removeClineFromStack()
|
||||
|
||||
// get the stack size after the abort call
|
||||
const stackSizeAfterAbort = provider.getClineStackSize()
|
||||
const stackSizeAfterAbort = provider.getTaskStackSize()
|
||||
|
||||
// check if the abort method was called
|
||||
expect(mockCline.abortTask).toHaveBeenCalled()
|
||||
|
|
@ -692,7 +692,7 @@ describe("ClineProvider", () => {
|
|||
await provider.addClineToStack(mockCline)
|
||||
|
||||
// Verify stack size is 1
|
||||
expect(provider.getClineStackSize()).toBe(1)
|
||||
expect(provider.getTaskStackSize()).toBe(1)
|
||||
|
||||
// Get the message handler
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]
|
||||
|
|
@ -718,10 +718,10 @@ describe("ClineProvider", () => {
|
|||
await provider.addClineToStack(mockCline2)
|
||||
|
||||
// verify cline instances were added to the stack
|
||||
expect(provider.getClineStackSize()).toBe(2)
|
||||
expect(provider.getTaskStackSize()).toBe(2)
|
||||
|
||||
// verify current cline instance is the last one added
|
||||
expect(provider.getCurrentCline()).toBe(mockCline2)
|
||||
expect(provider.getCurrentTask()).toBe(mockCline2)
|
||||
})
|
||||
|
||||
test("getState returns correct initial state", async () => {
|
||||
|
|
@ -1205,8 +1205,8 @@ describe("ClineProvider", () => {
|
|||
historyItem: { id: "test-task-id" },
|
||||
})
|
||||
|
||||
// Mock initClineWithHistoryItem
|
||||
;(provider as any).initClineWithHistoryItem = vi.fn()
|
||||
// Mock createTaskWithHistoryItem
|
||||
;(provider as any).createTaskWithHistoryItem = vi.fn()
|
||||
|
||||
// Trigger message deletion
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]
|
||||
|
|
@ -1230,8 +1230,8 @@ describe("ClineProvider", () => {
|
|||
mockApiHistory[1],
|
||||
])
|
||||
|
||||
// Verify initClineWithHistoryItem was called
|
||||
expect((provider as any).initClineWithHistoryItem).toHaveBeenCalledWith({ id: "test-task-id" })
|
||||
// Verify createTaskWithHistoryItem was called
|
||||
expect((provider as any).createTaskWithHistoryItem).toHaveBeenCalledWith({ id: "test-task-id" })
|
||||
})
|
||||
|
||||
test("handles case when no current task exists", async () => {
|
||||
|
|
@ -1660,7 +1660,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("initClineWithHistoryItem mode validation", () => {
|
||||
describe("createTaskWithHistoryItem mode validation", () => {
|
||||
test("validates and falls back to default mode when restored mode no longer exists", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
|
|
@ -1711,7 +1711,7 @@ describe("ClineProvider", () => {
|
|||
}
|
||||
|
||||
// Initialize with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify mode validation occurred
|
||||
expect(mockCustomModesManager.getCustomModes).toHaveBeenCalled()
|
||||
|
|
@ -1780,7 +1780,7 @@ describe("ClineProvider", () => {
|
|||
}
|
||||
|
||||
// Initialize with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify mode validation occurred
|
||||
expect(mockCustomModesManager.getCustomModes).toHaveBeenCalled()
|
||||
|
|
@ -1832,7 +1832,7 @@ describe("ClineProvider", () => {
|
|||
}
|
||||
|
||||
// Initialize with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify mode was preserved
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("mode", "architect")
|
||||
|
|
@ -1863,7 +1863,7 @@ describe("ClineProvider", () => {
|
|||
}
|
||||
|
||||
// Initialize with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify no mode validation occurred (mode update not called)
|
||||
expect(mockContext.globalState.update).not.toHaveBeenCalledWith("mode", expect.any(String))
|
||||
|
|
@ -1913,7 +1913,7 @@ describe("ClineProvider", () => {
|
|||
}
|
||||
|
||||
// Initialize with history item - should not throw
|
||||
await expect(provider.initClineWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
await expect(provider.createTaskWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
|
||||
// Verify error was logged but task restoration continued
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
|
|
@ -3235,7 +3235,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
|
|||
|
||||
test("handles edit permission failures", async () => {
|
||||
// Mock no current cline (simulating permission failure)
|
||||
vi.spyOn(provider, "getCurrentCline").mockReturnValue(undefined)
|
||||
vi.spyOn(provider, "getCurrentTask").mockReturnValue(undefined)
|
||||
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]
|
||||
|
||||
|
|
@ -3654,7 +3654,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
|
|||
;(provider as any).getTaskWithId = vi.fn().mockResolvedValue({
|
||||
historyItem: { id: "test-task-id" },
|
||||
})
|
||||
;(provider as any).initClineWithHistoryItem = vi.fn()
|
||||
;(provider as any).createTaskWithHistoryItem = vi.fn()
|
||||
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]
|
||||
|
||||
|
|
@ -3671,7 +3671,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
|
|||
|
||||
// Verify successful operation completed
|
||||
expect(mockCline.overwriteClineMessages).toHaveBeenCalled()
|
||||
expect(provider.initClineWithHistoryItem).toHaveBeenCalled()
|
||||
expect(provider.createTaskWithHistoryItem).toHaveBeenCalled()
|
||||
expect(vscode.window.showErrorMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("initClineWithHistoryItem", () => {
|
||||
describe("createTaskWithHistoryItem", () => {
|
||||
it("should restore mode from history item when reopening task", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
|
|
@ -437,7 +437,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
const updateGlobalStateSpy = vi.spyOn(provider as any, "updateGlobalState").mockResolvedValue(undefined)
|
||||
|
||||
// Initialize task with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify mode was restored via updateGlobalState
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("mode", "architect")
|
||||
|
|
@ -479,7 +479,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
const handleModeSwitchSpy = vi.spyOn(provider, "handleModeSwitch").mockResolvedValue()
|
||||
|
||||
// Initialize task with history item
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify mode was not changed (should use current mode)
|
||||
expect(handleModeSwitchSpy).not.toHaveBeenCalled()
|
||||
|
|
@ -606,15 +606,15 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
// Initialize subtask with parent's mode
|
||||
taskModes[subtaskId] = "architect"
|
||||
|
||||
// Mock getCurrentCline to return the parent task initially
|
||||
const getCurrentClineMock = vi.spyOn(provider, "getCurrentCline")
|
||||
getCurrentClineMock.mockReturnValue(parentTask as any)
|
||||
// Mock getCurrentTask to return the parent task initially
|
||||
const getCurrentTaskMock = vi.spyOn(provider, "getCurrentTask")
|
||||
getCurrentTaskMock.mockReturnValue(parentTask as any)
|
||||
|
||||
// Add subtask to stack
|
||||
await provider.addClineToStack(subtask)
|
||||
|
||||
// Now mock getCurrentCline to return the subtask (simulating stack behavior)
|
||||
getCurrentClineMock.mockReturnValue(subtask as any)
|
||||
// Now mock getCurrentTask to return the subtask (simulating stack behavior)
|
||||
getCurrentTaskMock.mockReturnValue(subtask as any)
|
||||
|
||||
// Switch subtask to code mode - this should only affect the subtask
|
||||
await provider.handleModeSwitch("code")
|
||||
|
|
@ -678,7 +678,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
const handleModeSwitchSpy = vi.spyOn(provider, "handleModeSwitch").mockResolvedValue()
|
||||
|
||||
// Initialize task with history item - should not throw
|
||||
await expect(provider.initClineWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
await expect(provider.createTaskWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
|
||||
// Verify mode switch was not called with null
|
||||
expect(handleModeSwitchSpy).not.toHaveBeenCalledWith(null)
|
||||
|
|
@ -719,7 +719,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
}
|
||||
|
||||
// Restore the task from history
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Verify that the mode was restored
|
||||
const state = await provider.getState()
|
||||
|
|
@ -764,7 +764,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
const handleModeSwitchSpy = vi.spyOn(provider, "handleModeSwitch").mockResolvedValue()
|
||||
|
||||
// Initialize task with history item - should not throw
|
||||
await expect(provider.initClineWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
await expect(provider.createTaskWithHistoryItem(historyItem)).resolves.not.toThrow()
|
||||
|
||||
// Verify mode switch was not called with deleted mode
|
||||
expect(handleModeSwitchSpy).not.toHaveBeenCalledWith("deleted-mode")
|
||||
|
|
@ -1105,17 +1105,17 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
.spyOn(provider, "updateTaskHistory")
|
||||
.mockImplementation(() => Promise.resolve([]))
|
||||
|
||||
// Mock getCurrentCline to return different tasks
|
||||
const getCurrentClineSpy = vi.spyOn(provider, "getCurrentCline")
|
||||
// Mock getCurrentTask to return different tasks
|
||||
const getCurrentTaskSpy = vi.spyOn(provider, "getCurrentTask")
|
||||
|
||||
// Simulate simultaneous mode switches for different tasks
|
||||
getCurrentClineSpy.mockReturnValue(task1 as any)
|
||||
getCurrentTaskSpy.mockReturnValue(task1 as any)
|
||||
const switch1 = provider.handleModeSwitch("architect")
|
||||
|
||||
getCurrentClineSpy.mockReturnValue(task2 as any)
|
||||
getCurrentTaskSpy.mockReturnValue(task2 as any)
|
||||
const switch2 = provider.handleModeSwitch("debug")
|
||||
|
||||
getCurrentClineSpy.mockReturnValue(task3 as any)
|
||||
getCurrentTaskSpy.mockReturnValue(task3 as any)
|
||||
const switch3 = provider.handleModeSwitch("code")
|
||||
|
||||
await Promise.all([switch1, switch2, switch3])
|
||||
|
|
@ -1166,7 +1166,7 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
vi.clearAllMocks()
|
||||
|
||||
// Start initialization
|
||||
const initPromise = provider.initClineWithHistoryItem(historyItem)
|
||||
const initPromise = provider.createTaskWithHistoryItem(historyItem)
|
||||
|
||||
// Try to switch mode during initialization
|
||||
await provider.handleModeSwitch("code")
|
||||
|
|
@ -1201,13 +1201,13 @@ describe("ClineProvider - Sticky Mode", () => {
|
|||
await provider.addClineToStack(task as any)
|
||||
}
|
||||
|
||||
// Mock getCurrentCline
|
||||
const getCurrentClineSpy = vi.spyOn(provider, "getCurrentCline")
|
||||
// Mock getCurrentTask
|
||||
const getCurrentTaskSpy = vi.spyOn(provider, "getCurrentTask")
|
||||
|
||||
// Rapidly switch between tasks and modes
|
||||
const switches: Promise<void>[] = []
|
||||
tasks.forEach((task, index) => {
|
||||
getCurrentClineSpy.mockReturnValue(task as any)
|
||||
getCurrentTaskSpy.mockReturnValue(task as any)
|
||||
const mode = ["architect", "debug", "code"][index % 3]
|
||||
switches.push(provider.handleModeSwitch(mode as any))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ const mockClineProvider = {
|
|||
},
|
||||
log: vi.fn(),
|
||||
postStateToWebview: vi.fn(),
|
||||
getCurrentCline: vi.fn(),
|
||||
getCurrentTask: vi.fn(),
|
||||
getTaskWithId: vi.fn(),
|
||||
initClineWithHistoryItem: vi.fn(),
|
||||
createTaskWithHistoryItem: vi.fn(),
|
||||
} as unknown as ClineProvider
|
||||
|
||||
import { t } from "../../../i18n"
|
||||
|
|
@ -533,7 +533,7 @@ describe("webviewMessageHandler - message dialog preferences", () => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
// Mock a current Cline instance
|
||||
vi.mocked(mockClineProvider.getCurrentCline).mockReturnValue({
|
||||
vi.mocked(mockClineProvider.getCurrentTask).mockReturnValue({
|
||||
taskId: "test-task-id",
|
||||
apiConversationHistory: [],
|
||||
clineMessages: [],
|
||||
|
|
@ -544,7 +544,7 @@ describe("webviewMessageHandler - message dialog preferences", () => {
|
|||
|
||||
describe("deleteMessage", () => {
|
||||
it("should always show dialog for delete confirmation", async () => {
|
||||
vi.mocked(mockClineProvider.getCurrentCline).mockReturnValue({} as any) // Mock current cline exists
|
||||
vi.mocked(mockClineProvider.getCurrentTask).mockReturnValue({} as any)
|
||||
|
||||
await webviewMessageHandler(mockClineProvider, {
|
||||
type: "deleteMessage",
|
||||
|
|
@ -560,12 +560,12 @@ describe("webviewMessageHandler - message dialog preferences", () => {
|
|||
|
||||
describe("submitEditedMessage", () => {
|
||||
it("should always show dialog for edit confirmation", async () => {
|
||||
vi.mocked(mockClineProvider.getCurrentCline).mockReturnValue({} as any) // Mock current cline exists
|
||||
vi.mocked(mockClineProvider.getCurrentTask).mockReturnValue({} as any)
|
||||
|
||||
await webviewMessageHandler(mockClineProvider, {
|
||||
type: "submitEditedMessage",
|
||||
value: 123456789, // messageTs as number
|
||||
editedMessageContent: "edited content", // text content in editedMessageContent field
|
||||
value: 123456789,
|
||||
editedMessageContent: "edited content",
|
||||
})
|
||||
|
||||
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
|
|||
const mode = message.mode ?? defaultModeSlug
|
||||
const customModes = await provider.customModesManager.getCustomModes()
|
||||
|
||||
const rooIgnoreInstructions = provider.getCurrentCline()?.rooIgnoreController?.getInstructions()
|
||||
const rooIgnoreInstructions = provider.getCurrentTask()?.rooIgnoreController?.getInstructions()
|
||||
|
||||
// Determine if browser tools can be used based on model support, mode, and user settings
|
||||
let modelSupportsComputerUse = false
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ export const webviewMessageHandler = async (
|
|||
* Handles confirmed message deletion from webview dialog
|
||||
*/
|
||||
const handleDeleteMessageConfirm = async (messageTs: number): Promise<void> => {
|
||||
// Only proceed if we have a current cline
|
||||
if (provider.getCurrentCline()) {
|
||||
const currentCline = provider.getCurrentCline()!
|
||||
// Only proceed if we have a current task.
|
||||
if (provider.getCurrentTask()) {
|
||||
const currentCline = provider.getCurrentTask()!
|
||||
const { messageIndex, apiConversationHistoryIndex } = findMessageIndices(messageTs, currentCline)
|
||||
|
||||
if (messageIndex !== -1) {
|
||||
|
|
@ -124,7 +124,7 @@ export const webviewMessageHandler = async (
|
|||
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
|
||||
|
||||
// Initialize with history item after deletion
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
await provider.createTaskWithHistoryItem(historyItem)
|
||||
} catch (error) {
|
||||
console.error("Error in delete message:", error)
|
||||
vscode.window.showErrorMessage(
|
||||
|
|
@ -156,9 +156,9 @@ export const webviewMessageHandler = async (
|
|||
editedContent: string,
|
||||
images?: string[],
|
||||
): Promise<void> => {
|
||||
// Only proceed if we have a current cline
|
||||
if (provider.getCurrentCline()) {
|
||||
const currentCline = provider.getCurrentCline()!
|
||||
// Only proceed if we have a current task.
|
||||
if (provider.getCurrentTask()) {
|
||||
const currentCline = provider.getCurrentTask()!
|
||||
|
||||
// Use findMessageIndices to find messages based on timestamp
|
||||
const { messageIndex, apiConversationHistoryIndex } = findMessageIndices(messageTs, currentCline)
|
||||
|
|
@ -288,7 +288,7 @@ export const webviewMessageHandler = async (
|
|||
// Initializing new instance of Cline will make sure that any
|
||||
// agentically running promises in old instance don't affect our new
|
||||
// task. This essentially creates a fresh slate for the new task.
|
||||
await provider.initClineWithTask(message.text, message.images)
|
||||
await provider.createTask(message.text, message.images)
|
||||
break
|
||||
case "customInstructions":
|
||||
await provider.updateCustomInstructions(message.text)
|
||||
|
|
@ -346,7 +346,7 @@ export const webviewMessageHandler = async (
|
|||
await provider.postStateToWebview()
|
||||
break
|
||||
case "askResponse":
|
||||
provider.getCurrentCline()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
|
||||
provider.getCurrentTask()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
|
||||
break
|
||||
case "autoCondenseContext":
|
||||
await updateGlobalState("autoCondenseContext", message.bool)
|
||||
|
|
@ -358,19 +358,23 @@ export const webviewMessageHandler = async (
|
|||
break
|
||||
case "terminalOperation":
|
||||
if (message.terminalOperation) {
|
||||
provider.getCurrentCline()?.handleTerminalOperation(message.terminalOperation)
|
||||
provider.getCurrentTask()?.handleTerminalOperation(message.terminalOperation)
|
||||
}
|
||||
break
|
||||
case "clearTask":
|
||||
// clear task resets the current session and allows for a new task to be started, if this session is a subtask - it allows the parent task to be resumed
|
||||
// Check if the current task actually has a parent task
|
||||
const currentTask = provider.getCurrentCline()
|
||||
// Clear task resets the current session and allows for a new task
|
||||
// to be started, if this session is a subtask - it allows the
|
||||
// parent task to be resumed.
|
||||
// Check if the current task actually has a parent task.
|
||||
const currentTask = provider.getCurrentTask()
|
||||
|
||||
if (currentTask && currentTask.parentTask) {
|
||||
await provider.finishSubTask(t("common:tasks.canceled"))
|
||||
} else {
|
||||
// Regular task - just clear it
|
||||
await provider.clearTask()
|
||||
}
|
||||
|
||||
await provider.postStateToWebview()
|
||||
break
|
||||
case "didShowAnnouncement":
|
||||
|
|
@ -387,14 +391,15 @@ export const webviewMessageHandler = async (
|
|||
})
|
||||
break
|
||||
case "exportCurrentTask":
|
||||
const currentTaskId = provider.getCurrentCline()?.taskId
|
||||
const currentTaskId = provider.getCurrentTask()?.taskId
|
||||
if (currentTaskId) {
|
||||
provider.exportTaskWithId(currentTaskId)
|
||||
}
|
||||
break
|
||||
case "shareCurrentTask":
|
||||
const shareTaskId = provider.getCurrentCline()?.taskId
|
||||
const clineMessages = provider.getCurrentCline()?.clineMessages
|
||||
const shareTaskId = provider.getCurrentTask()?.taskId
|
||||
const clineMessages = provider.getCurrentTask()?.clineMessages
|
||||
|
||||
if (!shareTaskId) {
|
||||
vscode.window.showErrorMessage(t("common:errors.share_no_active_task"))
|
||||
break
|
||||
|
|
@ -725,7 +730,7 @@ export const webviewMessageHandler = async (
|
|||
const result = checkoutDiffPayloadSchema.safeParse(message.payload)
|
||||
|
||||
if (result.success) {
|
||||
await provider.getCurrentCline()?.checkpointDiff(result.data)
|
||||
await provider.getCurrentTask()?.checkpointDiff(result.data)
|
||||
}
|
||||
|
||||
break
|
||||
|
|
@ -736,13 +741,13 @@ export const webviewMessageHandler = async (
|
|||
await provider.cancelTask()
|
||||
|
||||
try {
|
||||
await pWaitFor(() => provider.getCurrentCline()?.isInitialized === true, { timeout: 3_000 })
|
||||
await pWaitFor(() => provider.getCurrentTask()?.isInitialized === true, { timeout: 3_000 })
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(t("common:errors.checkpoint_timeout"))
|
||||
}
|
||||
|
||||
try {
|
||||
await provider.getCurrentCline()?.checkpointRestore(result.data)
|
||||
await provider.getCurrentTask()?.checkpointRestore(result.data)
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(t("common:errors.checkpoint_failed"))
|
||||
}
|
||||
|
|
@ -1227,14 +1232,14 @@ export const webviewMessageHandler = async (
|
|||
}
|
||||
break
|
||||
case "deleteMessage": {
|
||||
if (provider.getCurrentCline() && typeof message.value === "number" && message.value) {
|
||||
if (provider.getCurrentTask() && typeof message.value === "number" && message.value) {
|
||||
await handleMessageModificationsOperation(message.value, "delete")
|
||||
}
|
||||
break
|
||||
}
|
||||
case "submitEditedMessage": {
|
||||
if (
|
||||
provider.getCurrentCline() &&
|
||||
provider.getCurrentTask() &&
|
||||
typeof message.value === "number" &&
|
||||
message.value &&
|
||||
message.editedMessageContent
|
||||
|
|
@ -1366,6 +1371,7 @@ export const webviewMessageHandler = async (
|
|||
if (message.text) {
|
||||
try {
|
||||
const state = await provider.getState()
|
||||
|
||||
const {
|
||||
apiConfiguration,
|
||||
customSupportPrompts,
|
||||
|
|
@ -1374,7 +1380,8 @@ export const webviewMessageHandler = async (
|
|||
includeTaskHistoryInEnhance,
|
||||
} = state
|
||||
|
||||
const currentCline = provider.getCurrentCline()
|
||||
const currentCline = provider.getCurrentTask()
|
||||
|
||||
const result = await MessageEnhancer.enhanceMessage({
|
||||
text: message.text,
|
||||
apiConfiguration,
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
await provider.postMessageToWebview({ type: "invoke", invoke: "newChat", text, images })
|
||||
|
||||
const cline = await provider.initClineWithTask(text, images, undefined, {
|
||||
const cline = await provider.createTask(text, images, undefined, {
|
||||
consecutiveMistakeLimit: Number.MAX_SAFE_INTEGER,
|
||||
})
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
|
||||
public async resumeTask(taskId: string): Promise<void> {
|
||||
const { historyItem } = await this.sidebarProvider.getTaskWithId(taskId)
|
||||
await this.sidebarProvider.initClineWithHistoryItem(historyItem)
|
||||
await this.sidebarProvider.createTaskWithHistoryItem(historyItem)
|
||||
await this.sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@
|
|||
"@mistralai/mistralai": "^1.3.6",
|
||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||
"@qdrant/js-client-rest": "^1.14.0",
|
||||
"@roo-code/cloud": "^0.14.0",
|
||||
"@roo-code/cloud": "^0.15.0",
|
||||
"@roo-code/ipc": "workspace:^",
|
||||
"@roo-code/telemetry": "workspace:^",
|
||||
"@roo-code/types": "workspace:^",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { ExecException } from "child_process"
|
||||
import * as vscode from "vscode"
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
|
||||
|
|
@ -12,7 +11,6 @@ import {
|
|||
sanitizeGitUrl,
|
||||
extractRepositoryName,
|
||||
getWorkspaceGitInfo,
|
||||
GitRepositoryInfo,
|
||||
convertGitUrlToHttps,
|
||||
} from "../git"
|
||||
import { truncateOutput } from "../../integrations/misc/extract-text"
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ export async function getWorkspaceGitInfo(): Promise<GitRepositoryInfo> {
|
|||
return {}
|
||||
}
|
||||
|
||||
// Use the first workspace folder
|
||||
// Use the first workspace folder.
|
||||
const workspaceRoot = workspaceFolders[0].uri.fsPath
|
||||
return getGitRepositoryInfo(workspaceRoot)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue