mirror of
https://github.com/cline/cline.git
synced 2026-09-01 15:11:04 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 042d3a3fdb | |||
| ec5348180b | |||
| 2793a8fd38 |
@@ -4,6 +4,7 @@ import { WorkspacePathAdapter } from "@core/workspace/WorkspacePathAdapter"
|
||||
import { showSystemNotification } from "@integrations/notifications"
|
||||
import { COMMAND_REQ_APP_STRING } from "@shared/combineCommandSequences"
|
||||
import { ClineAsk } from "@shared/ExtensionMessage"
|
||||
import { arePathsEqual } from "@utils/path"
|
||||
import { fixModelHtmlEscaping } from "@utils/string"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { ClineDefaultTool } from "@/shared/tools"
|
||||
@@ -81,13 +82,17 @@ export class ExecuteCommandToolHandler implements IFullyManagedTool {
|
||||
let executionDir: string = config.cwd
|
||||
let actualCommand: string = command
|
||||
|
||||
let workspaceHintUsed = false
|
||||
let workspaceHint: string | undefined
|
||||
|
||||
if (config.isMultiRootEnabled && config.workspaceManager) {
|
||||
// Check if command has a workspace hint prefix
|
||||
// e.g., "@backend:npm install" or just "npm install"
|
||||
const commandMatch = command.match(/^@(\w+):(.+)$/)
|
||||
|
||||
if (commandMatch) {
|
||||
const workspaceHint = commandMatch[1]
|
||||
workspaceHintUsed = true
|
||||
workspaceHint = commandMatch[1]
|
||||
actualCommand = commandMatch[2].trim()
|
||||
|
||||
// Find the workspace root for this hint
|
||||
@@ -122,13 +127,35 @@ export class ExecuteCommandToolHandler implements IFullyManagedTool {
|
||||
? autoApproveResult
|
||||
: [autoApproveResult, false]
|
||||
|
||||
// Determine workspace context for telemetry
|
||||
const resolvedToNonPrimary = !arePathsEqual(executionDir, config.cwd)
|
||||
const workspaceContext = {
|
||||
isMultiRootEnabled: config.isMultiRootEnabled || false,
|
||||
usedWorkspaceHint: workspaceHintUsed,
|
||||
resolvedToNonPrimary,
|
||||
resolutionMethod: (workspaceHintUsed ? "hint" : "primary_fallback") as "hint" | "primary_fallback",
|
||||
}
|
||||
|
||||
// Capture workspace path resolution telemetry
|
||||
if (config.isMultiRootEnabled && config.workspaceManager) {
|
||||
telemetryService.captureWorkspacePathResolved(
|
||||
config.ulid,
|
||||
"ExecuteCommandToolHandler",
|
||||
workspaceHintUsed ? "hint_provided" : "fallback_to_primary",
|
||||
workspaceHintUsed ? "workspace_name" : undefined,
|
||||
resolvedToNonPrimary, // resolution success = resolved to different workspace
|
||||
undefined, // TODO: could calculate workspace index if needed
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
if ((!requiresApprovalPerLLM && autoApproveSafe) || (requiresApprovalPerLLM && autoApproveSafe && autoApproveAll)) {
|
||||
// Auto-approve flow
|
||||
await config.callbacks.removeLastPartialMessageIfExistsWithType("ask", "command")
|
||||
await config.callbacks.say("command", actualCommand, undefined, undefined, false)
|
||||
config.taskState.consecutiveAutoApprovedRequestsCount++
|
||||
didAutoApprove = true
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true, workspaceContext)
|
||||
} else {
|
||||
// Manual approval flow
|
||||
showNotificationForApprovalIfAutoApprovalEnabled(
|
||||
@@ -143,10 +170,17 @@ export class ExecuteCommandToolHandler implements IFullyManagedTool {
|
||||
config,
|
||||
)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, false)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
false,
|
||||
workspaceContext,
|
||||
)
|
||||
return formatResponse.toolDenied()
|
||||
}
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true, workspaceContext)
|
||||
}
|
||||
|
||||
// Setup timeout notification for long-running auto-approved commands
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import path from "node:path"
|
||||
import type { ToolUse } from "@core/assistant-message"
|
||||
import { formatResponse } from "@core/prompts/responses"
|
||||
import { getWorkspaceBasename, resolveWorkspacePath } from "@core/workspace"
|
||||
import { listFiles } from "@services/glob/list-files"
|
||||
import { getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { arePathsEqual, getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { ClineDefaultTool } from "@/shared/tools"
|
||||
import type { ToolResponse } from "../../index"
|
||||
@@ -69,6 +70,15 @@ export class ListFilesToolHandler implements IFullyManagedTool {
|
||||
const { absolutePath, displayPath } =
|
||||
typeof pathResult === "string" ? { absolutePath: pathResult, displayPath: relDirPath! } : pathResult
|
||||
|
||||
// Determine workspace context for telemetry
|
||||
const fallbackAbsolutePath = path.resolve(config.cwd, relDirPath ?? "")
|
||||
const workspaceContext = {
|
||||
isMultiRootEnabled: config.isMultiRootEnabled || false,
|
||||
usedWorkspaceHint: typeof pathResult !== "string", // multi-root path result indicates hint usage
|
||||
resolvedToNonPrimary: !arePathsEqual(absolutePath, fallbackAbsolutePath),
|
||||
resolutionMethod: (typeof pathResult !== "string" ? "hint" : "primary_fallback") as "hint" | "primary_fallback",
|
||||
}
|
||||
|
||||
// Execute the actual list files operation
|
||||
const [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)
|
||||
|
||||
@@ -91,7 +101,7 @@ export class ListFilesToolHandler implements IFullyManagedTool {
|
||||
config.taskState.consecutiveAutoApprovedRequestsCount++
|
||||
|
||||
// Capture telemetry
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true, workspaceContext)
|
||||
} else {
|
||||
// Manual approval flow
|
||||
const notificationMessage = `Cline wants to view directory ${getWorkspaceBasename(absolutePath, "ListFilesToolHandler.notification")}/`
|
||||
@@ -107,10 +117,24 @@ export class ListFilesToolHandler implements IFullyManagedTool {
|
||||
|
||||
const didApprove = await ToolResultUtils.askApprovalAndPushFeedback("tool", completeMessage, config)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, false)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
false,
|
||||
workspaceContext,
|
||||
)
|
||||
return formatResponse.toolDenied()
|
||||
} else {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
true,
|
||||
workspaceContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import path from "node:path"
|
||||
import type { ToolUse } from "@core/assistant-message"
|
||||
import { formatResponse } from "@core/prompts/responses"
|
||||
import { getWorkspaceBasename, resolveWorkspacePath } from "@core/workspace"
|
||||
import { extractFileContent } from "@integrations/misc/extract-file-content"
|
||||
import { getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { arePathsEqual, getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { ClineSayTool } from "@/shared/ExtensionMessage"
|
||||
import { ClineDefaultTool } from "@/shared/tools"
|
||||
@@ -72,6 +73,15 @@ export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
const { absolutePath, displayPath } =
|
||||
typeof pathResult === "string" ? { absolutePath: pathResult, displayPath: relPath! } : pathResult
|
||||
|
||||
// Determine workspace context for telemetry
|
||||
const fallbackAbsolutePath = path.resolve(config.cwd, relPath ?? "")
|
||||
const workspaceContext = {
|
||||
isMultiRootEnabled: config.isMultiRootEnabled || false,
|
||||
usedWorkspaceHint: typeof pathResult !== "string", // multi-root path result indicates hint usage
|
||||
resolvedToNonPrimary: !arePathsEqual(absolutePath, fallbackAbsolutePath),
|
||||
resolutionMethod: (typeof pathResult !== "string" ? "hint" : "primary_fallback") as "hint" | "primary_fallback",
|
||||
}
|
||||
|
||||
// Handle approval flow
|
||||
const sharedMessageProps = {
|
||||
tool: "readFile",
|
||||
@@ -89,7 +99,7 @@ export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
config.taskState.consecutiveAutoApprovedRequestsCount++
|
||||
|
||||
// Capture telemetry
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true, workspaceContext)
|
||||
} else {
|
||||
// Manual approval flow
|
||||
const notificationMessage = `Cline wants to read ${getWorkspaceBasename(absolutePath, "ReadFileToolHandler.notification")}`
|
||||
@@ -105,10 +115,24 @@ export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
|
||||
const didApprove = await ToolResultUtils.askApprovalAndPushFeedback("tool", completeMessage, config)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, false)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
false,
|
||||
workspaceContext,
|
||||
)
|
||||
return formatResponse.toolDenied()
|
||||
} else {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
true,
|
||||
workspaceContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ToolUse } from "@core/assistant-message"
|
||||
import { regexSearchFiles } from "@services/ripgrep"
|
||||
import { getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { arePathsEqual, getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import * as path from "path"
|
||||
import { formatResponse } from "@/core/prompts/responses"
|
||||
import { parseWorkspaceInlinePath } from "@/core/workspace/utils/parseWorkspaceInlinePath"
|
||||
@@ -227,17 +227,68 @@ export class SearchFilesToolHandler implements IFullyManagedTool {
|
||||
// Determine which paths to search
|
||||
const searchPaths = this.determineSearchPaths(config, parsedPath, workspaceHint, relDirPath!)
|
||||
|
||||
// Determine workspace context for telemetry
|
||||
const primaryWorkspaceRoot = searchPaths[0]?.workspaceRoot
|
||||
const resolvedToNonPrimary =
|
||||
searchPaths.length === 0
|
||||
? true
|
||||
: searchPaths.length > 1 || (primaryWorkspaceRoot ? !arePathsEqual(primaryWorkspaceRoot, config.cwd) : true)
|
||||
const workspaceContext = {
|
||||
isMultiRootEnabled: config.isMultiRootEnabled || false,
|
||||
usedWorkspaceHint: !!workspaceHint,
|
||||
resolvedToNonPrimary,
|
||||
resolutionMethod: (workspaceHint ? "hint" : searchPaths.length > 1 ? "path_detection" : "primary_fallback") as
|
||||
| "hint"
|
||||
| "primary_fallback"
|
||||
| "path_detection",
|
||||
}
|
||||
|
||||
// Capture workspace path resolution telemetry
|
||||
if (config.isMultiRootEnabled && config.workspaceManager) {
|
||||
const resolutionType = workspaceHint
|
||||
? "hint_provided"
|
||||
: searchPaths.length > 1
|
||||
? "cross_workspace_search"
|
||||
: "fallback_to_primary"
|
||||
telemetryService.captureWorkspacePathResolved(
|
||||
config.ulid,
|
||||
"SearchFilesToolHandler",
|
||||
resolutionType,
|
||||
workspaceHint ? "workspace_name" : undefined,
|
||||
searchPaths.length > 0, // resolution success = found paths to search
|
||||
undefined, // TODO: could calculate primary workspace index
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// Execute searches in all relevant workspaces in parallel
|
||||
const searchPromises = searchPaths.map(({ absolutePath, workspaceName, workspaceRoot }) =>
|
||||
this.executeSearch(config, absolutePath, workspaceName, workspaceRoot, regex, filePattern),
|
||||
)
|
||||
|
||||
// Wait for all searches to complete
|
||||
const searchStartTime = performance.now()
|
||||
const searchResults = await Promise.all(searchPromises)
|
||||
const searchDurationMs = performance.now() - searchStartTime
|
||||
|
||||
// Format and combine results
|
||||
const results = this.formatSearchResults(config, searchResults, searchPaths)
|
||||
|
||||
// Capture workspace search pattern telemetry
|
||||
if (config.isMultiRootEnabled && config.workspaceManager) {
|
||||
const searchType = workspaceHint ? "targeted" : searchPaths.length > 1 ? "cross_workspace" : "primary_only"
|
||||
const resultsFound = searchResults.some((result) => result.resultCount > 0)
|
||||
|
||||
telemetryService.captureWorkspaceSearchPattern(
|
||||
config.ulid,
|
||||
searchType,
|
||||
searchPaths.length,
|
||||
!!workspaceHint,
|
||||
resultsFound,
|
||||
searchDurationMs,
|
||||
)
|
||||
}
|
||||
|
||||
const sharedMessageProps = {
|
||||
tool: "searchFiles",
|
||||
path: getReadablePath(config.cwd, relDirPath!),
|
||||
@@ -256,7 +307,7 @@ export class SearchFilesToolHandler implements IFullyManagedTool {
|
||||
config.taskState.consecutiveAutoApprovedRequestsCount++
|
||||
|
||||
// Capture telemetry
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true, workspaceContext)
|
||||
} else {
|
||||
// Manual approval flow
|
||||
const notificationMessage = `Cline wants to search files for ${regex}`
|
||||
@@ -272,10 +323,24 @@ export class SearchFilesToolHandler implements IFullyManagedTool {
|
||||
|
||||
const didApprove = await ToolResultUtils.askApprovalAndPushFeedback("tool", completeMessage, config)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, false)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
false,
|
||||
workspaceContext,
|
||||
)
|
||||
return formatResponse.toolDenied()
|
||||
} else {
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
true,
|
||||
workspaceContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path"
|
||||
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
|
||||
import type { ToolUse } from "@core/assistant-message"
|
||||
import { constructNewFileContent } from "@core/assistant-message/diff"
|
||||
@@ -6,7 +7,7 @@ import { getWorkspaceBasename, resolveWorkspacePath } from "@core/workspace"
|
||||
import { processFilesIntoText } from "@integrations/misc/extract-text"
|
||||
import { ClineSayTool } from "@shared/ExtensionMessage"
|
||||
import { fileExistsAtPath } from "@utils/fs"
|
||||
import { getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { arePathsEqual, getReadablePath, isLocatedInWorkspace } from "@utils/path"
|
||||
import { fixModelHtmlEscaping, removeInvalidChars } from "@utils/string"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { ClineDefaultTool } from "@/shared/tools"
|
||||
@@ -121,7 +122,8 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
return "" // can only happen if the sharedLogic adds an error to userMessages
|
||||
}
|
||||
|
||||
const { relPath, absolutePath, fileExists, diff, content, newContent } = result
|
||||
const { relPath, absolutePath, fileExists, diff, content, newContent, workspaceContext } = result
|
||||
|
||||
|
||||
// Handle approval flow
|
||||
const sharedMessageProps: ClineSayTool = {
|
||||
@@ -163,7 +165,7 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
config.taskState.consecutiveAutoApprovedRequestsCount++
|
||||
|
||||
// Capture telemetry
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true)
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, true, true, workspaceContext)
|
||||
|
||||
// we need an artificial delay to let the diagnostics catch up to the changes
|
||||
await setTimeoutPromise(3_500)
|
||||
@@ -212,7 +214,14 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
// await config.services.diffViewProvider.reset()
|
||||
|
||||
config.taskState.didRejectTool = true
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, false)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
false,
|
||||
workspaceContext,
|
||||
)
|
||||
|
||||
await config.services.diffViewProvider.revertChanges()
|
||||
return `The user denied this operation. ${fileDeniedNote}`
|
||||
@@ -234,7 +243,14 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
await config.callbacks.say("user_feedback", text, images, files)
|
||||
}
|
||||
|
||||
telemetryService.captureToolUsage(config.ulid, block.name, config.api.getModel().id, false, true)
|
||||
telemetryService.captureToolUsage(
|
||||
config.ulid,
|
||||
block.name,
|
||||
config.api.getModel().id,
|
||||
false,
|
||||
true,
|
||||
workspaceContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +320,15 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
? { absolutePath: pathResult, resolvedPath: relPath }
|
||||
: { absolutePath: pathResult.absolutePath, resolvedPath: pathResult.resolvedPath }
|
||||
|
||||
// Determine workspace context for telemetry
|
||||
const fallbackAbsolutePath = path.resolve(config.cwd, relPath)
|
||||
const workspaceContext = {
|
||||
isMultiRootEnabled: config.isMultiRootEnabled || false,
|
||||
usedWorkspaceHint: typeof pathResult !== "string", // multi-root path result indicates hint usage
|
||||
resolvedToNonPrimary: !arePathsEqual(absolutePath, fallbackAbsolutePath),
|
||||
resolutionMethod: (typeof pathResult !== "string" ? "hint" : "primary_fallback") as "hint" | "primary_fallback",
|
||||
}
|
||||
|
||||
// Check clineignore access first
|
||||
const accessValidation = this.validator.checkClineIgnorePath(resolvedPath)
|
||||
if (!accessValidation.ok) {
|
||||
@@ -420,6 +445,6 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
|
||||
|
||||
newContent = newContent.trimEnd() // remove any trailing newlines, since it's automatically inserted by the editor
|
||||
|
||||
return { relPath, absolutePath, fileExists, diff, content, newContent }
|
||||
return { relPath, absolutePath, fileExists, diff, content, newContent, workspaceContext }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,8 @@ export class TelemetryService {
|
||||
MENTION_USED: "task.mention_used",
|
||||
MENTION_FAILED: "task.mention_failed",
|
||||
MENTION_SEARCH_RESULTS: "task.mention_search_results",
|
||||
// Multi-workspace search pattern tracking
|
||||
WORKSPACE_SEARCH_PATTERN: "task.workspace_search_pattern",
|
||||
},
|
||||
// UI interaction events for tracking user engagement
|
||||
UI: {
|
||||
@@ -580,10 +582,24 @@ export class TelemetryService {
|
||||
* Records when a tool is used during task execution
|
||||
* @param ulid Unique identifier for the task
|
||||
* @param tool Name of the tool being used
|
||||
* @param modelId The model ID being used
|
||||
* @param autoApproved Whether the tool was auto-approved based on settings
|
||||
* @param success Whether the tool execution was successful
|
||||
* @param workspaceContext Optional workspace context for multi-root workspace tracking
|
||||
*/
|
||||
public captureToolUsage(ulid: string, tool: string, modelId: string, autoApproved: boolean, success: boolean) {
|
||||
public captureToolUsage(
|
||||
ulid: string,
|
||||
tool: string,
|
||||
modelId: string,
|
||||
autoApproved: boolean,
|
||||
success: boolean,
|
||||
workspaceContext?: {
|
||||
isMultiRootEnabled: boolean
|
||||
usedWorkspaceHint: boolean
|
||||
resolvedToNonPrimary: boolean
|
||||
resolutionMethod: "hint" | "primary_fallback" | "path_detection"
|
||||
},
|
||||
) {
|
||||
this.capture({
|
||||
event: TelemetryService.EVENTS.TASK.TOOL_USED,
|
||||
properties: {
|
||||
@@ -592,6 +608,13 @@ export class TelemetryService {
|
||||
autoApproved,
|
||||
success,
|
||||
modelId,
|
||||
// Workspace context (optional)
|
||||
...(workspaceContext && {
|
||||
workspace_multi_root_enabled: workspaceContext.isMultiRootEnabled,
|
||||
workspace_hint_used: workspaceContext.usedWorkspaceHint,
|
||||
workspace_resolved_non_primary: workspaceContext.resolvedToNonPrimary,
|
||||
workspace_resolution_method: workspaceContext.resolutionMethod,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1246,6 +1269,69 @@ export class TelemetryService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Records workspace path resolution events
|
||||
* @param ulid Unique identifier for the task
|
||||
* @param context The component/handler where resolution occurred
|
||||
* @param resolutionType Type of resolution performed
|
||||
* @param hintType Type of workspace hint provided (if any)
|
||||
* @param resolutionSuccess Whether the resolution was successful
|
||||
* @param targetWorkspaceIndex Index of the resolved workspace (0=primary, 1=secondary, etc.)
|
||||
* @param isMultiRootEnabled Whether multi-root mode is enabled
|
||||
*/
|
||||
public captureWorkspacePathResolved(
|
||||
ulid: string,
|
||||
context: string,
|
||||
resolutionType: "hint_provided" | "fallback_to_primary" | "cross_workspace_search",
|
||||
hintType?: "workspace_name" | "workspace_path" | "invalid",
|
||||
resolutionSuccess?: boolean,
|
||||
targetWorkspaceIndex?: number,
|
||||
isMultiRootEnabled?: boolean,
|
||||
) {
|
||||
this.capture({
|
||||
event: TelemetryService.EVENTS.WORKSPACE.PATH_RESOLVED,
|
||||
properties: {
|
||||
ulid,
|
||||
context,
|
||||
resolution_type: resolutionType,
|
||||
hint_type: hintType,
|
||||
resolution_success: resolutionSuccess,
|
||||
target_workspace_index: targetWorkspaceIndex,
|
||||
is_multi_root_enabled: isMultiRootEnabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Records multi-workspace search patterns and performance
|
||||
* @param ulid Unique identifier for the task
|
||||
* @param searchType Type of search performed
|
||||
* @param workspaceCount Number of workspaces searched
|
||||
* @param hintProvided Whether a workspace hint was provided
|
||||
* @param resultsFound Whether search results were found
|
||||
* @param searchDurationMs Optional search duration in milliseconds
|
||||
*/
|
||||
public captureWorkspaceSearchPattern(
|
||||
ulid: string,
|
||||
searchType: "targeted" | "cross_workspace" | "primary_only",
|
||||
workspaceCount: number,
|
||||
hintProvided: boolean,
|
||||
resultsFound: boolean,
|
||||
searchDurationMs?: number,
|
||||
) {
|
||||
this.capture({
|
||||
event: TelemetryService.EVENTS.TASK.WORKSPACE_SEARCH_PATTERN,
|
||||
properties: {
|
||||
ulid,
|
||||
search_type: searchType,
|
||||
workspace_count: workspaceCount,
|
||||
hint_provided: hintProvided,
|
||||
results_found: resultsFound,
|
||||
search_duration_ms: searchDurationMs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific telemetry category is enabled
|
||||
* @param category The telemetry category to check
|
||||
|
||||
Reference in New Issue
Block a user