diff --git a/apps/code/src/main/trpc/trpc.ts b/apps/code/src/main/trpc/trpc.ts index 96e6ad3c5..32992a377 100644 --- a/apps/code/src/main/trpc/trpc.ts +++ b/apps/code/src/main/trpc/trpc.ts @@ -1,4 +1,5 @@ import { initTRPC } from "@trpc/server"; +import log from "electron-log/main"; const trpc = initTRPC.create({ isServer: true, @@ -9,28 +10,49 @@ const CALL_RATE_THRESHOLD = 50; const callCounts: Record = {}; -const callRateMonitor = trpc.middleware(async ({ path, next }) => { - if (process.env.NODE_ENV !== "development") { - return next(); - } +const ipcTimingEnabled = process.env.IPC_TIMINGS === "true"; +const ipcTimingBootMs = 15_000; +const bootTime = Date.now(); - const now = Date.now(); - if (!callCounts[path]) { - callCounts[path] = []; - } +const callRateMonitor = trpc.middleware(async ({ path, next, type }) => { + const shouldTime = + ipcTimingEnabled && Date.now() - bootTime < ipcTimingBootMs; + const t = shouldTime ? performance.now() : 0; - const timestamps = callCounts[path]; - timestamps.push(now); + if (shouldTime) { + log.info(`[ipc-timing] >> ${type} ${path}`); + } - const cutoff = now - CALL_RATE_WINDOW_MS; - while (timestamps.length > 0 && timestamps[0] < cutoff) { - timestamps.shift(); + if (process.env.NODE_ENV === "development") { + const now = Date.now(); + if (!callCounts[path]) { + callCounts[path] = []; + } + + const timestamps = callCounts[path]; + timestamps.push(now); + + const cutoff = now - CALL_RATE_WINDOW_MS; + while (timestamps.length > 0 && timestamps[0] < cutoff) { + timestamps.shift(); + } + + if (timestamps.length >= CALL_RATE_THRESHOLD) { + log.warn( + `[ipc-rate] ${type} ${path} called ${timestamps.length} times in ${CALL_RATE_WINDOW_MS}ms`, + ); + } } - if (timestamps.length >= CALL_RATE_THRESHOLD) { + const result = await next(); + + if (shouldTime) { + log.info( + `[ipc-timing] << ${type} ${path}: ${(performance.now() - t).toFixed(0)}ms`, + ); } - return next(); + return result; }); export const router = trpc.router; diff --git a/apps/code/src/renderer/features/command-center/components/CommandCenterView.tsx b/apps/code/src/renderer/features/command-center/components/CommandCenterView.tsx index 76eb6f9e3..1eaf69cda 100644 --- a/apps/code/src/renderer/features/command-center/components/CommandCenterView.tsx +++ b/apps/code/src/renderer/features/command-center/components/CommandCenterView.tsx @@ -13,16 +13,17 @@ export function CommandCenterView() { const { cells, summary } = useCommandCenterData(); const { markAsViewed } = useTaskViewed(); - const visibleTaskIds = useMemo( - () => cells.map((c) => c.taskId).filter((id): id is string => id != null), - [cells], - ); + const visibleTaskIdsKey = cells + .map((c) => c.taskId) + .filter(Boolean) + .join(","); useEffect(() => { - for (const taskId of visibleTaskIds) { + if (!visibleTaskIdsKey) return; + for (const taskId of visibleTaskIdsKey.split(",")) { markAsViewed(taskId); } - }, [visibleTaskIds, markAsViewed]); + }, [visibleTaskIdsKey, markAsViewed]); const headerContent = useMemo( () => ( diff --git a/apps/code/src/renderer/features/sessions/hooks/useSessionConnection.ts b/apps/code/src/renderer/features/sessions/hooks/useSessionConnection.ts index 8fd117c77..f8afc734d 100644 --- a/apps/code/src/renderer/features/sessions/hooks/useSessionConnection.ts +++ b/apps/code/src/renderer/features/sessions/hooks/useSessionConnection.ts @@ -11,6 +11,7 @@ import { useChatTitleGenerator } from "./useChatTitleGenerator"; const log = logger.scope("session-connection"); const connectingTasks = new Set(); +const activityRecorded = new Set(); interface UseSessionConnectionOptions { taskId: string; @@ -37,14 +38,20 @@ export function useSessionConnection({ useEffect(() => { const taskRunId = session?.taskRunId; if (!taskRunId) return; - trpcClient.agent.recordActivity.mutate({ taskRunId }).catch(() => {}); + if (!activityRecorded.has(taskRunId)) { + activityRecorded.add(taskRunId); + trpcClient.agent.recordActivity.mutate({ taskRunId }).catch(() => {}); + } const heartbeat = setInterval( () => { trpcClient.agent.recordActivity.mutate({ taskRunId }).catch(() => {}); }, 5 * 60 * 1000, ); - return () => clearInterval(heartbeat); + return () => { + clearInterval(heartbeat); + activityRecorded.delete(taskRunId); + }; }, [session?.taskRunId]); useEffect(() => {