diff --git a/build.ts b/build.ts index 4854fd51cf..6cb8e73eca 100644 --- a/build.ts +++ b/build.ts @@ -21,7 +21,13 @@ const result = await Bun.build({ outdir, target: 'bun', splitting: true, - define: getMacroDefines(), + define: { + ...getMacroDefines(), + // React production mode — eliminates _debugStack Error objects + // (6,889 objects × ~1.7KB = 12MB in development builds) and removes + // prop-type / key warnings not useful in a production CLI tool. + 'process.env.NODE_ENV': JSON.stringify('production'), + }, features, }) diff --git a/scripts/dev.ts b/scripts/dev.ts index 452cf7ea2e..72919caa8c 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -14,7 +14,12 @@ const __dirname = dirname(__filename) const projectRoot = join(__dirname, '..') const cliPath = join(projectRoot, 'src/entrypoints/cli.tsx') -const defines = getMacroDefines() +const defines = { + ...getMacroDefines(), + // React production mode — prevents 6,889+ _debugStack Error objects + // (12MB) from accumulating during long-running sessions. + 'process.env.NODE_ENV': JSON.stringify('production'), +} const defineArgs = Object.entries(defines).flatMap(([k, v]) => [ '-d', diff --git a/src/QueryEngine.ts b/src/QueryEngine.ts index de50ce3c11..b53257e32c 100644 --- a/src/QueryEngine.ts +++ b/src/QueryEngine.ts @@ -41,11 +41,7 @@ import { type Tools, type ToolUseContext, toolMatchesName } from './Tool.js' import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js' import { SYNTHETIC_OUTPUT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/SyntheticOutputTool/SyntheticOutputTool.js' import type { APIError } from '@anthropic-ai/sdk' -import type { - CompactMetadata, - Message, - SystemCompactBoundaryMessage, -} from './types/message.js' +import type { Message, SystemCompactBoundaryMessage } from './types/message.js' import type { OrphanedPermission } from './types/textInputTypes.js' import { createAbortController } from './utils/abortController.js' import type { AttributionState } from './utils/commitAttribution.js' diff --git a/src/assistant/gate.ts b/src/assistant/gate.ts index 0cf8f0c4b6..c6bc6ff7c2 100644 --- a/src/assistant/gate.ts +++ b/src/assistant/gate.ts @@ -1,5 +1,4 @@ import { feature } from 'bun:bundle' -import { getKairosActive } from '../bootstrap/state.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' /** diff --git a/src/bridge/bridgeMain.ts b/src/bridge/bridgeMain.ts index feb3da8b8a..77e0373ae3 100644 --- a/src/bridge/bridgeMain.ts +++ b/src/bridge/bridgeMain.ts @@ -11,7 +11,6 @@ import { logEvent, logEventAsync, } from '../services/analytics/index.js' -import { isInBundledMode } from '../utils/bundledMode.js' import { getBootstrapArgs, getScriptPath } from '../utils/cliLaunch.js' import { logForDebugging } from '../utils/debug.js' import { rcLog } from './rcDebugLog.js' diff --git a/src/bridge/remoteBridgeCore.ts b/src/bridge/remoteBridgeCore.ts index 40ddbbef29..69abccae52 100644 --- a/src/bridge/remoteBridgeCore.ts +++ b/src/bridge/remoteBridgeCore.ts @@ -72,7 +72,6 @@ import type { SDKControlResponse, } from '../entrypoints/sdk/controlTypes.js' import type { StdoutMessage } from '../entrypoints/sdk/controlTypes.js' -import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js' import type { PermissionMode } from '../utils/permissions/PermissionMode.js' import { setSessionMetadataChangedListener } from '../utils/sessionState.js' diff --git a/src/bridge/replBridge.ts b/src/bridge/replBridge.ts index f7c323bff9..03f07e49e5 100644 --- a/src/bridge/replBridge.ts +++ b/src/bridge/replBridge.ts @@ -55,7 +55,6 @@ import type { SDKControlResponse, } from '../entrypoints/sdk/controlTypes.js' import type { StdoutMessage } from '../entrypoints/sdk/controlTypes.js' -import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js' /** * StdoutMessage with optional session_id. The transport layer accepts diff --git a/src/buddy/CompanionCard.tsx b/src/buddy/CompanionCard.tsx index 1f571605c7..fb1a2c591d 100644 --- a/src/buddy/CompanionCard.tsx +++ b/src/buddy/CompanionCard.tsx @@ -2,7 +2,6 @@ * Companion display card — shown by /buddy (no args). * Mirrors official vc8 component: bordered box with sprite, stats, last reaction. */ -import React from 'react'; import { Box, Text } from '@anthropic/ink'; import { useInput } from '@anthropic/ink'; import { renderSprite } from './sprites.js'; diff --git a/src/cli/handlers/mcp.tsx b/src/cli/handlers/mcp.tsx index 95fb15de9f..7557f0cba8 100644 --- a/src/cli/handlers/mcp.tsx +++ b/src/cli/handlers/mcp.tsx @@ -6,7 +6,6 @@ import { stat } from 'fs/promises'; import pMap from 'p-map'; import { cwd } from 'process'; -import React from 'react'; import { MCPServerDesktopImportDialog } from '../../components/MCPServerDesktopImportDialog.js'; import { wrappedRender as render } from '@anthropic/ink'; import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js'; diff --git a/src/cli/print.ts b/src/cli/print.ts index 84b19a120f..7a9ca405c8 100644 --- a/src/cli/print.ts +++ b/src/cli/print.ts @@ -112,7 +112,6 @@ import type { ModelInfo, SDKMessage, SDKUserMessage, - SDKUserMessageReplay, PermissionResult, McpServerConfigForProcessTransport, McpServerStatus, @@ -5477,7 +5476,7 @@ function getStructuredIO( */ export async function handleOrphanedPermissionResponse({ message, - setAppState, + setAppState: _setAppState, onEnqueued, handledToolUseIds, }: { diff --git a/src/commands/assistant/assistant.tsx b/src/commands/assistant/assistant.tsx index 91f75542a4..5deb30786d 100644 --- a/src/commands/assistant/assistant.tsx +++ b/src/commands/assistant/assistant.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { resolve } from 'path'; import { Box, Text } from '@anthropic/ink'; import { Dialog } from '../../components/design-system/Dialog.js'; diff --git a/src/commands/config/config.tsx b/src/commands/config/config.tsx index 95796286cd..01cbcf1bee 100644 --- a/src/commands/config/config.tsx +++ b/src/commands/config/config.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Settings } from '../../components/Settings/Settings.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; diff --git a/src/commands/diff/diff.tsx b/src/commands/diff/diff.tsx index b867646480..bcd0009248 100644 --- a/src/commands/diff/diff.tsx +++ b/src/commands/diff/diff.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import type { LocalJSXCommandCall } from '../../types/command.js'; export const call: LocalJSXCommandCall = async (onDone, context) => { diff --git a/src/commands/doctor/doctor.tsx b/src/commands/doctor/doctor.tsx index aedbc407da..e5b772fc81 100644 --- a/src/commands/doctor/doctor.tsx +++ b/src/commands/doctor/doctor.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Doctor } from '../../screens/Doctor.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; diff --git a/src/commands/help/help.tsx b/src/commands/help/help.tsx index b2310c13f2..6b05a6bb9b 100644 --- a/src/commands/help/help.tsx +++ b/src/commands/help/help.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { HelpV2 } from '../../components/HelpV2/HelpV2.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; diff --git a/src/commands/hooks/hooks.tsx b/src/commands/hooks/hooks.tsx index cabdea254a..9b5a1c3489 100644 --- a/src/commands/hooks/hooks.tsx +++ b/src/commands/hooks/hooks.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { HooksConfigMenu } from '../../components/hooks/HooksConfigMenu.js'; import { logEvent } from '../../services/analytics/index.js'; import { getTools } from '../../tools.js'; diff --git a/src/commands/install-github-app/ApiKeyStep.tsx b/src/commands/install-github-app/ApiKeyStep.tsx index 66055677fa..f6676a2778 100644 --- a/src/commands/install-github-app/ApiKeyStep.tsx +++ b/src/commands/install-github-app/ApiKeyStep.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import { useCallback, useState } from 'react'; import TextInput from '../../components/TextInput.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Box, color, Text, useTheme } from '@anthropic/ink'; diff --git a/src/commands/install-github-app/CheckExistingSecretStep.tsx b/src/commands/install-github-app/CheckExistingSecretStep.tsx index 9fb66320be..7ac6db7869 100644 --- a/src/commands/install-github-app/CheckExistingSecretStep.tsx +++ b/src/commands/install-github-app/CheckExistingSecretStep.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import { useCallback, useState } from 'react'; import TextInput from '../../components/TextInput.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Box, color, Text, useTheme } from '@anthropic/ink'; diff --git a/src/commands/install-github-app/CheckGitHubStep.tsx b/src/commands/install-github-app/CheckGitHubStep.tsx index 1f4fc95213..e2c38fe0b1 100644 --- a/src/commands/install-github-app/CheckGitHubStep.tsx +++ b/src/commands/install-github-app/CheckGitHubStep.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Text } from '@anthropic/ink'; export function CheckGitHubStep() { diff --git a/src/commands/install-github-app/ChooseRepoStep.tsx b/src/commands/install-github-app/ChooseRepoStep.tsx index 634b539ae8..87b05457c8 100644 --- a/src/commands/install-github-app/ChooseRepoStep.tsx +++ b/src/commands/install-github-app/ChooseRepoStep.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import { useCallback, useState } from 'react'; import TextInput from '../../components/TextInput.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Box, Text } from '@anthropic/ink'; diff --git a/src/commands/install-github-app/CreatingStep.tsx b/src/commands/install-github-app/CreatingStep.tsx index 48f2eac082..be5b671ca0 100644 --- a/src/commands/install-github-app/CreatingStep.tsx +++ b/src/commands/install-github-app/CreatingStep.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Box, Text } from '@anthropic/ink'; import type { Workflow } from './types.js'; diff --git a/src/commands/install-github-app/ErrorStep.tsx b/src/commands/install-github-app/ErrorStep.tsx index 19f9a30e21..4edcb02584 100644 --- a/src/commands/install-github-app/ErrorStep.tsx +++ b/src/commands/install-github-app/ErrorStep.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'; import { Box, Text } from '@anthropic/ink'; diff --git a/src/commands/install-github-app/ExistingWorkflowStep.tsx b/src/commands/install-github-app/ExistingWorkflowStep.tsx index 2a11e6adf6..ff7a86057f 100644 --- a/src/commands/install-github-app/ExistingWorkflowStep.tsx +++ b/src/commands/install-github-app/ExistingWorkflowStep.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Select } from 'src/components/CustomSelect/index.js'; import { Box, Text } from '@anthropic/ink'; diff --git a/src/commands/install-github-app/InstallAppStep.tsx b/src/commands/install-github-app/InstallAppStep.tsx index 2ba78cc3cd..f9a98c63e1 100644 --- a/src/commands/install-github-app/InstallAppStep.tsx +++ b/src/commands/install-github-app/InstallAppStep.tsx @@ -1,5 +1,4 @@ import figures from 'figures'; -import React from 'react'; import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'; import { Box, Text } from '@anthropic/ink'; import { useKeybinding } from '../../keybindings/useKeybinding.js'; diff --git a/src/commands/install-github-app/WarningsStep.tsx b/src/commands/install-github-app/WarningsStep.tsx index 3d9bc62429..bfd080b5cd 100644 --- a/src/commands/install-github-app/WarningsStep.tsx +++ b/src/commands/install-github-app/WarningsStep.tsx @@ -1,5 +1,4 @@ import figures from 'figures'; -import React from 'react'; import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'; import { Box, Text } from '@anthropic/ink'; import { useKeybinding } from '../../keybindings/useKeybinding.js'; diff --git a/src/commands/login/login.tsx b/src/commands/login/login.tsx index b8ee0862a7..961bf40895 100644 --- a/src/commands/login/login.tsx +++ b/src/commands/login/login.tsx @@ -1,4 +1,3 @@ -import { feature } from 'bun:bundle'; import * as React from 'react'; import { resetCostState } from '../../bootstrap/state.js'; import { clearTrustedDeviceToken, enrollTrustedDevice } from '../../bridge/trustedDevice.js'; diff --git a/src/commands/permissions/permissions.tsx b/src/commands/permissions/permissions.tsx index 83676488a5..770316883e 100644 --- a/src/commands/permissions/permissions.tsx +++ b/src/commands/permissions/permissions.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { PermissionRuleList } from '../../components/permissions/rules/PermissionRuleList.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; import { createPermissionRetryMessage } from '../../utils/messages.js'; diff --git a/src/commands/provider.ts b/src/commands/provider.ts index c2d43163f6..5d12f74573 100644 --- a/src/commands/provider.ts +++ b/src/commands/provider.ts @@ -36,7 +36,7 @@ function getMergedEnv(): Record { return merged } -const call: LocalCommandCall = async (args, context) => { +const call: LocalCommandCall = async (args, _context) => { const arg = args.trim().toLowerCase() // No argument: show current provider diff --git a/src/commands/remoteControlServer/remoteControlServer.tsx b/src/commands/remoteControlServer/remoteControlServer.tsx index c586b9484b..398870c255 100644 --- a/src/commands/remoteControlServer/remoteControlServer.tsx +++ b/src/commands/remoteControlServer/remoteControlServer.tsx @@ -2,7 +2,7 @@ import { type ChildProcess } from 'child_process'; import { resolve } from 'path'; import * as React from 'react'; import { useEffect, useState } from 'react'; -import { getBridgeDisabledReason, isBridgeEnabled } from '../../bridge/bridgeEnabled.js'; +import { getBridgeDisabledReason } from '../../bridge/bridgeEnabled.js'; import { getBridgeAccessToken } from '../../bridge/bridgeConfig.js'; import { BRIDGE_LOGIN_INSTRUCTION } from '../../bridge/types.js'; import { Dialog } from '../../components/design-system/Dialog.js'; diff --git a/src/commands/review/ultrareviewCommand.tsx b/src/commands/review/ultrareviewCommand.tsx index ab1004dcc1..46d53a47ad 100644 --- a/src/commands/review/ultrareviewCommand.tsx +++ b/src/commands/review/ultrareviewCommand.tsx @@ -1,5 +1,4 @@ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js'; -import React from 'react'; import type { LocalJSXCommandCall, LocalJSXCommandOnDone } from '../../types/command.js'; import { checkOverageGate, confirmOverage, launchRemoteReview } from './reviewRemote.js'; import { UltrareviewOverageDialog } from './UltrareviewOverageDialog.js'; diff --git a/src/commands/skill-learning/skill-learning.ts b/src/commands/skill-learning/skill-learning.ts index febb0a8332..1cbbd78958 100644 --- a/src/commands/skill-learning/skill-learning.ts +++ b/src/commands/skill-learning/skill-learning.ts @@ -9,7 +9,6 @@ import { exportInstincts, findPromotionCandidates, generateSkillCandidates, - importInstincts, ingestTranscript, listKnownProjects, loadInstincts, diff --git a/src/commands/stats/stats.tsx b/src/commands/stats/stats.tsx index 86110777ee..5ba8e8302d 100644 --- a/src/commands/stats/stats.tsx +++ b/src/commands/stats/stats.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Stats } from '../../components/Stats.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; diff --git a/src/commands/ultraplan.tsx b/src/commands/ultraplan.tsx index fc1920e562..2a7e2ee85e 100644 --- a/src/commands/ultraplan.tsx +++ b/src/commands/ultraplan.tsx @@ -1,4 +1,3 @@ -import { readFileSync } from 'fs'; import { REMOTE_CONTROL_DISCONNECTED_MSG } from '../bridge/types.js'; import type { Command } from '../commands.js'; import { DIAMOND_OPEN } from '../constants/figures.js'; @@ -21,7 +20,6 @@ import { logForDebugging } from '../utils/debug.js'; import { errorMessage } from '../utils/errors.js'; import { logError } from '../utils/log.js'; import { enqueuePendingNotification } from '../utils/messageQueueManager.js'; -import { ALL_MODEL_CONFIGS } from '../utils/model/configs.js'; import { updateTaskState } from '../utils/task/framework.js'; import { archiveRemoteSession, teleportToRemote } from '../utils/teleport.js'; import { pollForApprovedExitPlanMode, UltraplanPollError } from '../utils/ultraplan/ccrSession.js'; @@ -36,13 +34,6 @@ import { registerCleanup } from '../utils/cleanupRegistry.js'; // TODO(prod-hardening): OAuth token may go stale over the 30min poll; // consider refresh. -/** - * Multi-agent exploration is slow; 30min timeout. - * - * @deprecated use getUltraplanTimeoutMs() - */ -const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000; - export const CCR_TERMS_URL = 'https://code.claude.com/docs/en/claude-code-on-the-web'; export function getUltraplanTimeoutMs(): number { @@ -61,15 +52,6 @@ export function isUltraplanEnabled(): boolean { ); } -// CCR runs against the first-party API — use the canonical ID, not the -// provider-specific string getModelStrings() would return (which may be a -// Bedrock ARN or Vertex ID on the local CLI). Read at call time, not module -// load: the GrowthBook cache is empty at import and `/config` Gates can flip -// it between invocations. -function getUltraplanModel(): string { - return getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_model', ALL_MODEL_CONFIGS.opus47.firstParty); -} - // prompt.txt is wrapped in so the CCR browser hides // scaffolding (CLI_BLOCK_TAGS dropped by stripSystemNotifications) // while the model still sees full text. @@ -84,19 +66,6 @@ const _rawPrompt = require('../utils/ultraplan/prompt.txt'); /* eslint-enable @typescript-eslint/no-require-imports */ const DEFAULT_INSTRUCTIONS: string = (typeof _rawPrompt === 'string' ? _rawPrompt : _rawPrompt.default).trimEnd(); -// Dev-only prompt override resolved eagerly at module load. -// Gated to ant builds (USER_TYPE is a build-time define, -// so the override path is DCE'd from external builds). -// Shell-set env only, so top-level process.env read is fine -// — settings.env never injects this. -// @deprecated use buildUltraplanPrompt() -/* eslint-disable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs -- ant-only dev override; eager top-level read is the point (crash at startup, not silently inside the slash-command try/catch) */ -const ULTRAPLAN_INSTRUCTIONS: string = - process.env.USER_TYPE === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE - ? readFileSync(process.env.ULTRAPLAN_PROMPT_FILE, 'utf8').trimEnd() - : DEFAULT_INSTRUCTIONS; -/* eslint-enable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs */ - /** * Assemble the initial CCR user message. seedPlan and blurb stay outside the * system-reminder so the browser renders them; scaffolding is hidden. diff --git a/src/commands/usage/usage.tsx b/src/commands/usage/usage.tsx index 94e2711878..9ba06c6ab1 100644 --- a/src/commands/usage/usage.tsx +++ b/src/commands/usage/usage.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Settings } from '../../components/Settings/Settings.js'; import type { LocalJSXCommandCall } from '../../types/command.js'; diff --git a/src/components/BuiltinStatusLine.tsx b/src/components/BuiltinStatusLine.tsx deleted file mode 100644 index 733d5574b0..0000000000 --- a/src/components/BuiltinStatusLine.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { formatCost } from '../cost-tracker.js'; -import { Box, Text, ProgressBar } from '@anthropic/ink'; -import { formatTokens } from '../utils/format.js'; -import { useTerminalSize } from '../hooks/useTerminalSize.js'; - -type RateLimitBucket = { - utilization: number; - resets_at: number; -}; - -type BuiltinStatusLineProps = { - modelName: string; - contextUsedPct: number; - usedTokens: number; - contextWindowSize: number; - totalCostUsd: number; - rateLimits: { - five_hour?: RateLimitBucket; - seven_day?: RateLimitBucket; - }; -}; - -/** - * Format a countdown from now until the given epoch time (in seconds). - * Returns a compact human-readable string like "3h12m", "5d20h", "45m", or "now". - */ -export function formatCountdown(epochSeconds: number): string { - const diff = epochSeconds - Date.now() / 1000; - if (diff <= 0) return 'now'; - - const days = Math.floor(diff / 86400); - const hours = Math.floor((diff % 86400) / 3600); - const minutes = Math.floor((diff % 3600) / 60); - - if (days >= 1) return `${days}d${hours}h`; - if (hours >= 1) return `${hours}h${minutes}m`; - return `${minutes}m`; -} - -function Separator() { - return {' \u2502 '}; -} - -function BuiltinStatusLineInner({ - modelName, - contextUsedPct, - usedTokens, - contextWindowSize, - totalCostUsd, - rateLimits, -}: BuiltinStatusLineProps) { - const { columns } = useTerminalSize(); - - // Force re-render every 60s so countdowns stay current - const [tick, setTick] = useState(0); - useEffect(() => { - const hasResetTime = (rateLimits.five_hour?.resets_at ?? 0) || (rateLimits.seven_day?.resets_at ?? 0); - if (!hasResetTime) return; - const id = setInterval(() => setTick(t => t + 1), 60_000); - return () => clearInterval(id); - }, [rateLimits.five_hour?.resets_at, rateLimits.seven_day?.resets_at]); - - // Suppress unused-variable lint for tick (it exists only to trigger re-renders) - void tick; - - // Model display: use first two words (e.g. "Opus 4.6") instead of just first word - const modelParts = modelName.split(' '); - const shortModel = modelParts.length >= 2 ? `${modelParts[0]} ${modelParts[1]}` : modelName; - - const wide = columns >= 100; - const narrow = columns < 60; - - const hasFiveHour = rateLimits.five_hour != null; - const hasSevenDay = rateLimits.seven_day != null; - - const fiveHourPct = hasFiveHour ? Math.round(rateLimits.five_hour!.utilization * 100) : 0; - const sevenDayPct = hasSevenDay ? Math.round(rateLimits.seven_day!.utilization * 100) : 0; - - // Token display: "50k/1M" - const tokenDisplay = `${formatTokens(usedTokens)}/${formatTokens(contextWindowSize)}`; - - return ( - - {/* Model name */} - {shortModel} - - {/* Context usage with token counts */} - - Context - {contextUsedPct}% - {!narrow && ({tokenDisplay})} - - {/* 5-hour session rate limit */} - {hasFiveHour && ( - <> - - Session - {wide && ( - <> - - - - )} - {fiveHourPct}% - {!narrow && rateLimits.five_hour!.resets_at > 0 && ( - {formatCountdown(rateLimits.five_hour!.resets_at)} - )} - - )} - - {/* 7-day weekly rate limit */} - {hasSevenDay && ( - <> - - Weekly - {wide && ( - <> - - - - )} - {sevenDayPct}% - {!narrow && rateLimits.seven_day!.resets_at > 0 && ( - {formatCountdown(rateLimits.seven_day!.resets_at)} - )} - - )} - - {/* Cost */} - {totalCostUsd > 0 && ( - <> - - {formatCost(totalCostUsd)} - - )} - - ); -} - -export const BuiltinStatusLine = React.memo(BuiltinStatusLineInner); diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 4f53dbc61b..9ca4641b3c 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -11,12 +11,12 @@ import { getSSLErrorHint } from '@ant/model-provider'; import { sendNotification } from '../services/notifier.js'; import { OAuthService } from '../services/oauth/index.js'; import { getOauthAccountInfo, validateForceLoginOrg } from '../utils/auth.js'; + import { logError } from '../utils/log.js'; import { getSettings_DEPRECATED, updateSettingsForSource } from '../utils/settings/settings.js'; import { Select } from './CustomSelect/select.js'; import { Spinner } from './Spinner.js'; import TextInput from './TextInput.js'; -import { fi } from 'zod/v4/locales'; type Props = { onDone(): void; @@ -596,7 +596,7 @@ function OAuthStatusMessage({ [activeField, baseUrl, apiKey, haikuModel, sonnetModel, opusModel], ); - const switchTo = useCallback( + const _switchTo = useCallback( (target: Field) => { setOAuthStatus(buildState(activeField, inputValue, target)); setInputValue(displayValues[target] ?? ''); diff --git a/src/components/FileEditToolUpdatedMessage.tsx b/src/components/FileEditToolUpdatedMessage.tsx index d36d47b131..8725e4c4a8 100644 --- a/src/components/FileEditToolUpdatedMessage.tsx +++ b/src/components/FileEditToolUpdatedMessage.tsx @@ -12,7 +12,7 @@ type Props = { }; export function FileEditToolUpdatedMessage({ - filePath, + filePath: _filePath, structuredPatch, style, verbose, diff --git a/src/components/LogoV2/CondensedLogo.tsx b/src/components/LogoV2/CondensedLogo.tsx index 50ffb18e27..86f52ba0ac 100644 --- a/src/components/LogoV2/CondensedLogo.tsx +++ b/src/components/LogoV2/CondensedLogo.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { type ReactNode, useEffect } from 'react'; import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; diff --git a/src/components/LogoV2/LogoV2.tsx b/src/components/LogoV2/LogoV2.tsx index f0a9db0303..93491ec577 100644 --- a/src/components/LogoV2/LogoV2.tsx +++ b/src/components/LogoV2/LogoV2.tsx @@ -63,7 +63,6 @@ import { incrementOverageCreditUpsellSeenCount, createOverageCreditFeed, } from './OverageCreditUpsell.js'; -import { plural } from '../../utils/stringUtils.js'; import { useAppState } from '../../state/AppState.js'; import { getEffortSuffix } from '../../utils/effort.js'; import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; diff --git a/src/components/LogoV2/feedConfigs.tsx b/src/components/LogoV2/feedConfigs.tsx index 9a326bd173..4fb387c6b2 100644 --- a/src/components/LogoV2/feedConfigs.tsx +++ b/src/components/LogoV2/feedConfigs.tsx @@ -1,6 +1,5 @@ import figures from 'figures'; import { homedir } from 'os'; -import * as React from 'react'; import { Box, Text } from '@anthropic/ink'; import type { Step } from '../../projectOnboardingState.js'; import { formatCreditAmount, getCachedReferrerReward } from '../../services/api/referral.js'; diff --git a/src/components/Onboarding.tsx b/src/components/Onboarding.tsx index 4138e74457..2d0cf32edd 100644 --- a/src/components/Onboarding.tsx +++ b/src/components/Onboarding.tsx @@ -111,7 +111,7 @@ export function Onboarding({ onDone }: Props): React.ReactNode { ); - const preflightStep = ; + const _preflightStep = ; // Create the steps array - determine which steps to include based on reAuth and oauthEnabled const apiKeyNeedingApproval = useMemo(() => { // Add API key step if needed diff --git a/src/components/PromptInput/Notifications.tsx b/src/components/PromptInput/Notifications.tsx index 836c122e9d..65464dcf9e 100644 --- a/src/components/PromptInput/Notifications.tsx +++ b/src/components/PromptInput/Notifications.tsx @@ -24,7 +24,6 @@ import { setEnvHookNotifier } from '../../utils/hooks/fileChangedWatcher.js'; import { toIDEDisplayName } from '../../utils/ide.js'; import { getMessagesAfterCompactBoundary } from '../../utils/messages.js'; import { tokenCountFromLastAPIResponse } from '../../utils/tokens.js'; -import { AutoUpdaterWrapper } from '../AutoUpdaterWrapper.js'; import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; import { IdeStatusIndicator } from '../IdeStatusIndicator.js'; import { MemoryUsageIndicator } from '../MemoryUsageIndicator.js'; @@ -57,13 +56,13 @@ type Props = { export function Notifications({ apiKeyStatus, - autoUpdaterResult, + autoUpdaterResult: _autoUpdaterResult, debug, - isAutoUpdating, + isAutoUpdating: _isAutoUpdating, verbose, messages, - onAutoUpdaterResult, - onChangeIsUpdating, + onAutoUpdaterResult: _onAutoUpdaterResult, + onChangeIsUpdating: _onChangeIsUpdating, ideSelection, mcpClients, isInputWrapped = false, @@ -102,9 +101,6 @@ export function Notifications({ const shouldShowIdeSelection = ideStatus === 'connected' && (ideSelection?.filePath || (ideSelection?.text && ideSelection.lineCount > 0)); - // Hide update installed message when showing IDE selection - const shouldShowAutoUpdater = !shouldShowIdeSelection || isAutoUpdating || autoUpdaterResult?.status !== 'success'; - // Check if we're in overage mode for UI indicators const isInOverageMode = claudeAiLimits.isUsingOverage; const subscriptionType = getSubscriptionType(); @@ -157,12 +153,6 @@ export function Notifications({ verbose={verbose} tokenUsage={tokenUsage} mainLoopModel={mainLoopModel} - shouldShowAutoUpdater={shouldShowAutoUpdater} - autoUpdaterResult={autoUpdaterResult} - isAutoUpdating={isAutoUpdating} - isShowingCompactMessage={isShowingCompactMessage} - onAutoUpdaterResult={onAutoUpdaterResult} - onChangeIsUpdating={onChangeIsUpdating} /> @@ -180,12 +170,6 @@ function NotificationContent({ verbose, tokenUsage, mainLoopModel, - shouldShowAutoUpdater, - autoUpdaterResult, - isAutoUpdating, - isShowingCompactMessage, - onAutoUpdaterResult, - onChangeIsUpdating, }: { ideSelection: IDESelection | undefined; mcpClients?: MCPServerConnection[]; @@ -200,12 +184,6 @@ function NotificationContent({ verbose: boolean; tokenUsage: number; mainLoopModel: string; - shouldShowAutoUpdater: boolean; - autoUpdaterResult: AutoUpdaterResult | null; - isAutoUpdating: boolean; - isShowingCompactMessage: boolean; - onAutoUpdaterResult: (result: AutoUpdaterResult) => void; - onChangeIsUpdating: (isUpdating: boolean) => void; }): ReactNode { // Poll apiKeyHelper inflight state to show slow-helper notice. // Gated on configuration — most users never set apiKeyHelper, so the diff --git a/src/components/PromptInput/PromptInput.tsx b/src/components/PromptInput/PromptInput.tsx index 99bd41138c..58d04c9100 100644 --- a/src/components/PromptInput/PromptInput.tsx +++ b/src/components/PromptInput/PromptInput.tsx @@ -55,7 +55,6 @@ import { } from '@claude-code-best/builtin-tools/tools/AgentTool/agentColorManager.js'; import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'; import type { Message } from '../../types/message.js'; -import type { PermissionMode } from '../../types/permissions.js'; import type { BaseTextInputProps, PromptInputMode, VimMode } from '../../types/textInputTypes.js'; import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; import { count } from '../../utils/array.js'; @@ -329,7 +328,7 @@ function PromptInput({ const hasTungstenSession = useAppState(s => process.env.USER_TYPE === 'ant' && s.tungstenActiveSession !== undefined); const tmuxFooterVisible = process.env.USER_TYPE === 'ant' && hasTungstenSession; // WebBrowser pill — visible when a browser is open - const bagelFooterVisible = useAppState(s => false); + const bagelFooterVisible = useAppState(_s => false); const teamContext = useAppState(s => s.teamContext); const queuedCommands = useCommandQueue(); const promptSuggestionState = useAppState(s => s.promptSuggestion); @@ -538,7 +537,7 @@ function PromptInput({ const tasksSelected = footerItemSelected === 'tasks'; const tmuxSelected = footerItemSelected === 'tmux'; - const bagelSelected = footerItemSelected === 'bagel'; + const _bagelSelected = footerItemSelected === 'bagel'; const teamsSelected = footerItemSelected === 'teams'; const bridgeSelected = footerItemSelected === 'bridge'; diff --git a/src/components/PromptInput/PromptInputFooter.tsx b/src/components/PromptInput/PromptInputFooter.tsx index f1193ba9f8..fef8c74f05 100644 --- a/src/components/PromptInput/PromptInputFooter.tsx +++ b/src/components/PromptInput/PromptInputFooter.tsx @@ -1,6 +1,6 @@ import { feature } from 'bun:bundle'; import * as React from 'react'; -import { memo, type ReactNode, useCallback, useMemo, useRef, useState } from 'react'; +import { memo, type ReactNode, useMemo, useRef, useState } from 'react'; import { isBridgeEnabled } from '../../bridge/bridgeEnabled.js'; import { getBridgeStatus } from '../../bridge/bridgeStatusUtil.js'; import { useSetPromptOverlay } from '../../context/promptOverlayContext.js'; diff --git a/src/components/PromptInput/PromptInputFooterSuggestions.tsx b/src/components/PromptInput/PromptInputFooterSuggestions.tsx index e45167a8c4..e7f755d247 100644 --- a/src/components/PromptInput/PromptInputFooterSuggestions.tsx +++ b/src/components/PromptInput/PromptInputFooterSuggestions.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { memo, type ReactNode } from 'react'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Box, Text, stringWidth } from '@anthropic/ink'; diff --git a/src/components/PromptInput/SandboxPromptFooterHint.tsx b/src/components/PromptInput/SandboxPromptFooterHint.tsx index dc831e4c1a..efcd0ef861 100644 --- a/src/components/PromptInput/SandboxPromptFooterHint.tsx +++ b/src/components/PromptInput/SandboxPromptFooterHint.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { type ReactNode, useEffect, useRef, useState } from 'react'; import { Box, Text } from '@anthropic/ink'; import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; diff --git a/src/components/SandboxViolationExpandedView.tsx b/src/components/SandboxViolationExpandedView.tsx index b1982547ac..bcf6b66c43 100644 --- a/src/components/SandboxViolationExpandedView.tsx +++ b/src/components/SandboxViolationExpandedView.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { type ReactNode, useEffect, useState } from 'react'; import { Box, Text } from '@anthropic/ink'; import type { SandboxViolationEvent } from '../utils/sandbox/sandbox-adapter.js'; diff --git a/src/components/Settings/Config.tsx b/src/components/Settings/Config.tsx index 2f62ad3c1a..a2031bde21 100644 --- a/src/components/Settings/Config.tsx +++ b/src/components/Settings/Config.tsx @@ -15,14 +15,11 @@ import { } from '../../utils/config.js'; import chalk from 'chalk'; import { - permissionModeTitle, permissionModeShortTitle, permissionModeFromString, toExternalPermissionMode, isExternalPermissionMode, - EXTERNAL_PERMISSION_MODES, PERMISSION_MODES, - type ExternalPermissionMode, type PermissionMode, } from '../../utils/permissions/PermissionMode.js'; import { diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx index fd3ce07bce..fe5e74fd91 100644 --- a/src/components/Spinner.tsx +++ b/src/components/Spinner.tsx @@ -9,7 +9,7 @@ import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growt import { isEnvTruthy } from '../utils/envUtils.js'; import { count } from '../utils/array.js'; import sample from 'lodash-es/sample.js'; -import { formatDuration, formatNumber, formatSecondsShort } from '../utils/format.js'; +import { formatDuration, formatNumber } from '../utils/format.js'; import type { Theme } from 'src/utils/theme.js'; import { activityManager } from '../utils/activityManager.js'; import { getSpinnerVerbs } from '../constants/spinnerVerbs.js'; @@ -244,7 +244,7 @@ function SpinnerWithVerbInner({ // TTFT display is gated to internal builds — apiMetricsRef was removed from // props during a refactor, so skip this until it's re-threaded. - let ttftText: string | null = null; + const _ttftText: string | null = null; // When leader is idle but teammates are running (and we're viewing the leader), // show a static dim idle display instead of the animated spinner — otherwise diff --git a/src/components/agents/new-agent-creation/CreateAgentWizard.tsx b/src/components/agents/new-agent-creation/CreateAgentWizard.tsx index 61e6f19b87..7ae00e1711 100644 --- a/src/components/agents/new-agent-creation/CreateAgentWizard.tsx +++ b/src/components/agents/new-agent-creation/CreateAgentWizard.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { isAutoMemoryEnabled } from '../../../memdir/paths.js'; import type { Tools } from '../../../Tool.js'; import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx index 67ce1f1031..c89d2772ee 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import type { AgentColorName } from '@claude-code-best/builtin-tools/tools/AgentTool/agentColorManager.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx index 60d2ff7b3a..a0d9700791 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { type KeyboardEvent, Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import { isAutoMemoryEnabled } from '../../../../memdir/paths.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx index eb664eb6f6..c867a3ccc7 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx @@ -1,5 +1,5 @@ import chalk from 'chalk'; -import React, { type ReactNode, useCallback, useState } from 'react'; +import { type ReactNode, useCallback, useState } from 'react'; import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, diff --git a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx index bfb8388056..5d18fc1759 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode, useCallback, useState } from 'react'; +import { type ReactNode, useCallback, useState } from 'react'; import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import { editPromptInEditor } from '../../../../utils/promptEditor.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx index 9200e239bd..ec598bff18 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx @@ -1,5 +1,5 @@ import { APIUserAbortError } from '@anthropic-ai/sdk'; -import React, { type ReactNode, useCallback, useRef, useState } from 'react'; +import { type ReactNode, useCallback, useRef, useState } from 'react'; import { useMainLoopModel } from '../../../../hooks/useMainLoopModel.js'; import { Box, Byline, Text } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx index 88a4555863..317afcf597 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink'; import type { SettingSource } from '../../../../utils/settings/constants.js'; import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx index 4c0aaa6788..6bfd081979 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import { isAutoMemoryEnabled } from '../../../../memdir/paths.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx index 9a12778729..8a8985160a 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink'; import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; import { Select } from '../../../CustomSelect/select.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx index f68028705b..5c984f3bb0 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; import { Byline, KeyboardShortcutHint } from '@anthropic/ink'; import { useWizard } from '../../../wizard/index.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx index 562e75c6f4..41a23ccf71 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode, useCallback, useState } from 'react'; +import { type ReactNode, useCallback, useState } from 'react'; import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import { editPromptInEditor } from '../../../../utils/promptEditor.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx index c885d6ee03..eb250a919e 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import type { Tools } from '../../../../Tool.js'; import { Byline, KeyboardShortcutHint } from '@anthropic/ink'; import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; diff --git a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx index 7e203e1955..df6a9b8c43 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode, useState } from 'react'; +import { type ReactNode, useState } from 'react'; import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink'; import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'; diff --git a/src/components/messages/SnipBoundaryMessage.ts b/src/components/messages/SnipBoundaryMessage.ts deleted file mode 100644 index 87592f1510..0000000000 --- a/src/components/messages/SnipBoundaryMessage.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} -export const SnipBoundaryMessage: (props: Record) => null = - () => null diff --git a/src/components/messages/UserCrossSessionMessage.ts b/src/components/messages/UserCrossSessionMessage.ts deleted file mode 100644 index 1d9677b3d2..0000000000 --- a/src/components/messages/UserCrossSessionMessage.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} -export const UserCrossSessionMessage: (props: Record) => null = - () => null diff --git a/src/components/messages/UserForkBoilerplateMessage.ts b/src/components/messages/UserForkBoilerplateMessage.ts deleted file mode 100644 index c45f71de91..0000000000 --- a/src/components/messages/UserForkBoilerplateMessage.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} -export const UserForkBoilerplateMessage: ( - props: Record, -) => null = () => null diff --git a/src/components/messages/UserGitHubWebhookMessage.ts b/src/components/messages/UserGitHubWebhookMessage.ts deleted file mode 100644 index 0bd63f9a54..0000000000 --- a/src/components/messages/UserGitHubWebhookMessage.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} -export const UserGitHubWebhookMessage: ( - props: Record, -) => null = () => null diff --git a/src/components/permissions/FilePermissionDialog/permissionOptions.tsx b/src/components/permissions/FilePermissionDialog/permissionOptions.tsx index 9f648034cd..7da5a1f692 100644 --- a/src/components/permissions/FilePermissionDialog/permissionOptions.tsx +++ b/src/components/permissions/FilePermissionDialog/permissionOptions.tsx @@ -1,6 +1,6 @@ import { homedir } from 'os'; import { basename, join, sep } from 'path'; -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { getOriginalCwd } from '../../../bootstrap/state.js'; import { Text } from '@anthropic/ink'; import { getShortcutDisplay } from '../../../keybindings/shortcutFormat.js'; diff --git a/src/components/permissions/shellPermissionHelpers.tsx b/src/components/permissions/shellPermissionHelpers.tsx index 1ea9bebad5..c85b9d5de8 100644 --- a/src/components/permissions/shellPermissionHelpers.tsx +++ b/src/components/permissions/shellPermissionHelpers.tsx @@ -1,5 +1,5 @@ import { basename, sep } from 'path'; -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { getOriginalCwd } from '../../bootstrap/state.js'; import { Text } from '@anthropic/ink'; import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'; diff --git a/src/components/tasks/ShellProgress.tsx b/src/components/tasks/ShellProgress.tsx index 412047f616..d49a3e1ebc 100644 --- a/src/components/tasks/ShellProgress.tsx +++ b/src/components/tasks/ShellProgress.tsx @@ -1,5 +1,4 @@ import type { ReactNode } from 'react'; -import React from 'react'; import { Text } from '@anthropic/ink'; import type { TaskStatus } from 'src/Task.js'; import type { LocalShellTaskState } from 'src/tasks/LocalShellTask/guards.js'; diff --git a/src/components/tasks/WorkflowDetailDialog.tsx b/src/components/tasks/WorkflowDetailDialog.tsx index 6ae83866c8..fae990e1eb 100644 --- a/src/components/tasks/WorkflowDetailDialog.tsx +++ b/src/components/tasks/WorkflowDetailDialog.tsx @@ -24,7 +24,7 @@ type Props = { */ export function WorkflowDetailDialog({ workflow, - onDone, + onDone: _onDone, onKill, onSkipAgent: _onSkipAgent, onRetryAgent: _onRetryAgent, diff --git a/src/components/teams/TeamsDialog.tsx b/src/components/teams/TeamsDialog.tsx index 4b0acbf38a..19fc08a5e1 100644 --- a/src/components/teams/TeamsDialog.tsx +++ b/src/components/teams/TeamsDialog.tsx @@ -26,13 +26,7 @@ import { IT2_COMMAND, isInsideTmuxSync } from '../../utils/swarm/backends/detect import { ensureBackendsRegistered, getBackendByType, getCachedBackend } from '../../utils/swarm/backends/registry.js'; import { isPaneBackend, type PaneBackendType } from '../../utils/swarm/backends/types.js'; import { getSwarmSocketName, TMUX_COMMAND } from '../../utils/swarm/constants.js'; -import { - addHiddenPaneId, - removeHiddenPaneId, - removeMemberFromTeam, - setMemberMode, - setMultipleMemberModes, -} from '../../utils/swarm/teamHelpers.js'; +import { removeMemberFromTeam, setMemberMode, setMultipleMemberModes } from '../../utils/swarm/teamHelpers.js'; import { listTasks, type Task, unassignTeammateTasks } from '../../utils/tasks.js'; import { getTeammateStatuses, type TeammateStatus, type TeamSummary } from '../../utils/teamDiscovery.js'; import { @@ -560,13 +554,13 @@ async function toggleTeammateVisibility(teammate: TeammateStatus, teamName: stri * Hide a teammate pane using the backend abstraction. * Only available for ant users (gated for dead code elimination in external builds) */ -async function hideTeammate(teammate: TeammateStatus, teamName: string): Promise {} +async function hideTeammate(_teammate: TeammateStatus, _teamName: string): Promise {} /** * Show a previously hidden teammate pane using the backend abstraction. * Only available for ant users (gated for dead code elimination in external builds) */ -async function showTeammate(teammate: TeammateStatus, teamName: string): Promise {} +async function showTeammate(_teammate: TeammateStatus, _teamName: string): Promise {} /** * Send a mode change message to a single teammate diff --git a/src/components/wizard/WizardDialogLayout.tsx b/src/components/wizard/WizardDialogLayout.tsx index a77765b9bc..30ca64a7cf 100644 --- a/src/components/wizard/WizardDialogLayout.tsx +++ b/src/components/wizard/WizardDialogLayout.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import type { Theme } from '../../utils/theme.js'; import { Dialog } from '@anthropic/ink'; import { useWizard } from './useWizard.js'; diff --git a/src/components/wizard/WizardNavigationFooter.tsx b/src/components/wizard/WizardNavigationFooter.tsx index 6f2eb9dcaa..d6cbcd6141 100644 --- a/src/components/wizard/WizardNavigationFooter.tsx +++ b/src/components/wizard/WizardNavigationFooter.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; import { Box, Text } from '@anthropic/ink'; import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; diff --git a/src/components/wizard/WizardProvider.tsx b/src/components/wizard/WizardProvider.tsx index 617715913b..8395cbd070 100644 --- a/src/components/wizard/WizardProvider.tsx +++ b/src/components/wizard/WizardProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; +import { createContext, type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; import type { WizardContextValue, WizardProviderProps } from './types.js'; diff --git a/src/context/promptOverlayContext.tsx b/src/context/promptOverlayContext.tsx index 65db861cf2..a8e337b146 100644 --- a/src/context/promptOverlayContext.tsx +++ b/src/context/promptOverlayContext.tsx @@ -18,7 +18,7 @@ * Split into data/setter context pairs so writers never re-render on * their own writes — the setter contexts are stable. */ -import React, { createContext, type ReactNode, useContext, useEffect, useState } from 'react'; +import { createContext, type ReactNode, useContext, useEffect, useState } from 'react'; import type { SuggestionItem } from '../components/PromptInput/PromptInputFooterSuggestions.js'; export type PromptOverlayData = { diff --git a/src/daemon/main.ts b/src/daemon/main.ts index 0d3855ddb2..4a5a7c3727 100644 --- a/src/daemon/main.ts +++ b/src/daemon/main.ts @@ -1,7 +1,6 @@ import { type ChildProcess } from 'child_process' import { resolve } from 'path' import { buildCliLaunch, spawnCli } from '../utils/cliLaunch.js' -import { errorMessage } from '../utils/errors.js' import { writeDaemonState, removeDaemonState, diff --git a/src/entrypoints/cli.tsx b/src/entrypoints/cli.tsx index 7e4da4ed0d..efe7e52727 100644 --- a/src/entrypoints/cli.tsx +++ b/src/entrypoints/cli.tsx @@ -1,4 +1,8 @@ #!/usr/bin/env bun +// Performance shim MUST be the first import — it replaces globalThis.performance +// with a JS-backed implementation before React/OTel capture the native reference. +// Without this, JSC's C++ Vector grows without bound in long-running sessions. +import '../utils/performanceShim.js'; import { feature } from 'bun:bundle'; import { isEnvTruthy } from '../utils/envUtils.js'; diff --git a/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx b/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx index a377a131f5..5c1b27b29a 100644 --- a/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx +++ b/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { getOauthProfileFromApiKey } from 'src/services/oauth/getOauthProfile.js'; import { isClaudeAISubscriber } from 'src/utils/auth.js'; import { Text } from '@anthropic/ink'; diff --git a/src/hooks/notifs/useIDEStatusIndicator.tsx b/src/hooks/notifs/useIDEStatusIndicator.tsx index 20ad7ec3ff..b0735d1dfb 100644 --- a/src/hooks/notifs/useIDEStatusIndicator.tsx +++ b/src/hooks/notifs/useIDEStatusIndicator.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import { useEffect, useRef } from 'react'; import { useNotifications } from 'src/context/notifications.js'; import { Text } from '@anthropic/ink'; import type { MCPServerConnection } from 'src/services/mcp/types.js'; diff --git a/src/hooks/notifs/useMcpConnectivityStatus.tsx b/src/hooks/notifs/useMcpConnectivityStatus.tsx index 2f0c02cdbb..d2d0e27526 100644 --- a/src/hooks/notifs/useMcpConnectivityStatus.tsx +++ b/src/hooks/notifs/useMcpConnectivityStatus.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useEffect } from 'react'; import { useNotifications } from 'src/context/notifications.js'; import { getIsRemoteMode } from '../../bootstrap/state.js'; diff --git a/src/hooks/notifs/usePluginAutoupdateNotification.tsx b/src/hooks/notifs/usePluginAutoupdateNotification.tsx index 1a4e2ee11f..6256c1a143 100644 --- a/src/hooks/notifs/usePluginAutoupdateNotification.tsx +++ b/src/hooks/notifs/usePluginAutoupdateNotification.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useEffect, useState } from 'react'; import { getIsRemoteMode } from '../../bootstrap/state.js'; import { useNotifications } from '../../context/notifications.js'; diff --git a/src/hooks/notifs/usePluginInstallationStatus.tsx b/src/hooks/notifs/usePluginInstallationStatus.tsx index 8884531d87..5caeb9a97b 100644 --- a/src/hooks/notifs/usePluginInstallationStatus.tsx +++ b/src/hooks/notifs/usePluginInstallationStatus.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useEffect, useMemo } from 'react'; import { getIsRemoteMode } from '../../bootstrap/state.js'; import { useNotifications } from '../../context/notifications.js'; diff --git a/src/hooks/notifs/useRateLimitWarningNotification.tsx b/src/hooks/notifs/useRateLimitWarningNotification.tsx index 5d30e761d1..a90bd306ab 100644 --- a/src/hooks/notifs/useRateLimitWarningNotification.tsx +++ b/src/hooks/notifs/useRateLimitWarningNotification.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useNotifications } from 'src/context/notifications.js'; import { Text } from '@anthropic/ink'; diff --git a/src/hooks/useArrowKeyHistory.tsx b/src/hooks/useArrowKeyHistory.tsx index ff31bd7440..4c9edc6963 100644 --- a/src/hooks/useArrowKeyHistory.tsx +++ b/src/hooks/useArrowKeyHistory.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { getModeFromInput } from 'src/components/PromptInput/inputModes.js'; import { useNotifications } from 'src/context/notifications.js'; import { ConfigurableShortcutHint } from '../components/ConfigurableShortcutHint.js'; diff --git a/src/hooks/useChromeExtensionNotification.tsx b/src/hooks/useChromeExtensionNotification.tsx index bb681d45de..98ba53edb9 100644 --- a/src/hooks/useChromeExtensionNotification.tsx +++ b/src/hooks/useChromeExtensionNotification.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Text } from '@anthropic/ink'; import { isClaudeAISubscriber } from '../utils/auth.js'; import { isChromeExtensionInstalled, shouldEnableClaudeInChrome } from '../utils/claudeInChrome/setup.js'; diff --git a/src/hooks/useMergedTools.ts b/src/hooks/useMergedTools.ts index 48b1deeca1..368db61fe3 100644 --- a/src/hooks/useMergedTools.ts +++ b/src/hooks/useMergedTools.ts @@ -2,7 +2,6 @@ import { useMemo } from 'react' import type { Tools, ToolPermissionContext } from '../Tool.js' import { assembleToolPool } from '../tools.js' -import { useAppState } from '../state/AppState.js' import { mergeAndFilterTools } from '../utils/toolPool.js' /** diff --git a/src/hooks/useOfficialMarketplaceNotification.tsx b/src/hooks/useOfficialMarketplaceNotification.tsx index 2d6f24c696..64c68eda96 100644 --- a/src/hooks/useOfficialMarketplaceNotification.tsx +++ b/src/hooks/useOfficialMarketplaceNotification.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import type { Notification } from '../context/notifications.js'; import { Text } from '@anthropic/ink'; import { logForDebugging } from '../utils/debug.js'; diff --git a/src/hooks/useSSHSession.ts b/src/hooks/useSSHSession.ts index d1fb25b74a..dc34926c76 100644 --- a/src/hooks/useSSHSession.ts +++ b/src/hooks/useSSHSession.ts @@ -21,10 +21,7 @@ import { isSessionEndMessage, } from '../remote/sdkMessageAdapter.js' import type { SSHSession } from '../ssh/createSSHSession.js' -import type { - SSHSessionManager, - SSHPermissionRequest, -} from '../ssh/SSHSessionManager.js' +import type { SSHSessionManager } from '../ssh/SSHSessionManager.js' import type { Tool } from '../Tool.js' import { findToolByName } from '../Tool.js' import type { Message as MessageType } from '../types/message.js' diff --git a/src/hooks/useTypeahead.tsx b/src/hooks/useTypeahead.tsx index 5e625f9114..a0f30f5bde 100644 --- a/src/hooks/useTypeahead.tsx +++ b/src/hooks/useTypeahead.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useNotifications } from 'src/context/notifications.js'; import { Text } from '@anthropic/ink'; diff --git a/src/interactiveHelpers.tsx b/src/interactiveHelpers.tsx index d7ad64a6a7..4bc523d7dd 100644 --- a/src/interactiveHelpers.tsx +++ b/src/interactiveHelpers.tsx @@ -19,11 +19,7 @@ import { isSynchronizedOutputSupported } from '@anthropic/ink'; import type { RenderOptions, Root, TextProps } from '@anthropic/ink'; import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js'; import { startDeferredPrefetches } from './main.js'; -import { - checkGate_CACHED_OR_BLOCKING, - initializeGrowthBook, - resetGrowthBook, -} from './services/analytics/growthbook.js'; +import { initializeGrowthBook, resetGrowthBook } from './services/analytics/growthbook.js'; import { isQualifiedForGrove } from './services/api/grove.js'; import { handleMcpjsonServerApprovals } from './services/mcpServerApproval.js'; import { AppStateProvider } from './state/AppState.js'; diff --git a/src/main.tsx b/src/main.tsx index 0a001d07f0..169a555f14 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -28,7 +28,6 @@ import { readFileSync } from 'fs'; import mapValues from 'lodash-es/mapValues.js'; import pickBy from 'lodash-es/pickBy.js'; import uniqBy from 'lodash-es/uniqBy.js'; -import React from 'react'; import { getOauthConfig } from './constants/oauth.js'; import { getRemoteSessionUrl } from './constants/product.js'; import { getSystemContext, getUserContext } from './context.js'; @@ -383,7 +382,7 @@ function logManagedSettings(): void { } // Check if running in debug/inspection mode -function isBeingDebugged() { +function _isBeingDebugged() { const isBun = isRunningWithBun(); // Check for inspect flags in process arguments (including all variants) diff --git a/src/query.ts b/src/query.ts index 767ba998fb..197054c636 100644 --- a/src/query.ts +++ b/src/query.ts @@ -8,7 +8,6 @@ import { FallbackTriggeredError } from './services/api/withRetry.js' import { calculateTokenWarningState, estimateMaxTurnGrowth, - getAutoCompactThreshold, getEffectiveContextWindowSize, isAutoCompactEnabled, type AutoCompactTrackingState, @@ -69,7 +68,7 @@ import { const skillPrefetch = feature('EXPERIMENTAL_SKILL_SEARCH') ? (require('./services/skillSearch/prefetch.js') as typeof import('./services/skillSearch/prefetch.js')) : null -const jobClassifier = feature('TEMPLATES') +const _jobClassifier = feature('TEMPLATES') ? (require('./jobs/classifier.js') as typeof import('./jobs/classifier.js')) : null /* eslint-enable @typescript-eslint/no-require-imports */ @@ -124,6 +123,7 @@ import { count } from './utils/array.js' import { createTrace, endTrace, + flushLangfuse, isLangfuseEnabled, } from './services/langfuse/index.js' import { getAPIProvider } from './utils/model/providers.js' @@ -339,6 +339,11 @@ export async function* query( terminal?.reason === 'aborted_streaming' || terminal?.reason === 'aborted_tools' endTrace(langfuseTrace, undefined, isAborted ? 'interrupted' : undefined) + // Flush the processor to release span data (including serialized + // conversation history stored as langfuse.observation.input). Without + // this, SpanImpl objects retain hundreds of KB of JSON until the + // processor's batch timer fires (default 10s). + await flushLangfuse() } // Break the closure chain: toolUseContext captures langfuseTrace which @@ -349,6 +354,21 @@ export async function* query( paramsWithTrace.toolUseContext.langfuseRootTrace = null paramsWithTrace.toolUseContext.langfuseBatchSpan = null } + + // Clear JSC's native Performance buffers. OTel (otperformance) references + // globalThis.performance which stores marks/measures/resource timings in a + // C++ Vector that never shrinks. Long-running sessions accumulate hundreds + // of MB of dead capacity even after spans are flushed and nullified. + const gPerf = globalThis.performance + if (gPerf && typeof gPerf.clearMarks === 'function') { + try { + gPerf.clearMarks() + gPerf.clearMeasures?.() + gPerf.clearResourceTimings?.() + } catch { + // Non-critical — some environments may not support all methods + } + } } // Only reached if queryLoop returned normally. Skipped on throw (error diff --git a/src/screens/REPL.tsx b/src/screens/REPL.tsx index 929604af43..750da47ca5 100644 --- a/src/screens/REPL.tsx +++ b/src/screens/REPL.tsx @@ -270,7 +270,7 @@ import { useManagePlugins } from '../hooks/useManagePlugins.js'; import { Messages } from '../components/Messages.js'; import { TaskListV2 } from '../components/TaskListV2.js'; import { TeammateViewHeader } from '../components/TeammateViewHeader.js'; -import { getPipeDisplayRole, getPipeIpc, isPipeControlled } from '../utils/pipeTransport.js'; +import { getPipeIpc } from '../utils/pipeTransport.js'; import { useTasksV2WithCollapseEffect } from '../hooks/useTasksV2.js'; import { maybeMarkProjectOnboardingComplete } from '../projectOnboardingState.js'; import type { MCPServerConnection } from '../services/mcp/types.js'; @@ -808,8 +808,8 @@ export function REPL({ pendingHookMessages, initialFileHistorySnapshots, initialContentReplacements, - initialAgentName, - initialAgentColor, + initialAgentName: _initialAgentName, + initialAgentColor: _initialAgentColor, mcpClients: initialMcpClients, dynamicMcpConfig: initialDynamicMcpConfig, autoConnectIdeFlag, @@ -2797,7 +2797,7 @@ export function REPL({ const getToolUseContext = useCallback( ( messages: MessageType[], - newMessages: MessageType[], + _newMessages: MessageType[], abortController: AbortController, mainLoopModel: string, ): ProcessUserInputContext => { @@ -4937,7 +4937,7 @@ export function REPL({ useMailboxBridge({ isLoading, onSubmitMessage: handleIncomingPrompt }); useMasterMonitor(); useSlaveNotifications(); - const pipeIpcState = useAppState(s => getPipeIpc(s as any)); + const _pipeIpcState = useAppState(s => getPipeIpc(s as any)); usePipePermissionForward({ store, tools, setMessages, setToolUseConfirmQueue, getToolUseContext, mainLoopModel }); usePipeMuteSync({ setToolUseConfirmQueue }); diff --git a/src/services/acp/permissions.ts b/src/services/acp/permissions.ts index a7d55bba22..69c87451b8 100644 --- a/src/services/acp/permissions.ts +++ b/src/services/acp/permissions.ts @@ -32,7 +32,7 @@ import { toolInfoFromToolUse } from './bridge.js' export function createAcpCanUseTool( conn: AgentSideConnection, sessionId: string, - getCurrentMode: () => string, + _getCurrentMode: () => string, clientCapabilities?: ClientCapabilities, cwd?: string, onModeChange?: (modeId: string) => void, diff --git a/src/services/api/claude.ts b/src/services/api/claude.ts index 20b86ebcc0..ec2a5602f4 100644 --- a/src/services/api/claude.ts +++ b/src/services/api/claude.ts @@ -121,7 +121,6 @@ import { getAfkModeHeaderLatched, getCacheEditingHeaderLatched, getFastModeHeaderLatched, - getLastApiCompletionTimestamp, getPromptCache1hAllowlist, getPromptCache1hEligible, getSessionId, @@ -253,7 +252,6 @@ import { type NonNullableUsage, } from './logging.js' import { - CACHE_TTL_1HOUR_MS, checkResponseForCacheBreak, recordPromptState, } from './promptCacheBreakDetection.js' diff --git a/src/services/api/gemini/index.ts b/src/services/api/gemini/index.ts index a490957683..6754189af4 100644 --- a/src/services/api/gemini/index.ts +++ b/src/services/api/gemini/index.ts @@ -6,7 +6,7 @@ import type { StreamEvent, SystemAPIErrorMessage, } from '../../../types/message.js' -import { getEmptyToolPermissionContext, type Tools } from '../../../Tool.js' +import { type Tools } from '../../../Tool.js' import { toolToAPISchema } from '../../../utils/api.js' import { logForDebugging } from '../../../utils/debug.js' import { diff --git a/src/services/api/openai/client.ts b/src/services/api/openai/client.ts index d7cf972bbd..5ee37cd414 100644 --- a/src/services/api/openai/client.ts +++ b/src/services/api/openai/client.ts @@ -2,7 +2,6 @@ import OpenAI from 'openai' import { openaiAdapter } from 'src/services/providerUsage/adapters/openai.js' import { updateProviderBuckets } from 'src/services/providerUsage/store.js' import { getProxyFetchOptions } from 'src/utils/proxy.js' -import { isEnvTruthy } from 'src/utils/envUtils.js' /** * Environment variables: diff --git a/src/services/api/openai/index.ts b/src/services/api/openai/index.ts index c30807f10b..5ac47ab4e7 100644 --- a/src/services/api/openai/index.ts +++ b/src/services/api/openai/index.ts @@ -9,8 +9,6 @@ import type { } from '../../../types/message.js' import type { AgentId } from '../../../types/ids.js' import type { Tools } from '../../../Tool.js' -import type { Stream } from 'openai/streaming.mjs' -import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions/completions.mjs' import { getOpenAIClient } from './client.js' import { anthropicMessagesToOpenAI, diff --git a/src/services/compact/cachedMicrocompact.ts b/src/services/compact/cachedMicrocompact.ts index 56e7537387..d2be119209 100644 --- a/src/services/compact/cachedMicrocompact.ts +++ b/src/services/compact/cachedMicrocompact.ts @@ -98,7 +98,7 @@ export function getToolResultsToDelete(state: CachedMCState): string[] { * Returns null if toolIds is empty. */ export function createCacheEditsBlock( - state: CachedMCState, + _state: CachedMCState, toolIds: string[], ): CacheEditsBlock | null { if (toolIds.length === 0) return null diff --git a/src/services/compact/snipCompact.ts b/src/services/compact/snipCompact.ts index 4e4a0d9fc7..e95d6bfe0e 100644 --- a/src/services/compact/snipCompact.ts +++ b/src/services/compact/snipCompact.ts @@ -82,7 +82,7 @@ function estimateMessageTokens(message: Message): number { */ export function snipCompactIfNeeded( messages: Message[], - options?: { force?: boolean }, + _options?: { force?: boolean }, ): { messages: Message[] executed: boolean diff --git a/src/services/doubaoSTT.ts b/src/services/doubaoSTT.ts index 0cd1e50246..2d6b98cc0b 100644 --- a/src/services/doubaoSTT.ts +++ b/src/services/doubaoSTT.ts @@ -18,9 +18,6 @@ import { logError } from '../utils/log.js' // Re-export FinalizeSource so useVoice can import from either module export type { FinalizeSource } from './voiceStreamSTT.js' -// Maximum time to wait for the generator to finish after end-of-stream signal. -const FINALIZE_SAFETY_TIMEOUT_MS = 5_000 - // ─── AsyncIterable audio queue ───────────────────────────────────────── // A push-based queue that implements AsyncIterable. diff --git a/src/services/extractMemories/extractMemories.ts b/src/services/extractMemories/extractMemories.ts index 51e5324392..26639fc7e9 100644 --- a/src/services/extractMemories/extractMemories.ts +++ b/src/services/extractMemories/extractMemories.ts @@ -38,7 +38,6 @@ import { REPL_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/REPLTool/c import type { AssistantMessage, Message, - SystemLocalCommandMessage, SystemMessage, } from '../../types/message.js' import { createAbortController } from '../../utils/abortController.js' diff --git a/src/services/langfuse/client.ts b/src/services/langfuse/client.ts index 256cd8af87..7fd50f2ed5 100644 --- a/src/services/langfuse/client.ts +++ b/src/services/langfuse/client.ts @@ -61,6 +61,16 @@ export function initLangfuse(): boolean { } } +export async function flushLangfuse(): Promise { + try { + if (processor) { + await processor.forceFlush() + } + } catch (e) { + logForDebugging(`[langfuse] Flush error: ${e}`, { level: 'error' }) + } +} + export async function shutdownLangfuse(): Promise { try { if (processor) { diff --git a/src/services/langfuse/index.ts b/src/services/langfuse/index.ts index b533bec697..d52088a261 100644 --- a/src/services/langfuse/index.ts +++ b/src/services/langfuse/index.ts @@ -1,6 +1,7 @@ export { initLangfuse, shutdownLangfuse, + flushLangfuse, isLangfuseEnabled, getLangfuseProcessor, } from './client.js' diff --git a/src/services/langfuse/sanitize.ts b/src/services/langfuse/sanitize.ts index 0c8dd68c4f..e34c6b3ece 100644 --- a/src/services/langfuse/sanitize.ts +++ b/src/services/langfuse/sanitize.ts @@ -60,7 +60,7 @@ function sanitizeObject(obj: Record): Record { return result } -export function sanitizeToolInput(toolName: string, input: unknown): unknown { +export function sanitizeToolInput(_toolName: string, input: unknown): unknown { if (typeof input !== 'object' || input === null) return input const obj = { ...(input as Record) } diff --git a/src/services/mcp/channelNotification.ts b/src/services/mcp/channelNotification.ts index 43551f8cb1..82346c90ef 100644 --- a/src/services/mcp/channelNotification.ts +++ b/src/services/mcp/channelNotification.ts @@ -23,12 +23,10 @@ import { CHANNEL_TAG } from '../../constants/xml.js' import { getSubscriptionType } from '../../utils/auth.js' import { lazySchema } from '../../utils/lazySchema.js' import { parsePluginIdentifier } from '../../utils/plugins/pluginIdentifier.js' -import { getSettingsForSource } from '../../utils/settings/settings.js' import { escapeXmlAttr } from '../../utils/xml.js' import { type ChannelAllowlistEntry, getChannelAllowlist, - isChannelsEnabled, } from './channelAllowlist.js' export const ChannelMessageNotificationSchema = lazySchema(() => diff --git a/src/services/mcp/client.ts b/src/services/mcp/client.ts index 82e46b6e75..cec2726427 100644 --- a/src/services/mcp/client.ts +++ b/src/services/mcp/client.ts @@ -117,13 +117,7 @@ import { getLoggingSafeMcpBaseUrl } from './utils.js' // Package imports — delegate to mcp-client package utilities where applicable import { - createMcpClient as createMcpClientFromPackage, - captureStderr, isMcpSessionExpiredError as isMcpSessionExpiredErrorFromPackage, - installConnectionMonitor, - createCleanup as createCleanupFromPackage, - buildConnectedServer, - DEFAULT_CONNECTION_TIMEOUT_MS, MAX_MCP_DESCRIPTION_LENGTH as PKG_MAX_MCP_DESCRIPTION_LENGTH, } from '@claude-code-best/mcp-client' import { recursivelySanitizeUnicode } from '@claude-code-best/mcp-client' diff --git a/src/services/mcpServerApproval.tsx b/src/services/mcpServerApproval.tsx index 92b76bff60..f4ea2ccf41 100644 --- a/src/services/mcpServerApproval.tsx +++ b/src/services/mcpServerApproval.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { MCPServerApprovalDialog } from '../components/MCPServerApprovalDialog.js'; import { MCPServerMultiselectDialog } from '../components/MCPServerMultiselectDialog.js'; import type { Root } from '@anthropic/ink'; diff --git a/src/services/remoteManagedSettings/securityCheck.tsx b/src/services/remoteManagedSettings/securityCheck.tsx index ea8281d617..bd45d96320 100644 --- a/src/services/remoteManagedSettings/securityCheck.tsx +++ b/src/services/remoteManagedSettings/securityCheck.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { getIsInteractive } from '../../bootstrap/state.js'; import { ManagedSettingsSecurityDialog } from '../../components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.js'; import { diff --git a/src/services/skillLearning/observationStore.ts b/src/services/skillLearning/observationStore.ts index bf87d136e3..a5429f5120 100644 --- a/src/services/skillLearning/observationStore.ts +++ b/src/services/skillLearning/observationStore.ts @@ -3,7 +3,6 @@ import { dirname, join } from 'node:path' import { createHash, randomUUID } from 'node:crypto' import type { SkillLearningProjectContext as BaseSkillLearningProjectContext, - SkillLearningScope, SkillObservation as BaseSkillObservation, SkillObservationEvent, SkillObservationOutcome, diff --git a/src/services/skillLearning/toolEventObserver.ts b/src/services/skillLearning/toolEventObserver.ts index 2e29710f6b..8c60660cfa 100644 --- a/src/services/skillLearning/toolEventObserver.ts +++ b/src/services/skillLearning/toolEventObserver.ts @@ -65,7 +65,7 @@ export function pruneEmittedTurns(): void { // Prune over-sized Sets first. FIFO by insertion order — NOT by turn // number magnitude. Non-monotonic turn ordering (e.g. replayed transcripts // or nested tool chains) should not cause us to evict the wrong entries. - for (const [sessionId, turns] of emittedTurns) { + for (const [_sessionId, turns] of emittedTurns) { if (turns.size > EMITTED_TURNS_SET_MAX) { const iter = turns.values() const toDrop = turns.size - EMITTED_TURNS_SET_KEEP diff --git a/src/services/skillSearch/localSearch.ts b/src/services/skillSearch/localSearch.ts index 5b2d2c643f..34c9535a25 100644 --- a/src/services/skillSearch/localSearch.ts +++ b/src/services/skillSearch/localSearch.ts @@ -270,7 +270,6 @@ function cosineSimilarity( const DISPLAY_MIN_SCORE = Number( process.env.SKILL_SEARCH_DISPLAY_MIN_SCORE ?? '0.10', ) -const NAME_MATCH_BONUS = 0.4 const NAME_MATCH_MIN_LENGTH = 4 const CJK_MIN_BIGRAM_MATCHES = 2 diff --git a/src/services/skillSearch/prefetch.ts b/src/services/skillSearch/prefetch.ts index 1f8ac7c19c..a229a290a1 100644 --- a/src/services/skillSearch/prefetch.ts +++ b/src/services/skillSearch/prefetch.ts @@ -307,7 +307,7 @@ export async function collectSkillDiscoveryPrefetch( export async function getTurnZeroSkillDiscovery( input: string, - messages: Message[], + _messages: Message[], context: ToolUseContext, ): Promise { if (!isSkillSearchEnabled()) return null diff --git a/src/services/tools/toolHooks.ts b/src/services/tools/toolHooks.ts index e5b84d2f89..4694680642 100644 --- a/src/services/tools/toolHooks.ts +++ b/src/services/tools/toolHooks.ts @@ -45,7 +45,7 @@ export async function* runPostToolUseHooks( toolResponse: Output, requestId: string | undefined, mcpServerType: McpServerType, - mcpServerBaseUrl: string | undefined, + _mcpServerBaseUrl: string | undefined, ): AsyncGenerator> { const postToolStartTime = Date.now() try { @@ -204,7 +204,7 @@ export async function* runPostToolUseFailureHooks( isInterrupt: boolean | undefined, requestId: string | undefined, mcpServerType: McpServerType, - mcpServerBaseUrl: string | undefined, + _mcpServerBaseUrl: string | undefined, ): AsyncGenerator< MessageUpdateLazy> > { @@ -448,7 +448,7 @@ export async function* runPreToolUseHooks( messageId: string, requestId: string | undefined, mcpServerType: McpServerType, - mcpServerBaseUrl: string | undefined, + _mcpServerBaseUrl: string | undefined, ): AsyncGenerator< | { type: 'message' diff --git a/src/types/hooks.ts b/src/types/hooks.ts index 313ceb4393..9f1ae3b735 100644 --- a/src/types/hooks.ts +++ b/src/types/hooks.ts @@ -175,9 +175,6 @@ export const hookJSONOutputSchema = lazySchema(() => { return z.union([asyncHookResponseSchema, syncHookResponseSchema()]) }) -// Infer the TypeScript type from the schema -type SchemaHookJSONOutput = z.infer> - // Type guard function to check if response is sync export function isSyncHookJSONOutput( json: HookJSONOutput, diff --git a/src/types/message.ts b/src/types/message.ts index 26bb8ecd51..a3dbb85658 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -53,8 +53,6 @@ import type { import type { AssistantMessage, CollapsibleMessage, - NormalizedAssistantMessage, - NormalizedUserMessage, UserMessage, } from '@ant/model-provider' import type { UUID } from 'crypto' diff --git a/src/types/permissions.ts b/src/types/permissions.ts index b0bfe917c0..3947791331 100644 --- a/src/types/permissions.ts +++ b/src/types/permissions.ts @@ -6,7 +6,6 @@ * to avoid circular dependencies. */ -import { feature } from 'bun:bundle' import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' // ============================================================================ diff --git a/src/utils/attachments.ts b/src/utils/attachments.ts index 8028d648aa..d53c16382a 100644 --- a/src/utils/attachments.ts +++ b/src/utils/attachments.ts @@ -18,7 +18,7 @@ import { import { FileTooLargeError, readFileInRange } from './readFileInRange.js' import { expandPath } from './path.js' import { countCharInString } from './stringUtils.js' -import { count, uniq } from './array.js' +import { uniq } from './array.js' import { getFsImplementation } from './fsOperations.js' import { readdir, stat } from 'fs/promises' import type { IDESelection } from '../hooks/useIdeSelection.js' @@ -37,9 +37,7 @@ import { import { getPlanFilePath, getPlan } from './plans.js' import { getConnectedIdeName } from './ide.js' import { - filterInjectedMemoryFiles, getManagedAndUserConditionalRules, - getMemoryFiles, getMemoryFilesForNestedDirectory, getConditionalRulesForCwdLevelDirectory, type MemoryFileInfo, @@ -63,7 +61,6 @@ import { isValidImagePaste, } from 'src/types/textInputTypes.js' import { randomUUID, type UUID } from 'crypto' -import { getSettings_DEPRECATED } from './settings/settings.js' import { getSnippetForTwoFileDiff } from '@claude-code-best/builtin-tools/tools/FileEditTool/utils.js' import type { ContentBlockParam, @@ -72,7 +69,7 @@ import type { } from '@anthropic-ai/sdk/resources/messages.mjs' import { maybeResizeAndDownsampleImageBlock } from './imageResizer.js' import type { PastedContent } from './config.js' -import { getGlobalConfig } from './config.js' +import { getSettings_DEPRECATED } from './settings/settings.js' import { getDefaultSonnetModel, getDefaultHaikuModel, @@ -3533,7 +3530,7 @@ async function getAsyncHookResponseAttachments(): Promise { hookName, hookEvent, toolName, - pluginId, + pluginId: _pluginId, stdout, stderr, exitCode, diff --git a/src/utils/autonomyStatus.ts b/src/utils/autonomyStatus.ts index 950aabbf70..71e85283ec 100644 --- a/src/utils/autonomyStatus.ts +++ b/src/utils/autonomyStatus.ts @@ -1,5 +1,4 @@ import { readdir } from 'fs/promises' -import { join } from 'path' import { queryDaemonStatus } from '../daemon/state.js' import { listLiveSessions } from '../cli/bg.js' import { diff --git a/src/utils/betas.ts b/src/utils/betas.ts index 892fe46337..f252bd47d5 100644 --- a/src/utils/betas.ts +++ b/src/utils/betas.ts @@ -23,7 +23,7 @@ import { import { OAUTH_BETA_HEADER } from '../constants/oauth.js' import { isClaudeAISubscriber } from './auth.js' import { has1mContext } from './context.js' -import { isEnvDefinedFalsy, isEnvTruthy } from './envUtils.js' +import { isEnvTruthy } from './envUtils.js' import { getCanonicalName } from './model/model.js' import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js' import { diff --git a/src/utils/claudemd.ts b/src/utils/claudemd.ts index 89710315f8..b8c0cc9a39 100644 --- a/src/utils/claudemd.ts +++ b/src/utils/claudemd.ts @@ -37,7 +37,6 @@ import { join, parse, relative, - sep, } from 'path' import picomatch from 'picomatch' import { logEvent } from 'src/services/analytics/index.js' diff --git a/src/utils/computerUse/executorCrossPlatform.ts b/src/utils/computerUse/executorCrossPlatform.ts index e911d1afda..c0818b3b42 100644 --- a/src/utils/computerUse/executorCrossPlatform.ts +++ b/src/utils/computerUse/executorCrossPlatform.ts @@ -39,7 +39,6 @@ import { sleep } from '../sleep.js' import { CLI_CU_CAPABILITIES, CLI_HOST_BUNDLE_ID } from './common.js' import { validateHwnd } from './win32/shared.js' import { loadPlatform } from './platforms/index.js' -import type { Platform } from './platforms/index.js' // --------------------------------------------------------------------------- // Helpers for HWND-bound mode @@ -197,7 +196,7 @@ function augmentScreenshot( // Executor // --------------------------------------------------------------------------- -export function createCrossPlatformExecutor(opts: { +export function createCrossPlatformExecutor(_opts: { getMouseAnimationEnabled: () => boolean getHideBeforeActionEnabled: () => boolean }): ComputerExecutor { @@ -499,7 +498,12 @@ $i = New-Object MUp+INPUT; $i.type=0; $i.mi.dwFlags=0x0004; [MUp]::SendInput(1, await (this as any).mouseUp() }, - async scroll(x: number, y: number, dx: number, dy: number): Promise { + async scroll( + _x: number, + _y: number, + dx: number, + dy: number, + ): Promise { if (dy !== 0) await platform.input.scroll(dy, 'vertical') if (dx !== 0) await platform.input.scroll(dx, 'horizontal') }, diff --git a/src/utils/computerUse/gates.ts b/src/utils/computerUse/gates.ts index b18af2a4a6..64c7f831a6 100644 --- a/src/utils/computerUse/gates.ts +++ b/src/utils/computerUse/gates.ts @@ -1,7 +1,6 @@ import type { CoordinateMode, CuSubGates } from '@ant/computer-use-mcp/types' import { getDynamicConfig_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' -import { getSubscriptionType } from '../auth.js' import { isEnvTruthy } from '../envUtils.js' type ChicagoConfig = CuSubGates & { diff --git a/src/utils/computerUse/win32/inputIndicator.ts b/src/utils/computerUse/win32/inputIndicator.ts index 0a6f6da0ca..42450d2b78 100644 --- a/src/utils/computerUse/win32/inputIndicator.ts +++ b/src/utils/computerUse/win32/inputIndicator.ts @@ -22,7 +22,6 @@ const INDICATOR_HEIGHT = 28 const FADE_AFTER_MS = 2000 const BG_COLOR = '30, 30, 30' // dark background const TEXT_COLOR = '220, 220, 220' // light text -const ACCENT_COLOR = '80, 200, 80' // green accent for active let indicatorProc: ReturnType | null = null let stopFile: string | null = null diff --git a/src/utils/computerUse/win32/windowMessage.ts b/src/utils/computerUse/win32/windowMessage.ts index 5b904e1ba2..b3eeff038a 100644 --- a/src/utils/computerUse/win32/windowMessage.ts +++ b/src/utils/computerUse/win32/windowMessage.ts @@ -12,9 +12,6 @@ import { validateHwnd, runPs, VK_MAP, MODIFIER_KEYS } from './shared.js' -/** Character count above which we switch to clipboard paste */ -const CLIPBOARD_THRESHOLD = 32 - /** Cache findEditChild results — window structure doesn't change while bound */ const editChildCache = new Map() @@ -275,46 +272,6 @@ function buildWmCharLines(hwnd: string, text: string): string[] { return lines } -/** - * Paste text via clipboard into the target window. - * Uses Clipboard.SetText() + SendMessageW(Ctrl+V). - * NO global APIs (SendInput/keybd_event/SendKeys) — only window-targeted messages. - */ -function pasteViaClipboard(hwnd: string, text: string): boolean { - // Escape single quotes for PowerShell string literal - const escaped = text.replace(/'/g, "''") - const hwndExpr = `[IntPtr]::new([long]${hwnd})` - const script = `${WINMSG_TYPE} -Add-Type -AssemblyName System.Windows.Forms - -# Save current clipboard -$saved = $null -try { $saved = [System.Windows.Forms.Clipboard]::GetText() } catch {} - -# Set our text -[System.Windows.Forms.Clipboard]::SetText('${escaped}') - -# Ctrl+V via PostMessage to the target window (NOT global keybd_event) -# Must use PostMessage + correct lParam (scan code) for Windows Terminal / ConPTY -[WinMsg]::PostMessage(${hwndExpr}, [WinMsg]::WM_KEYDOWN, [IntPtr]0x11, [WinMsg]::KeyDownLParam(0x11)) # Ctrl down -[WinMsg]::PostMessage(${hwndExpr}, [WinMsg]::WM_KEYDOWN, [IntPtr]0x56, [WinMsg]::KeyDownLParam(0x56)) # V down -[WinMsg]::PostMessage(${hwndExpr}, [WinMsg]::WM_KEYUP, [IntPtr]0x56, [WinMsg]::KeyUpLParam(0x56)) # V up -[WinMsg]::PostMessage(${hwndExpr}, [WinMsg]::WM_KEYUP, [IntPtr]0x11, [WinMsg]::KeyUpLParam(0x11)) # Ctrl up - -# Brief wait for paste to complete -Start-Sleep -Milliseconds 50 - -# Restore clipboard -if ($saved -ne $null -and $saved -ne '') { - try { [System.Windows.Forms.Clipboard]::SetText($saved) } catch {} -} else { - try { [System.Windows.Forms.Clipboard]::Clear() } catch {} -} -Write-Output 'OK' -` - return runPs(script) === 'OK' -} - /** * Send text to a window via WM_CHAR per Unicode codepoint. * Always uses the WM_CHAR path — reliable across all window types including diff --git a/src/utils/fileHistory.ts b/src/utils/fileHistory.ts index 79eb0958b3..414c312abc 100644 --- a/src/utils/fileHistory.ts +++ b/src/utils/fileHistory.ts @@ -51,7 +51,9 @@ export type FileHistoryState = { snapshotSequence: number } -const MAX_SNAPSHOTS = 100 +// Disabled: file checkpointing causes unbounded memory growth (100 snapshots × full file backups). +// See heap snapshot analysis — re-enable only after switching to incremental diffs. +const MAX_SNAPSHOTS = 20 export type DiffStats = | { filesChanged?: string[] diff --git a/src/utils/lanBeacon.ts b/src/utils/lanBeacon.ts index b54adab33e..e938611ccb 100644 --- a/src/utils/lanBeacon.ts +++ b/src/utils/lanBeacon.ts @@ -87,7 +87,7 @@ export class LanBeacon extends EventEmitter { // Non-fatal — multicast may not be supported on this network }) - this.socket.on('message', (buf, rinfo) => { + this.socket.on('message', (buf, _rinfo) => { try { const msg = JSON.parse(buf.toString()) as LanAnnounce if (msg.proto !== 'claude-pipe-v1') return diff --git a/src/utils/messages.ts b/src/utils/messages.ts index 494d9f4647..6d628f8953 100644 --- a/src/utils/messages.ts +++ b/src/utils/messages.ts @@ -1431,7 +1431,7 @@ export function updateMessageLookupsIncremental( const msg = messages[i]! if (msg.type === 'assistant') { const aMsg = msg as AssistantMessage - const id = aMsg.message.id! + const _id = aMsg.message.id! if (Array.isArray(aMsg.message.content)) { const newToolUseIDs: string[] = [] for (const content of aMsg.message.content) { diff --git a/src/utils/model/modelOptions.ts b/src/utils/model/modelOptions.ts index 754963955b..7e9bb5f11f 100644 --- a/src/utils/model/modelOptions.ts +++ b/src/utils/model/modelOptions.ts @@ -158,15 +158,6 @@ function getCustomOpusOption(): ModelOption | undefined { } } -function getOpus41Option(): ModelOption { - return { - value: 'opus', - label: 'Opus 4.1', - description: `Opus 4.1 · Legacy`, - descriptionForModel: 'Opus 4.1 - legacy version', - } -} - function getOpus47Option(fastMode = false): ModelOption { const is3P = getAPIProvider() !== 'firstParty' return { diff --git a/src/utils/performanceShim.ts b/src/utils/performanceShim.ts new file mode 100644 index 0000000000..174518bb6f --- /dev/null +++ b/src/utils/performanceShim.ts @@ -0,0 +1,165 @@ +/** + * Performance shim — replaces globalThis.performance to prevent JSC's C++ Vector + * from growing without bound. + * + * In Bun, globalThis.performance is JSC's native Performance object. It stores + * marks, measures, and resource timings in a C++ Vector that never shrinks even + * after clearMarks(). Long-running sessions (daemon, /loop) accumulate hundreds + * of MB of dead capacity. + * + * This shim keeps performance.now() on the native object (fast, no memory cost) + * but redirects mark/measure/getEntries operations to a plain JS Map that the GC + * can reclaim. Third-party code (React reconciler, OTel/Langfuse) uses + * performance.now() for timing — that stays native. The accumulating operations + * go to GC-able JS memory instead. + * + * MUST be installed before React/OTel import — see cli.tsx first import. + */ + +const original = globalThis.performance + +// JS-backed storage — fully GC-able +const marks = new Map() +const measures = new Map< + string, + { name: string; startTime: number; duration: number } +>() + +function now(): number { + return original.now() +} + +function mark(name: string): PerformanceMark { + marks.set(name, now()) + // Return a minimal PerformanceMark-like object to satisfy the interface. + // React/OTel only use mark() for side effects, not the return value. + return { + name, + entryType: 'mark', + startTime: marks.get(name)!, + duration: 0, + } as PerformanceMark +} + +function measure( + name: string, + startMarkOrOptions?: string | MeasureOptions, + endMark?: string, +): void { + let startTime: number + let duration: number + + if (typeof startMarkOrOptions === 'string') { + const start = marks.get(startMarkOrOptions) + const end = endMark ? marks.get(endMark) : now() + startTime = start ?? now() + duration = (end ?? now()) - startTime + } else if (startMarkOrOptions && typeof startMarkOrOptions === 'object') { + startTime = startMarkOrOptions.start ?? 0 + duration = (startMarkOrOptions.end ?? now()) - startTime + } else { + startTime = 0 + duration = now() + } + + measures.set(name, { name, startTime, duration }) +} + +interface MeasureOptions { + start?: number + end?: number + detail?: unknown +} + +interface PerformanceEntryLike { + readonly name: string + readonly entryType: string + readonly startTime: number + readonly duration: number +} + +function getEntriesByType(type: string): PerformanceEntryLike[] { + if (type === 'mark') { + return [...marks.entries()].map(([name, startTime]) => ({ + name, + entryType: 'mark', + startTime, + duration: 0, + })) + } + if (type === 'measure') { + return [...measures.values()].map(m => ({ + name: m.name, + entryType: 'measure', + startTime: m.startTime, + duration: m.duration, + })) + } + return [] +} + +function getEntriesByName(name: string, type?: string): PerformanceEntryLike[] { + const entries = getEntriesByType(type ?? 'mark').concat( + type === undefined ? getEntriesByType('measure') : [], + ) + return entries.filter(e => e.name === name) +} + +function clearMarks(name?: string): void { + if (name !== undefined) { + marks.delete(name) + } else { + marks.clear() + } +} + +function clearMeasures(name?: string): void { + if (name !== undefined) { + measures.delete(name) + } else { + measures.clear() + } +} + +// Plain object shim — must NOT inherit from Performance.prototype because +// native getters (onresourcetimingbufferfull, timeOrigin, toJSON) check +// that `this` is an actual JSC Performance instance and throw otherwise. +const shim = { + now, + mark, + measure: measure as typeof performance.measure, + getEntriesByType: getEntriesByType as typeof performance.getEntriesByType, + getEntriesByName: getEntriesByName as typeof performance.getEntriesByName, + clearMarks: clearMarks as typeof performance.clearMarks, + clearMeasures: clearMeasures as typeof performance.clearMeasures, + clearResourceTimings: (() => {}) as typeof performance.clearResourceTimings, + setResourceTimingBufferSize: + (() => {}) as typeof performance.setResourceTimingBufferSize, + // Delegate read-only properties to the original + get timeOrigin() { + return original.timeOrigin + }, + get onresourcetimingbufferfull() { + return (original as any).onresourcetimingbufferfull + }, + set onresourcetimingbufferfull(_v: any) { + // no-op — prevent accumulation + }, + toJSON() { + return original.toJSON() + }, +} as typeof performance + +/** + * Install the shim onto globalThis.performance. Safe to call multiple times. + * Must run before React and OTel import to prevent them from capturing the + * native Performance reference. + */ +export function installPerformanceShim(): void { + if ((globalThis as any).__performanceShimInstalled) return + ;(globalThis as any).__performanceShimInstalled = true + globalThis.performance = shim +} + +// Auto-install on import +installPerformanceShim() diff --git a/src/utils/permissions/PermissionMode.ts b/src/utils/permissions/PermissionMode.ts index ac02d9bbfd..4294391eb5 100644 --- a/src/utils/permissions/PermissionMode.ts +++ b/src/utils/permissions/PermissionMode.ts @@ -1,4 +1,3 @@ -import { feature } from 'bun:bundle' import z from 'zod/v4' import { PAUSE_ICON } from '../../constants/figures.js' // Types extracted to src/types/permissions.ts to break import cycles diff --git a/src/utils/permissions/bypassPermissionsKillswitch.ts b/src/utils/permissions/bypassPermissionsKillswitch.ts index 393f0e10a8..533e840f23 100644 --- a/src/utils/permissions/bypassPermissionsKillswitch.ts +++ b/src/utils/permissions/bypassPermissionsKillswitch.ts @@ -1,6 +1,5 @@ import { feature } from 'bun:bundle' import { useEffect, useRef } from 'react' -import { useNotifications } from 'src/context/notifications.js' import { toError } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { getIsRemoteMode } from '../../bootstrap/state.js' diff --git a/src/utils/permissions/getNextPermissionMode.ts b/src/utils/permissions/getNextPermissionMode.ts index 67a077d8a1..7352edf6c2 100644 --- a/src/utils/permissions/getNextPermissionMode.ts +++ b/src/utils/permissions/getNextPermissionMode.ts @@ -1,5 +1,4 @@ import type { ToolPermissionContext } from '../../Tool.js' -import { logForDebugging } from '../debug.js' import type { PermissionMode } from './PermissionMode.js' import { transitionPermissionMode } from './permissionSetup.js' diff --git a/src/utils/permissions/permissionSetup.ts b/src/utils/permissions/permissionSetup.ts index 7111077459..4242dfc13e 100644 --- a/src/utils/permissions/permissionSetup.ts +++ b/src/utils/permissions/permissionSetup.ts @@ -874,7 +874,7 @@ export async function initializeToolPermissionContext({ disallowedToolsCli, baseToolsCli, permissionMode, - allowDangerouslySkipPermissions, + allowDangerouslySkipPermissions: _allowDangerouslySkipPermissions, addDirs, }: { allowedToolsCli: string[] diff --git a/src/utils/pipeRegistry.ts b/src/utils/pipeRegistry.ts index 8e5554f8ff..da2b07a5f7 100644 --- a/src/utils/pipeRegistry.ts +++ b/src/utils/pipeRegistry.ts @@ -11,7 +11,6 @@ import { readFile, writeFile, unlink, mkdir } from 'fs/promises' import { join } from 'path' import { createHash } from 'crypto' -import { getClaudeConfigHomeDir } from './envUtils.js' import { isPipeAlive, getPipesDir } from './pipeTransport.js' import type { TcpEndpoint } from './pipeTransport.js' import type { LanAnnounce } from './lanBeacon.js' diff --git a/src/utils/promptEditor.ts b/src/utils/promptEditor.ts index fa23b28185..685ddcaff8 100644 --- a/src/utils/promptEditor.ts +++ b/src/utils/promptEditor.ts @@ -106,7 +106,7 @@ export function editFileInEditor(filePath: string): EditorResult { */ function recollapsePastedContent( editedPrompt: string, - originalPrompt: string, + _originalPrompt: string, pastedContents: Record, ): string { let collapsed = editedPrompt diff --git a/src/utils/queryHelpers.ts b/src/utils/queryHelpers.ts index 8f8679778b..94dbb74044 100644 --- a/src/utils/queryHelpers.ts +++ b/src/utils/queryHelpers.ts @@ -1,5 +1,4 @@ import type { ToolUseBlock } from '@anthropic-ai/sdk/resources/index.mjs' -import last from 'lodash-es/last.js' import { getSessionId, isSessionPersistenceDisabled, diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index 63d1359d71..83e85f3afb 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -76,7 +76,7 @@ export function initSentry(): void { 'CancelError', ], - beforeSendTransaction(event) { + beforeSendTransaction(_event) { // Don't send performance transactions for now — errors only return null }, diff --git a/src/utils/settings/types.ts b/src/utils/settings/types.ts index 974a27be0f..36a5a5519c 100644 --- a/src/utils/settings/types.ts +++ b/src/utils/settings/types.ts @@ -3,10 +3,7 @@ import { z } from 'zod/v4' import { SandboxSettingsSchema } from '../../entrypoints/sandboxTypes.js' import { isEnvTruthy } from '../envUtils.js' import { lazySchema } from '../lazySchema.js' -import { - EXTERNAL_PERMISSION_MODES, - PERMISSION_MODES, -} from '../permissions/PermissionMode.js' +import { PERMISSION_MODES } from '../permissions/PermissionMode.js' import { MarketplaceSourceSchema } from '../plugins/schemas.js' import { CLAUDE_CODE_SETTINGS_SCHEMA_URL } from './constants.js' import { PermissionRuleSchema } from './permissionValidation.js' diff --git a/src/utils/teleport.tsx b/src/utils/teleport.tsx index 96cf898e1f..10f236ec7d 100644 --- a/src/utils/teleport.tsx +++ b/src/utils/teleport.tsx @@ -1,7 +1,6 @@ import axios from 'axios'; import chalk from 'chalk'; import { randomUUID } from 'crypto'; -import React from 'react'; import { getOriginalCwd, getSessionId } from 'src/bootstrap/state.js'; import { checkGate_CACHED_OR_BLOCKING } from 'src/services/analytics/growthbook.js'; import { diff --git a/src/utils/toolPool.ts b/src/utils/toolPool.ts index b756d91537..5021980c85 100644 --- a/src/utils/toolPool.ts +++ b/src/utils/toolPool.ts @@ -55,7 +55,7 @@ export function applyCoordinatorToolFilter(tools: Tools): Tools { export function mergeAndFilterTools( initialTools: Tools, assembled: Tools, - mode: ToolPermissionContext['mode'], + _mode: ToolPermissionContext['mode'], ): Tools { // Merge initialTools on top - they take precedence in deduplication. // initialTools may include built-in tools (from getTools() in REPL.tsx) which diff --git a/vite.config.ts b/vite.config.ts index 7a3a1d350a..8f52dc0a28 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -116,6 +116,9 @@ export default defineConfig({ // Compile-time constant replacement (MACRO.* defines) define: { ...getMacroDefines(), + // React production mode — eliminates _debugStack Error objects + // (6,889 objects × ~1.7KB = 12MB in development builds) + 'process.env.NODE_ENV': JSON.stringify('production'), }, resolve: {