first set of perf improvements

This commit is contained in:
Max Paulus 🥪
2026-05-20 09:21:58 -07:00
parent 88ea6834d3
commit 3a6a8aebe9
7 changed files with 372 additions and 20 deletions
+241
View File
@@ -0,0 +1,241 @@
# History Flow Performance Improvements
This is a working checklist for improving slowness when:
1. Opening History.
2. Opening a task from History.
The current suspected high-level cause is a combination of cold SDK/core host startup, full-history scans, legacy task migration, large message loading, and expensive UI/state updates. We should tackle these incrementally and measure after each change.
## 1. Add timing instrumentation first
Status: implemented initial `[HistoryPerf]` logging in the webview and backend.
Before changing behavior, add focused timing logs around the hot path so each improvement can be verified.
Suggested spans:
- `HistoryView` mount to `getTaskHistory` response.
- `HistoryView` mount to `getTotalTasksSize` response.
- `SdkController.getTaskHistory` total time.
- `SdkTaskHistory.listHistory` total time.
- `VscodeSessionHost.create` time.
- `ClineCore.create` / hub connection wait time if separable.
- `host.listHistory` time.
- legacy `readTaskHistory` + merge/sort time.
- `showTaskWithId` total time.
- task lookup time.
- legacy migration time.
- `readMessages` time.
- `sdkMessagesToClineMessages` time.
- webview message push loop time.
- `postStateToWebview` time.
Goal: confirm which bottleneck dominates for the affected user.
Initial instrumentation locations:
- `webview-ui/src/components/history/HistoryView.tsx`
- `src/core/controller/task/getTotalTasksSize.ts`
- `src/sdk/SdkController.ts`
- `src/sdk/sdk-task-control-coordinator.ts`
- `src/sdk/sdk-task-history.ts`
- `src/sdk/vscode-session-host.ts`
## 2. Avoid unbounded history listing in `showTaskWithId`
Status: implemented targeted `findHistoryItem(taskId)` lookup for task opening. It now tries SDK `host.get(taskId)` first and falls back to legacy `taskHistory.json` lookup by ID, avoiding the previous default full `listHistory()` call in `SdkController.showTaskWithId`.
Current issue:
- `SdkController.showTaskWithId(taskId)` calls `this.taskHistory.listHistory()` with default options.
- `SdkTaskHistory.listHistory()` defaults to a very large limit and may hydrate records.
- Opening one task should not require listing thousands of sessions.
Potential fixes:
- Add a targeted `findHistoryItem(taskId)` path that does not scan/hydrate all history.
- At minimum, call `listHistory({ hydrate: false })` where lookup is unavoidable.
- Prefer SDK host `get(taskId)` plus legacy fallback lookup by ID.
Expected impact: faster task opening, especially with 1000+ old sessions.
## 3. Reuse a history/session host instead of creating temporary hosts repeatedly
Current issue:
- `SdkTaskHistory.withHistoryHost()` creates a temporary `VscodeSessionHost` when there is no active session.
- A single History open or task click can trigger multiple temporary host creations.
Potential fixes:
- Keep a shared lazy history host for read-only history operations.
- Dispose it on extension deactivation or after an idle timeout.
- Avoid creating and disposing a host for every `listHistory`, `getClineMessages`, or migration check.
Expected impact: reduces cold `ClineCore.create()`/hub setup costs and repeated startup overhead.
## 4. Decouple History from hub startup blocking
Current issue:
- `VscodeSessionHost.create()` awaits `getActivationHubConnection()` if hub startup is in progress.
- Starting the hub earlier can hide cold startup, but can also make the first History call wait for hub readiness and causes lifecycle risk.
Potential fixes:
- Do not block history-only operations on in-progress hub startup if local fallback is acceptable.
- Bound hub wait time more aggressively for history reads.
- Make hub startup lazy/non-blocking unless a real runtime session needs it.
Expected impact: avoids History waiting up to the hub startup timeout.
## 5. Optimize `getTaskHistory` pagination and filtering
Current issue:
- `SdkTaskHistory.listHistory({ limit, offset })` converts offset pagination into `hostLimit = offset + limit`.
- Later pages become increasingly expensive.
- Filtering/searching happens after fetching one page, so filtered results can be incomplete and may encourage extra loads.
Potential fixes:
- Add cursor-based pagination if SDK/core supports it.
- Add filtering/search/sort to the lower-level history API if possible.
- If not possible, maintain an indexed/cached history list and paginate from the cache.
- Ensure `hasMore` reflects filtered results, not just raw page size.
Expected impact: better infinite-scroll performance and more correct search/filter behavior.
## 6. Cache or index merged history metadata
Current issue:
- Every history request reads SDK history, reads legacy `taskHistory.json`, merges, sorts, and slices.
- With many old tasks this repeated work adds up.
Potential fixes:
- Maintain an in-memory cache of merged history metadata.
- Invalidate on task create/update/delete/favorite/migration.
- Consider persisting a lightweight index if startup scans are still expensive.
Expected impact: faster repeated History opens, filtering, and state updates.
## 7. Make `getTotalTasksSize` lazy/cached/backgrounded
Current issue:
- Opening History triggers recursive size calculation for `globalStorageFsPath/tasks` and `checkpoints`.
- This can be slow with many tasks/checkpoints.
Potential fixes:
- Cache the total size and recompute in the background.
- Show “calculating…” instead of blocking or competing with history load.
- Update size only after delete/export operations or on a debounce.
- Consider removing automatic full directory size scan from initial History mount.
Expected impact: faster perceived History open and less filesystem contention.
## 8. Improve legacy task migration on open
Current issue:
- Opening an old legacy task can trigger migration from `api_conversation_history.json` into SDK session persistence.
- This may read/translate/sanitize large histories and write new artifacts before the task is shown.
Potential fixes:
- Show legacy task immediately from existing UI messages if available, then migrate in background.
- Only migrate when the user resumes/continues the task, not when merely viewing.
- Cache migration status to avoid repeated checks.
- Optimize `migrateLegacyTaskIfNeeded` to avoid repeated full `readTaskHistory()` lookups.
Expected impact: much faster viewing of old tasks.
## 9. Batch or replace per-message webview pushes when opening a task
Current issue:
- `showTaskWithId` loops through loaded messages and awaits `pushMessageToWebview(msg)` for each message.
- Large tasks can involve many serialized webview messages.
Potential fixes:
- Send one batched message list for history load.
- Or rely on `postStateToWebview()` carrying `clineMessages` instead of pushing every message individually.
- If streaming individual messages is needed for UX, chunk them without awaiting each one serially.
Expected impact: faster task display for long conversations.
## 10. Navigate to chat earlier when a history item is clicked
Current issue:
- `src/core/controller/task/showTaskWithId.ts` sends the chat navigation event only after `controller.showTaskWithId()` completes.
- Backend slowness is experienced as staying stuck in History.
Potential fixes:
- Navigate immediately and show a loading state while messages load.
- Or have the webview navigate optimistically on click before awaiting RPC completion.
- Preserve error handling if loading fails.
Expected impact: improved perceived responsiveness.
## 11. Debounce History search and avoid duplicate reloads
Current issue:
- Search input updates `searchQuery` immediately.
- Search also changes `sortOption` to `mostRelevant`, which can trigger additional reloads.
Potential fixes:
- Debounce search queries.
- Coalesce search/sort state updates into one backend request.
- Cancel/ignore stale requests already exists via `historyRequestIdRef`, but backend work may still run.
Expected impact: fewer expensive history requests while typing.
## 12. Avoid expensive state history rebuilds after task open
Current issue:
- `postStateToWebview()` calls `getStateToPostToWebview()`.
- The SDK implementation then calls `this.taskHistory.listHistory({ limit: 100, hydrate: false })` again.
- This happens after loading a task from history.
Potential fixes:
- Reuse the already-found history item for `currentTaskItem`.
- Avoid rebuilding task history during task-open state post.
- Use cached history metadata from item 6.
Expected impact: reduces extra work after opening a task.
## 13. Add user-visible loading states and partial rendering
Current issue:
- Slow operations can look like the UI is frozen.
Potential fixes:
- Show a History loading skeleton immediately.
- Show total size later when available.
- Show chat task shell immediately, then progressively fill messages.
Expected impact: better perceived performance even before all backend optimizations are complete.
## Suggested implementation order
1. Add instrumentation.
2. Fix unbounded `showTaskWithId` lookup.
3. Stop initial History open from doing full folder size scans synchronously.
4. Reuse/cache history host or history metadata.
5. Optimize legacy migration/viewing.
6. Batch task message delivery.
7. Improve pagination/search/filtering.
8. Revisit hub lifecycle only after the above measurements.
@@ -1,4 +1,5 @@
import { EmptyRequest, Int64 } from "@shared/proto/cline/common"
import { Logger } from "@/shared/services/Logger"
import { getTotalTasksSize as calculateTotalTasksSize } from "../../../utils/storage"
import { Controller } from ".."
@@ -9,6 +10,8 @@ import { Controller } from ".."
* @returns The total size as an Int64 value
*/
export async function getTotalTasksSize(_controller: Controller, _request: EmptyRequest): Promise<Int64> {
const startedAt = Date.now()
const totalSize = await calculateTotalTasksSize()
Logger.log(`[HistoryPerf] getTotalTasksSize totalSize=${totalSize ?? 0} took ${Date.now() - startedAt}ms`)
return { value: totalSize || 0 }
}
+40 -14
View File
@@ -97,19 +97,18 @@ function dateStringToTimestamp(value: string | null | undefined): number {
return Number.isFinite(timestamp) ? timestamp : 0
}
function sdkHistoryRecordToTaskResponse(item: SessionHistoryRecord): TaskResponse {
const metadata = item.metadata
function historyItemToTaskResponse(item: HistoryItem): TaskResponse {
return TaskResponse.create({
id: item.sessionId,
task: metadataString(metadata, "title") ?? item.prompt ?? "",
ts: dateStringToTimestamp(item.updatedAt ?? item.endedAt ?? item.startedAt),
isFavorited: metadataBoolean(metadata, "isFavorited") ?? metadataBoolean(metadata, "is_favorited") ?? false,
size: metadataNumber(metadata, "size") ?? 0,
totalCost: metadataNumber(metadata, "totalCost") ?? 0,
tokensIn: metadataNumber(metadata, "tokensIn") ?? 0,
tokensOut: metadataNumber(metadata, "tokensOut") ?? 0,
cacheWrites: metadataNumber(metadata, "cacheWrites") ?? 0,
cacheReads: metadataNumber(metadata, "cacheReads") ?? 0,
id: item.id,
task: item.task,
ts: item.ts,
isFavorited: item.isFavorited ?? false,
size: item.size ?? 0,
totalCost: item.totalCost ?? 0,
tokensIn: item.tokensIn ?? 0,
tokensOut: item.tokensOut ?? 0,
cacheWrites: item.cacheWrites ?? 0,
cacheReads: item.cacheReads ?? 0,
})
}
@@ -788,13 +787,23 @@ export class Controller {
* 3. Only then push state to the webview
*/
async showTaskWithId(taskId: string): Promise<TaskResponse> {
const historyItem = (await this.taskHistory.listHistory()).find((item) => item.sessionId === taskId)
const startedAt = Date.now()
const lookupStartedAt = Date.now()
const historyItem = await this.taskHistory.findHistoryItem(taskId)
const lookupElapsed = Date.now() - lookupStartedAt
if (!historyItem) {
Logger.log(
`[HistoryPerf] SdkController.showTaskWithId taskId=${taskId} found=false targetedLookup=${lookupElapsed}ms total=${Date.now() - startedAt}ms`,
)
throw new Error(`Task not found in history: ${taskId}`)
}
const controlStartedAt = Date.now()
await this.taskControl.showTaskWithId(taskId, { skipHistoryLookup: true })
return sdkHistoryRecordToTaskResponse(historyItem)
Logger.log(
`[HistoryPerf] SdkController.showTaskWithId taskId=${taskId} targetedLookup=${lookupElapsed}ms control=${Date.now() - controlStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return historyItemToTaskResponse(historyItem)
}
// ---- Mode switching (Step 8) ----
@@ -921,11 +930,17 @@ export class Controller {
}
async getTaskHistory(request: GetTaskHistoryRequest): Promise<TaskHistoryArray> {
const startedAt = Date.now()
const { favoritesOnly, currentWorkspaceOnly, searchQuery, sortBy } = request
const limit = request.limit > 0 ? Math.min(request.limit, 100) : 50
const offset = request.offset > 0 ? request.offset : 0
const workspaceStartedAt = Date.now()
const workspacePath = currentWorkspaceOnly ? await this.getWorkspaceRoot() : undefined
const workspaceElapsed = Date.now() - workspaceStartedAt
const listStartedAt = Date.now()
const sessionHistory = await this.taskHistory.listHistory({ hydrate: false, limit: limit + 1, offset })
const listElapsed = Date.now() - listStartedAt
const transformStartedAt = Date.now()
let filteredTasks = sessionHistory.filter((item) => {
const ts = dateStringToTimestamp(item.updatedAt ?? item.endedAt ?? item.startedAt)
@@ -988,6 +1003,7 @@ export class Controller {
})
const hasMore = sessionHistory.length > limit
const mapStartedAt = Date.now()
const tasks = filteredTasks.slice(0, limit).map((item) => {
const metadata = item.metadata
return {
@@ -1005,6 +1021,9 @@ export class Controller {
}
})
Logger.log(
`[HistoryPerf] SdkController.getTaskHistory offset=${offset} limit=${limit} raw=${sessionHistory.length} filtered=${filteredTasks.length} tasks=${tasks.length} hasMore=${hasMore} workspace=${workspaceElapsed}ms list=${listElapsed}ms filterSortMap=${Date.now() - transformStartedAt}ms map=${Date.now() - mapStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return TaskHistoryArray.create({ tasks, hasMore })
}
@@ -1116,10 +1135,17 @@ export class Controller {
// ---- State management ----
async postStateToWebview(): Promise<void> {
const startedAt = Date.now()
// Import dynamically to avoid circular deps
const { sendStateUpdate } = await import("@core/controller/state/subscribeToState")
const stateStartedAt = Date.now()
const state = await this.getStateToPostToWebview()
const stateElapsed = Date.now() - stateStartedAt
const sendStartedAt = Date.now()
await sendStateUpdate(state)
Logger.log(
`[HistoryPerf] SdkController.postStateToWebview state=${stateElapsed}ms send=${Date.now() - sendStartedAt}ms total=${Date.now() - startedAt}ms`,
)
}
async getStateToPostToWebview(): Promise<ExtensionState> {
+19
View File
@@ -112,16 +112,23 @@ export class SdkTaskControlCoordinator {
}
async showTaskWithId(taskId: string, options: { skipHistoryLookup?: boolean } = {}): Promise<void> {
const startedAt = Date.now()
try {
if (!options.skipHistoryLookup) {
const lookupStartedAt = Date.now()
const historyItem = await this.options.taskHistory.findHistoryItem(taskId)
Logger.log(
`[HistoryPerf] SdkTaskControlCoordinator.showTaskWithId taskId=${taskId} historyLookup=${Date.now() - lookupStartedAt}ms`,
)
if (!historyItem) {
Logger.error(`[SdkController] Task not found in history: ${taskId}`)
return
}
}
const teardownStartedAt = Date.now()
this.silentlyTearDownActiveSession()
const teardownElapsed = Date.now() - teardownStartedAt
const currentTask = this.options.getTask()
if (currentTask) {
@@ -132,9 +139,13 @@ export class SdkTaskControlCoordinator {
// Load messages before installing the new task proxy so any concurrent
// postStateToWebview() caller never sees the new id with empty messages.
const loadMessagesStartedAt = Date.now()
const rawMessages = await this.options.taskHistory.getClineMessages(taskId)
const loadMessagesElapsed = Date.now() - loadMessagesStartedAt
const finalizeStartedAt = Date.now()
const messages = this.options.messages.finalizeMessagesForSave(rawMessages)
const cleanedMessages = messages.length > 0 ? this.appendFreshResumeMessage(messages) : []
const finalizeElapsed = Date.now() - finalizeStartedAt
const task = createTaskProxy(
taskId,
@@ -146,17 +157,25 @@ export class SdkTaskControlCoordinator {
}
this.options.setTask(task)
let pushElapsed = 0
if (cleanedMessages.length > 0) {
Logger.log(`[SdkController] Loaded ${cleanedMessages.length} messages for task: ${taskId}`)
const { pushMessageToWebview } = await import("./webview-grpc-bridge")
const pushStartedAt = Date.now()
for (const msg of cleanedMessages) {
await pushMessageToWebview(msg)
}
pushElapsed = Date.now() - pushStartedAt
} else {
Logger.log(`[SdkController] No messages found for task: ${taskId}`)
}
const postStateStartedAt = Date.now()
await this.options.postStateToWebview()
const postStateElapsed = Date.now() - postStateStartedAt
Logger.log(
`[HistoryPerf] SdkTaskControlCoordinator.showTaskWithId taskId=${taskId} rawMessages=${rawMessages.length} cleanedMessages=${cleanedMessages.length} teardown=${teardownElapsed}ms loadMessages=${loadMessagesElapsed}ms finalize=${finalizeElapsed}ms push=${pushElapsed}ms postState=${postStateElapsed}ms total=${Date.now() - startedAt}ms`,
)
Logger.log(`[SdkController] Showing task: ${taskId}`)
} catch (error) {
Logger.error("[SdkController] Failed to show task:", error)
+61 -6
View File
@@ -208,8 +208,10 @@ export class SdkTaskHistory {
return fn(activeHistoryHost)
}
const startedAt = Date.now()
const { VscodeSessionHost } = await import("./vscode-session-host")
const historyHost = await VscodeSessionHost.create({ mcpHub: this.options.mcpHub })
Logger.log(`[HistoryPerf] SdkTaskHistory.withHistoryHost created temp host in ${Date.now() - startedAt}ms`)
try {
return await fn(historyHost)
} finally {
@@ -220,59 +222,94 @@ export class SdkTaskHistory {
}
async listHistory(options: SdkTaskHistoryListOptions = {}): Promise<SessionHistoryRecord[]> {
const startedAt = Date.now()
const offset = Math.max(0, Math.floor(options.offset ?? 0))
const limit = Math.max(0, Math.floor(options.limit ?? 10_000))
const hostLimit = offset + limit
const hostOptions: ClineCoreListHistoryOptions = { ...options }
delete (hostOptions as { offset?: number }).offset
const hostStartedAt = Date.now()
const sdkHistory = await this.withHistoryHost((host) =>
host.listHistory({ ...hostOptions, limit: hostLimit || 10_000, includeManifestFallback: true }),
)
const hostElapsed = Date.now() - hostStartedAt
const mergeStartedAt = Date.now()
const visibleSdkHistory = sdkHistory.filter((item) => item.isSubagent !== true)
const sdkIds = new Set(visibleSdkHistory.map((item) => item.sessionId))
const legacyHistory = readTaskHistory()
.filter((item) => item.id && item.task && !sdkIds.has(item.id))
.map(historyItemToSessionHistoryRecord)
return [...visibleSdkHistory, ...legacyHistory]
const result = [...visibleSdkHistory, ...legacyHistory]
.sort(
(a, b) =>
dateStringToTimestamp(b.updatedAt ?? b.endedAt ?? b.startedAt) -
dateStringToTimestamp(a.updatedAt ?? a.endedAt ?? a.startedAt),
)
.slice(offset, offset + limit)
Logger.log(
`[HistoryPerf] SdkTaskHistory.listHistory offset=${offset} limit=${limit} hydrate=${options.hydrate !== false} sdk=${sdkHistory.length} visibleSdk=${visibleSdkHistory.length} legacy=${legacyHistory.length} result=${result.length} host=${hostElapsed}ms mergeSort=${Date.now() - mergeStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return result
}
async getClineMessages(taskId: string): Promise<ClineMessage[]> {
await this.migrateLegacyTaskIfNeeded(taskId)
const startedAt = Date.now()
const migrateStartedAt = Date.now()
const migrated = await this.migrateLegacyTaskIfNeeded(taskId)
const migrateElapsed = Date.now() - migrateStartedAt
const readStartedAt = Date.now()
const sdkMessages = await this.withHistoryHost((host) => host.readMessages(taskId) as Promise<SdkMessage[]>)
return sdkMessagesToClineMessages(sdkMessages)
const readElapsed = Date.now() - readStartedAt
const translateStartedAt = Date.now()
const clineMessages = sdkMessagesToClineMessages(sdkMessages)
Logger.log(
`[HistoryPerf] SdkTaskHistory.getClineMessages taskId=${taskId} migrated=${migrated} sdkMessages=${sdkMessages.length} clineMessages=${clineMessages.length} migrate=${migrateElapsed}ms read=${readElapsed}ms translate=${Date.now() - translateStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return clineMessages
}
private async migrateLegacyTaskIfNeeded(taskId: string): Promise<boolean> {
const startedAt = Date.now()
return this.withHistoryHost(async (host) => {
try {
const existing = await host.get(taskId)
if (existing) {
Logger.log(
`[HistoryPerf] SdkTaskHistory.migrateLegacyTaskIfNeeded taskId=${taskId} existingSdk=true total=${Date.now() - startedAt}ms`,
)
return false
}
} catch (error) {
Logger.warn(`[SdkTaskHistory] Failed to check SDK session before legacy migration: ${taskId}`, error)
}
const legacyLookupStartedAt = Date.now()
const historyItem = readTaskHistory().find((item) => item.id === taskId)
if (!historyItem) {
Logger.log(
`[HistoryPerf] SdkTaskHistory.migrateLegacyTaskIfNeeded taskId=${taskId} legacyFound=false legacyLookup=${Date.now() - legacyLookupStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return false
}
const legacyReadStartedAt = Date.now()
const legacyApiHistory = readApiConversationHistory(taskId)
if (legacyApiHistory.length === 0) {
Logger.log(
`[HistoryPerf] SdkTaskHistory.migrateLegacyTaskIfNeeded taskId=${taskId} legacyMessages=0 legacyRead=${Date.now() - legacyReadStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return false
}
const translateStartedAt = Date.now()
const initialMessages = legacyApiHistoryToSdkMessages(legacyApiHistory, historyItem)
const translateElapsed = Date.now() - translateStartedAt
if (initialMessages.length === 0) {
Logger.log(
`[HistoryPerf] SdkTaskHistory.migrateLegacyTaskIfNeeded taskId=${taskId} translatedMessages=0 legacyMessages=${legacyApiHistory.length} translate=${translateElapsed}ms total=${Date.now() - startedAt}ms`,
)
return false
}
@@ -280,6 +317,7 @@ export class SdkTaskHistory {
const config = await buildSessionConfig({ cwd, workspaceRoot: cwd, mode: "act" })
config.sessionId = taskId
const startStartedAt = Date.now()
await host.start({
config,
prompt: undefined,
@@ -300,6 +338,9 @@ export class SdkTaskHistory {
})
Logger.log(`[SdkTaskHistory] Migrated legacy task to SDK session: ${taskId}`)
Logger.log(
`[HistoryPerf] SdkTaskHistory.migrateLegacyTaskIfNeeded taskId=${taskId} legacyMessages=${legacyApiHistory.length} initialMessages=${initialMessages.length} translate=${translateElapsed}ms start=${Date.now() - startStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return true
})
}
@@ -332,9 +373,23 @@ export class SdkTaskHistory {
}
async findHistoryItem(taskId: string): Promise<HistoryItem | undefined> {
const history = await this.listHistory()
const item = history.find((candidate) => candidate.sessionId === taskId)
return item ? sessionHistoryRecordToHistoryItem(item) : undefined
const startedAt = Date.now()
const sdkLookupStartedAt = Date.now()
const sdkRecord = await this.withHistoryHost((host) => host.get(taskId))
const sdkLookupElapsed = Date.now() - sdkLookupStartedAt
if (sdkRecord && sdkRecord.isSubagent !== true) {
Logger.log(
`[HistoryPerf] SdkTaskHistory.findHistoryItem taskId=${taskId} source=sdk sdkLookup=${sdkLookupElapsed}ms total=${Date.now() - startedAt}ms`,
)
return sessionHistoryRecordToHistoryItem(sdkRecord as SessionHistoryRecord)
}
const legacyLookupStartedAt = Date.now()
const legacyItem = readTaskHistory().find((item) => item.id === taskId)
Logger.log(
`[HistoryPerf] SdkTaskHistory.findHistoryItem taskId=${taskId} source=${legacyItem ? "legacy" : "missing"} sdkLookup=${sdkLookupElapsed}ms legacyLookup=${Date.now() - legacyLookupStartedAt}ms total=${Date.now() - startedAt}ms`,
)
return legacyItem
}
async deleteTaskFromState(id: string): Promise<HistoryItem[]> {
+2
View File
@@ -70,6 +70,7 @@ export class VscodeSessionHost implements SdkSessionHost {
}
static async create(options: VscodeSessionHostOptions): Promise<VscodeSessionHost> {
const startedAt = Date.now()
// Build tool executor capabilities from options — only include keys that are provided.
// When a terminal manager is available, suppress the SDK's built-in run_commands
// tool by setting bash to undefined. Our custom run_commands (provided via
@@ -117,6 +118,7 @@ export class VscodeSessionHost implements SdkSessionHost {
}),
})
Logger.log(`[HistoryPerf] VscodeSessionHost.create took ${Date.now() - startedAt}ms`)
Logger.log("[VscodeSessionHost] Initialized with ClineCore + VSCode extra tools")
if (options.getTerminalManager) {
Logger.log("[VscodeSessionHost] SDK run_commands suppressed; using custom foreground/background terminal tool")
@@ -70,6 +70,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
isLoadingHistoryRef.current = true
setIsLoadingHistory(true)
try {
const startedAt = performance.now()
const response = await TaskServiceClient.getTaskHistory(
GetTaskHistoryRequest.create({
favoritesOnly: showFavoritesOnly,
@@ -80,6 +81,9 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
offset,
}),
)
console.log(
`[HistoryPerf] getTaskHistory offset=${offset} tasks=${response.tasks?.length ?? 0} hasMore=${response.hasMore} took ${Math.round(performance.now() - startedAt)}ms`,
)
if (requestId !== historyRequestIdRef.current) {
return
}
@@ -174,7 +178,9 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
const fetchTotalTasksSize = useCallback(async () => {
try {
const startedAt = performance.now()
const response = await TaskServiceClient.getTotalTasksSize(EmptyRequest.create({}))
console.log(`[HistoryPerf] getTotalTasksSize took ${Math.round(performance.now() - startedAt)}ms`)
if (response && typeof response.value === "number") {
setTotalTasksSize?.(response.value || 0)
}