diff --git a/config/knip.json b/config/knip.json new file mode 100644 index 00000000000..a48793b7902 --- /dev/null +++ b/config/knip.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://unpkg.com/knip@5/schema.json", + "entry": [ + "src/main/index.ts", + "src/preload/index.ts", + "src/preload/browser-window-close.ts", + "src/main/daemon/daemon-entry.ts", + "src/main/plugins/plugin-host-entry.ts", + "src/main/computer/sidecar-entry.ts", + "src/main/speech/stt-worker.ts", + "src/main/warp-themes/warp-theme-parser-worker.ts", + "src/main/ai-vault/session-scanner-opencode-sqlite-worker-entry.ts", + "src/main/ipc/parcel-watcher-process-entry.ts", + "src/main/hang-watchdog/main-thread-hang-watchdog-entry.ts", + "src/main/codex/codex-app-server-grant-entry.ts", + "src/main/agent-hooks/managed-agent-hook-controls.ts", + "src/main/claude-accounts/keychain.ts", + "src/renderer/src/main.tsx", + "src/renderer/src/popout.tsx", + "src/renderer/src/web/main.tsx", + "src/renderer/src/**/*.worker.ts", + "src/cli/index.ts", + "src/relay/relay.ts", + "src/relay/wsl-agent-hook-relay.ts", + "config/scripts/**/*.{mjs,cjs,js,ts}", + "build-plugins/**/*.ts", + "electron.vite.config.ts", + "vite.web.config.ts", + "config/vitest.config.ts", + "config/i18next.config.ts", + "config/electron-builder.config.cjs", + "config/oxlint-plugins/**/*.{js,mjs,ts}", + "tests/**/*.{ts,tsx,mjs}", + "**/*.test.{ts,tsx}" + ], + "project": ["src/**/*.{ts,tsx}", "config/scripts/**/*.{mjs,cjs,js,ts}", "build-plugins/**/*.ts"], + "ignore": ["mobile/**", "out/**", "dist/**", "node_modules/**", "resources/**"], + "ignoreDependencies": ["electron", "@types/*"], + "ignoreExportsUsedInFile": true, + "includeEntryExports": false +} diff --git a/package.json b/package.json index 7ccb8fabee3..52da12d0278 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "audit:code-quality:native": "oxlint --config config/oxlint-code-quality-native-plugins.json src config tests mobile --deny-warnings", "audit:code-quality:type-aware": "oxlint --type-aware --config config/oxlint-code-quality-type-aware.json src config tests --deny-warnings", "audit:react-doctor": "pnpm dlx react-doctor@0.9.1 . --yes --no-supply-chain --no-telemetry --blocking none", + "audit:dead-code": "pnpm dlx knip@5 --config config/knip.json", "check:code-quality:changed": "node config/scripts/check-changed-code-quality.mjs", "check:react-doctor:changed": "node config/scripts/check-react-doctor-changed.mjs", "check:zustand-selector-fanout": "node config/scripts/zustand-selector-fanout-benchmark.mjs --check", diff --git a/src/cli/runtime/environments.ts b/src/cli/runtime/environments.ts index 3009b11c9cb..c3594254587 100644 --- a/src/cli/runtime/environments.ts +++ b/src/cli/runtime/environments.ts @@ -20,10 +20,6 @@ export type EnvironmentAddResult = { environment: PublicKnownRuntimeEnvironment } -export type EnvironmentListResult = { - environments: PublicKnownRuntimeEnvironment[] -} - export type EnvironmentRemoveResult = { removed: PublicKnownRuntimeEnvironment } diff --git a/src/main/ai-vault/session-scanner-opencode-sqlite-worker-protocol.ts b/src/main/ai-vault/session-scanner-opencode-sqlite-worker-protocol.ts index f0a85edf891..4f67393537a 100644 --- a/src/main/ai-vault/session-scanner-opencode-sqlite-worker-protocol.ts +++ b/src/main/ai-vault/session-scanner-opencode-sqlite-worker-protocol.ts @@ -1,4 +1,4 @@ -import type { AiVaultScanIssue, AiVaultSession } from '../../shared/ai-vault-types' +import type { AiVaultScanIssue } from '../../shared/ai-vault-types' import type { SessionFileCandidate } from './session-scanner-types' // Why: request/response shapes shared by the worker entry and the main-thread @@ -30,8 +30,6 @@ export type OpenCodeSqliteListValue = { issues: AiVaultScanIssue[] } -export type OpenCodeSqliteParseValue = AiVaultSession | null - export type OpenCodeSqliteWorkerResponse = | { id: number; ok: true; value: unknown } | { id: number; ok: false; error: string } diff --git a/src/main/claude-usage/scanner.ts b/src/main/claude-usage/scanner.ts index 565f7e49c81..5006a73db0e 100644 --- a/src/main/claude-usage/scanner.ts +++ b/src/main/claude-usage/scanner.ts @@ -170,24 +170,6 @@ export async function listClaudeTranscriptFiles(): Promise { return [...new Set(files.flat())].sort() } -export async function getProcessedFileInfo(filePath: string): Promise { - const fileStat = await stat(filePath) - let lineCount = 0 - const lines = createInterface({ - input: createReadStream(filePath, { encoding: 'utf-8' }), - crlfDelay: Infinity - }) - for await (const _line of lines) { - lineCount++ - } - return { - path: filePath, - mtimeMs: fileStat.mtimeMs, - size: fileStat.size, - lineCount - } -} - async function getProcessedFileStat( filePath: string ): Promise> { @@ -784,7 +766,3 @@ export function getSessionProjectLabel(locationBreakdown: ClaudeUsageLocationBre } return 'Multiple locations' } - -export function getDefaultWorktreeLabel(pathValue: string): string { - return basename(pathValue) -} diff --git a/src/main/codex-usage/scanner.ts b/src/main/codex-usage/scanner.ts index eb5fbcaab14..86da9db678f 100644 --- a/src/main/codex-usage/scanner.ts +++ b/src/main/codex-usage/scanner.ts @@ -1179,17 +1179,3 @@ export function createWorktreeRefs( } return refs } - -export function getDefaultWorktreeLabel(pathValue: string): string { - return basename(pathValue) -} - -export function getSessionProjectLabel(locationBreakdown: CodexUsageLocationBreakdown[]): string { - if (locationBreakdown.length === 0) { - return 'Unknown location' - } - if (locationBreakdown.length === 1) { - return locationBreakdown[0].projectLabel - } - return 'Multiple locations' -} diff --git a/src/main/computer/macos-native-provider-paths.ts b/src/main/computer/macos-native-provider-paths.ts index 7ca8134f03f..bf0e9f3fc51 100644 --- a/src/main/computer/macos-native-provider-paths.ts +++ b/src/main/computer/macos-native-provider-paths.ts @@ -25,20 +25,3 @@ export function resolveMacOSComputerUseExecutablePath(): string | null { const executablePath = join(appPath, 'Contents', 'MacOS', 'orca-computer-use-macos') return existsSync(executablePath) ? executablePath : null } - -export function resolveMacOSNativeProviderPath(): string | null { - const override = process.env.ORCA_COMPUTER_MACOS_PROVIDER_PATH - if (override && existsSync(override)) { - return override - } - - const packaged = [join(process.resourcesPath ?? '', 'computer-use-macos/orca-computer-use-macos')] - const dev = [ - join(process.cwd(), 'native/computer-use-macos/.build/debug/orca-computer-use-macos'), - join(process.cwd(), 'native/computer-use-macos/.build/release/orca-computer-use-macos'), - resolve(__dirname, '../../native/computer-use-macos/.build/debug/orca-computer-use-macos') - ] - const candidates = process.resourcesPath ? [...packaged, ...dev] : dev - - return candidates.find((candidate) => candidate && existsSync(candidate)) ?? null -} diff --git a/src/main/daemon/terminal-private-mode-tracker.ts b/src/main/daemon/terminal-private-mode-tracker.ts deleted file mode 100644 index a8f20ef7a4d..00000000000 --- a/src/main/daemon/terminal-private-mode-tracker.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type { TerminalModes } from './types' - -// Why: PTY/SSH chunks can split a long combined DECSET before the final h/l. -// Keep parser state far beyond normal mode lists while still bounding memory. -const PRIVATE_MODE_SCAN_TAIL_LIMIT = 4096 - -type MouseTrackingMode = NonNullable - -// Why: @xterm/headless doesn't expose DECSET mouse/SGR encoding state, so the -// emulator mirrors it by scanning the raw byte stream; snapshots restore these -// modes on rehydrate. -export class TerminalPrivateModeTracker { - private scanTail = '' - mouseTrackingMode: MouseTrackingMode = 'none' - sgrMouseMode = false - sgrMousePixelsMode = false - - scan(data: string): void { - const input = this.scanTail + data - this.scanTail = this.extractScanTail(input) - // oxlint-disable-next-line no-control-regex -- terminal escape sequences require control chars - const privateModeRe = /\x1bc|\x1b\[\?([0-9;]+)([hl])|\x9b\?([0-9;]+)([hl])/g - let match: RegExpExecArray | null - while ((match = privateModeRe.exec(input)) !== null) { - if (match[0] === '\x1bc') { - this.mouseTrackingMode = 'none' - this.sgrMouseMode = false - this.sgrMousePixelsMode = false - continue - } - const params = match[1] ?? match[3] - const enabled = (match[2] ?? match[4]) === 'h' - for (const rawParam of params.split(';')) { - if (rawParam === '') { - continue - } - const param = Number(rawParam) - if (!Number.isInteger(param)) { - continue - } - if (param === 9) { - this.mouseTrackingMode = enabled ? 'x10' : 'none' - } - if (param === 1000) { - this.mouseTrackingMode = enabled ? 'vt200' : 'none' - } - if (param === 1002) { - this.mouseTrackingMode = enabled ? 'drag' : 'none' - } - if (param === 1003) { - this.mouseTrackingMode = enabled ? 'any' : 'none' - } - if (param === 1006) { - this.sgrMouseMode = enabled - this.sgrMousePixelsMode = false - } - if (param === 1016) { - this.sgrMouseMode = false - this.sgrMousePixelsMode = enabled - } - } - } - } - - private extractScanTail(input: string): string { - const start = Math.max(input.lastIndexOf('\x1b'), input.lastIndexOf('\x9b')) - if (start === -1) { - return '' - } - const tail = input.slice(start) - if (tail.length > PRIVATE_MODE_SCAN_TAIL_LIMIT) { - return '' - } - if (tail === '\x1b' || tail === '\x1b[' || tail === '\x9b') { - return tail - } - if (tail.startsWith('\x1b[?')) { - return this.isIncompleteParams(tail.slice(3)) ? tail : '' - } - if (tail.startsWith('\x9b?')) { - return this.isIncompleteParams(tail.slice(2)) ? tail : '' - } - return '' - } - - private isIncompleteParams(params: string): boolean { - return /^[0-9;]*$/.test(params) - } -} diff --git a/src/main/emulator/android/android-input-mapping.ts b/src/main/emulator/android/android-input-mapping.ts index 4805800f91d..d068e450e47 100644 --- a/src/main/emulator/android/android-input-mapping.ts +++ b/src/main/emulator/android/android-input-mapping.ts @@ -29,14 +29,6 @@ export function normalizedToDevicePixels( } } -export type AndroidHardwareButton = - | 'home' - | 'back' - | 'recents' - | 'power' - | 'volume_up' - | 'volume_down' - // Android KeyEvent KEYCODE_* values, including the common aliases agents use. const BUTTON_KEYCODES: Record = { home: 3, diff --git a/src/main/emulator/emulator-types.ts b/src/main/emulator/emulator-types.ts index d7bc31a26c5..f675d11bc66 100644 --- a/src/main/emulator/emulator-types.ts +++ b/src/main/emulator/emulator-types.ts @@ -14,9 +14,3 @@ export type EmulatorSessionInfo = { streamCodec?: EmulatorStreamCodec backend?: EmulatorBackendKind } - -export type EmulatorCliTarget = { - worktreeId?: string - deviceUdid?: string - emulatorId?: string // Orca-generated id from list (for stability, like browserPageId) -} diff --git a/src/main/git/git-username.ts b/src/main/git/git-username.ts index 320817b7eb7..da91361d0b8 100644 --- a/src/main/git/git-username.ts +++ b/src/main/git/git-username.ts @@ -219,14 +219,6 @@ async function getGhLoginOutcome(): Promise { return probe } -/** - * Resolve the `gh` CLI's GitHub login without ever blocking the caller for - * longer than the probe wall. Never rejects; unknown resolves to ''. - */ -export async function getGhLoginAsync(): Promise { - return (await getGhLoginOutcome()).login -} - async function readGitStdout(repoPath: string, args: string[]): Promise { try { const { stdout } = await gitExecFileAsync(args, { diff --git a/src/main/git/repo.ts b/src/main/git/repo.ts index ca1b4571c17..05d218e39db 100644 --- a/src/main/git/repo.ts +++ b/src/main/git/repo.ts @@ -911,14 +911,6 @@ async function listRemoteNames(path: string, options: LocalGitExecOptions = {}): } } -/** - * Parse `git for-each-ref --format=%(refname)%00%(refname:short)` stdout into a deduped list of short - * refs, dropping `/HEAD` pseudo-refs. Shared with the SSH branch in ipc/repos.ts so filtering can't drift. - */ -export function parseAndFilterSearchRefs(stdout: string, limit: number): string[] { - return parseAndFilterSearchRefDetails(stdout, limit).map((entry) => entry.refName) -} - export function parseAndFilterSearchRefDetails( stdout: string, limit: number, diff --git a/src/main/github/github-api-repository.ts b/src/main/github/github-api-repository.ts index 08461f849e9..3a61b9387da 100644 --- a/src/main/github/github-api-repository.ts +++ b/src/main/github/github-api-repository.ts @@ -65,11 +65,6 @@ export function _resetOriginGitHubApiRepositoryCache(): void { originRepoInFlight.clear() } -/** @internal - exposed for cache-bound tests only */ -export function _getOriginGitHubApiRepositoryCacheSize(): number { - return originRepoCache.size -} - function pruneOriginRepoCache(now: number): void { for (const [key, entry] of originRepoCache) { if (entry.expiresAt <= now) { @@ -296,10 +291,6 @@ export async function resolveGitHubApiRepository( return null } -export function isGitHubDotComRepository(repository: GitHubApiRepository): boolean { - return isDefaultGitHubHost(repository.host) -} - // Why: the gh runner host-qualifies argv from `options.host`, so every known // host must be carried through. Pinning github.com prevents a process-level // GH_HOST from silently redirecting an otherwise unambiguous API request. diff --git a/src/main/github/github-enterprise-repository.ts b/src/main/github/github-enterprise-repository.ts index fd4401e3f49..89f8bf7bbd7 100644 --- a/src/main/github/github-enterprise-repository.ts +++ b/src/main/github/github-enterprise-repository.ts @@ -49,11 +49,6 @@ export function _resetGitHubHostAuthCache(): void { hostAuthInFlight.clear() } -/** @internal - exposed for cache-bound tests only */ -export function _getGitHubHostAuthCacheSize(): number { - return hostAuthCache.size -} - function pruneHostAuthCache(now: number): void { for (const [key, entry] of hostAuthCache) { if (entry.expiresAt <= now) { diff --git a/src/main/github/project-view.ts b/src/main/github/project-view.ts index 2e53eff357c..db344de7133 100644 --- a/src/main/github/project-view.ts +++ b/src/main/github/project-view.ts @@ -54,7 +54,7 @@ import { import { githubProjectHost } from '../../shared/github-project-identity' // Re-export the public API so existing `./project-view` call sites keep working; the split is internal-only. -export { isValidOwnerSlug, isValidRepoSlug, isValidSlug } from './project-view/internals' +export { isValidOwnerSlug, isValidRepoSlug } from './project-view/internals' export { classifyProjectError } from './project-view/project-error-classification' export { updateProjectItemFieldValue, diff --git a/src/main/github/project-view/internals.ts b/src/main/github/project-view/internals.ts index 5952118ee4c..1dd794370c7 100644 --- a/src/main/github/project-view/internals.ts +++ b/src/main/github/project-view/internals.ts @@ -82,12 +82,6 @@ export function isValidRepoSlug(value: unknown): value is string { ) } -// Backwards-compatible alias for callers that don't distinguish owner vs repo. -// Prefer `isValidOwnerSlug` / `isValidRepoSlug` at new call sites. -export function isValidSlug(value: unknown): value is string { - return isValidOwnerSlug(value) || isValidRepoSlug(value) -} - export function assertSlug( value: unknown, field: 'owner' | 'repo' diff --git a/src/main/gitlab/gl-utils.ts b/src/main/gitlab/gl-utils.ts index 32b25ab0717..a8a7b5705a0 100644 --- a/src/main/gitlab/gl-utils.ts +++ b/src/main/gitlab/gl-utils.ts @@ -1,11 +1,6 @@ -import { execFile } from 'node:child_process' -import { promisify } from 'node:util' import { gitExecFileAsync, glabExecFileAsync } from '../git/runner' import { parseGlabApiResponse, type GlabApiResponse } from './glab-api-response' -// Why: legacy generic execFile wrapper - only used by callers that don't need -// WSL-aware routing. Repo-scoped callers should use the runner exports below. -export const execFileAsync = promisify(execFile) export { glabExecFileAsync, gitExecFileAsync } export { classifyGlabError, classifyListIssuesError } from './glab-error-classification' export { diff --git a/src/main/ipc/feedback-image-attachments.ts b/src/main/ipc/feedback-image-attachments.ts index f574a7129ab..361fe8b3fc0 100644 --- a/src/main/ipc/feedback-image-attachments.ts +++ b/src/main/ipc/feedback-image-attachments.ts @@ -25,10 +25,6 @@ export function isSupportedFeedbackImageContentType(contentType: string): boolea return Object.hasOwn(FEEDBACK_IMAGE_EXTENSIONS, contentType) } -export function getSupportedFeedbackImageContentTypes(): string[] { - return Object.keys(FEEDBACK_IMAGE_EXTENSIONS) -} - export function feedbackImageFilename(index: number, contentType: string): string { return `feedback-image-${index + 1}.${FEEDBACK_IMAGE_EXTENSIONS[contentType]}` } diff --git a/src/main/ipc/parcel-watcher-host-subscriptions.ts b/src/main/ipc/parcel-watcher-host-subscriptions.ts index 24f1803ee68..3dad4dee73c 100644 --- a/src/main/ipc/parcel-watcher-host-subscriptions.ts +++ b/src/main/ipc/parcel-watcher-host-subscriptions.ts @@ -117,21 +117,6 @@ export function resolvePendingWatcherUnsubscribes( pendingUnsubscribes.clear() } -export function disposeWatcherSupervisorSubscriptions( - records: Map, - pendingUnsubscribes: Map void>, - cancelledSubscribesAwaitingChild: Set, - error: Error -): void { - for (const record of records.values()) { - resetPendingSubscribeAttempt(record) - takePendingSubscribe(record)?.reject(error) - } - resolvePendingWatcherUnsubscribes(pendingUnsubscribes) - cancelledSubscribesAwaitingChild.clear() - records.clear() -} - export type CreateHostWatcherSubscriptionOptions = { record: WatcherProcessSubscriptionRecord records: Map diff --git a/src/main/ipc/parcel-watcher-process.ts b/src/main/ipc/parcel-watcher-process.ts index 978edb21c6b..223fed564f8 100644 --- a/src/main/ipc/parcel-watcher-process.ts +++ b/src/main/ipc/parcel-watcher-process.ts @@ -21,9 +21,6 @@ export type { } from './parcel-watcher-process-protocol' const sharedWatcherProcessSupervisor = new WatcherProcessSupervisor() -// Why: the canary pauses during legitimate crawls, so production callers need -// a separate liveness fuse once a child reports that it owns native setup. -export const WATCHER_PROCESS_SUBSCRIBE_TIMEOUT_MS = 60_000 // Why: healthy roots share one child; only fault quarantine scales to four, // containing a failed shard without paying that RSS cost during normal use. const runtimeWatcherProcessPool = new RuntimeWatcherProcessPool() diff --git a/src/main/ipc/runtime-environment-request-connections.ts b/src/main/ipc/runtime-environment-request-connections.ts index 361264d917e..946a0d30cf8 100644 --- a/src/main/ipc/runtime-environment-request-connections.ts +++ b/src/main/ipc/runtime-environment-request-connections.ts @@ -47,15 +47,6 @@ export function closeRemoteRuntimeRequestConnection(environmentId: string): void closeRemoteRuntimeSharedControlConnection(environmentId) } -export function closeAllRemoteRuntimeRequestConnections(): void { - for (const environmentId of Array.from(requestConnections.keys())) { - closeRemoteRuntimeRequestConnection(environmentId) - } - for (const environmentId of Array.from(sharedControlConnections.keys())) { - closeRemoteRuntimeSharedControlConnection(environmentId) - } -} - export function sendRemoteRuntimeSharedControlRequest( environmentId: string, pairing: PairingOffer, diff --git a/src/main/ipc/worktree-change-invalidators.ts b/src/main/ipc/worktree-change-invalidators.ts index a43657743b4..156ce0f722c 100644 --- a/src/main/ipc/worktree-change-invalidators.ts +++ b/src/main/ipc/worktree-change-invalidators.ts @@ -19,7 +19,3 @@ export function runWorktreeChangeInvalidators(repoId: string): void { } } } - -export function __resetWorktreeChangeInvalidatorsForTests(): void { - worktreeChangeInvalidators.clear() -} diff --git a/src/main/observability/index.ts b/src/main/observability/index.ts index 791653d272d..f891da51418 100644 --- a/src/main/observability/index.ts +++ b/src/main/observability/index.ts @@ -167,10 +167,6 @@ export async function shutdownObservability(): Promise { consent = null } -export function getObservabilityConsent(): ObservabilityConsent | null { - return consent -} - // ── Bundle / trace-folder operations exposed to IPC ───────────────────── export type DiagnosticsStatus = { diff --git a/src/main/observability/instrumentation.ts b/src/main/observability/instrumentation.ts index 3e944b23847..d4ab5d4e497 100644 --- a/src/main/observability/instrumentation.ts +++ b/src/main/observability/instrumentation.ts @@ -228,32 +228,6 @@ export async function withWorktreeRemoveStageSpan( }) } -export type PtySpanArgs = { - readonly stage: 'spawn' | 'exit' | 'recover' - readonly shell?: string - readonly cwd?: string -} - -/** Wrap a PTY-lifecycle event in a `pty.` span. The lifecycle is - * long-lived; callers typically use `startSpan` directly for the live - * session and call `withPtySpan` only for the spawn/exit moments. */ -export async function withPtySpan(meta: PtySpanArgs, fn: () => Promise | T): Promise { - return withSpan( - `pty.${meta.stage}`, - async (span) => { - span.setAttribute('pty.stage', meta.stage) - if (meta.shell) { - span.setAttribute('pty.shell', meta.shell) - } - if (meta.cwd) { - span.setAttribute('cwd', meta.cwd) - } - return await fn() - }, - { attributes: { kind: 'pty' } } - ) -} - export type UpdaterSpanArgs = { readonly stage: 'check' | 'download' | 'install' } diff --git a/src/main/observability/redactor.ts b/src/main/observability/redactor.ts index 173382bde58..cd5570f8577 100644 --- a/src/main/observability/redactor.ts +++ b/src/main/observability/redactor.ts @@ -234,14 +234,3 @@ export function redactSpan(span: RedactableSpan, mode: RedactorMode = 'client'): exit } } - -// Test-only introspection: lets tests verify the rule set without re-deriving it. - -export const _internalsForTests = { - PROVIDER_PATTERNS, - CLIENT_ATTR_BLOCKLIST, - SERVER_ATTR_BLOCKLIST_EXTRA, - LABELED_KV, - URL_USERINFO, - ENV_LINE -} diff --git a/src/main/pi/prefill-extension-source.ts b/src/main/pi/prefill-extension-source.ts index 054502c1c39..910c8cb796b 100644 --- a/src/main/pi/prefill-extension-source.ts +++ b/src/main/pi/prefill-extension-source.ts @@ -10,15 +10,6 @@ const PREFILL_ENV_VAR_BY_KIND: Record = { omp: 'ORCA_OMP_PREFILL' } -/** Pi's prefill env var. Exported for callers that need the literal name - * (renderer draft-launch plan builder, tests). OMP callers should read - * `ORCA_OMP_PREFILL_ENV_VAR` instead. */ -export const ORCA_PI_PREFILL_ENV_VAR = PREFILL_ENV_VAR_BY_KIND.pi - -/** OMP's prefill env var. Mirrors `ORCA_PI_PREFILL_ENV_VAR` for OMP launches - * so renderer plans and shell-ready restore lines can stay agent-scoped. */ -export const ORCA_OMP_PREFILL_ENV_VAR = PREFILL_ENV_VAR_BY_KIND.omp - export function getPiPrefillExtensionSource(kind: PiAgentKind): string { const envVar = PREFILL_ENV_VAR_BY_KIND[kind] return [ diff --git a/src/main/pi/titlebar-extension-service.ts b/src/main/pi/titlebar-extension-service.ts index 538d0a0be9d..66f491925ed 100644 --- a/src/main/pi/titlebar-extension-service.ts +++ b/src/main/pi/titlebar-extension-service.ts @@ -11,7 +11,6 @@ import { ORCA_PI_PREFILL_EXTENSION_FILE, getPiPrefillExtensionSource } from './prefill-extension-source' -export { ORCA_OMP_PREFILL_ENV_VAR, ORCA_PI_PREFILL_ENV_VAR } from './prefill-extension-source' import { ORCA_PI_EXTENSION_FILE, getPiTitlebarExtensionSource } from './titlebar-extension-source' import { isSafeDescendCandidate as sharedIsSafeDescendCandidate, diff --git a/src/main/plugins/plugin-enablement.ts b/src/main/plugins/plugin-enablement.ts index 44349ef087b..e81a8c7e522 100644 --- a/src/main/plugins/plugin-enablement.ts +++ b/src/main/plugins/plugin-enablement.ts @@ -14,8 +14,6 @@ import { verifyInstructionalPluginContent } from './plugin-instructional-content * IPC handlers and the headless RPC methods both route through here. */ -export type PluginConsentDecision = PluginConsentRequest['decision'] - /** Records the user's consent-dialog answer. Approving stores the CURRENT * consent fingerprint and clears any disable; declining disables so the plugin * never re-prompts on later launches. */ diff --git a/src/main/providers/pty-process-list-admission.ts b/src/main/providers/pty-process-list-admission.ts index f65a86aacea..bca3bf4fa18 100644 --- a/src/main/providers/pty-process-list-admission.ts +++ b/src/main/providers/pty-process-list-admission.ts @@ -145,17 +145,3 @@ export async function visitPtyProcessListingsInBatches( visit(listing.entry, listing.processes) } } - -export async function collectPtyProcessListings( - sources: Iterable, - load: (source: T) => Promise -): Promise { - const admission = new PtyProcessListAdmission() - const processes: PtyProcessInfo[] = [] - await visitPtyProcessListingsInBatches(sources, load, (_source, listing) => { - for (const process of listing) { - processes.push(admission.admit(process)) - } - }) - return processes -} diff --git a/src/main/providers/types.ts b/src/main/providers/types.ts index a36b222f5e4..23c36859a1f 100644 --- a/src/main/providers/types.ts +++ b/src/main/providers/types.ts @@ -1,9 +1,6 @@ // Aggregate provider contract surface. The three per-domain contracts live in // their own files; this barrel keeps every historical `providers/types` import -// path working and owns the registry that ties them together. -import type { IPtyProvider } from './pty-provider-contract' -import type { IFilesystemProvider } from './filesystem-provider-contract' -import type { IGitProvider } from './git-provider-contract' +// path working. // ─── PTY Provider ─────────────────────────────────────────────────── @@ -31,12 +28,3 @@ export type { // ─── Git Provider ─────────────────────────────────────────────────── export type { GitProviderStatusOptions, IGitProvider } from './git-provider-contract' - -// ─── Provider Registry ────────────────────────────────────────────── - -/** Routes operations by connectionId; null/undefined selects the local provider. */ -export type IProviderRegistry = { - getPtyProvider(connectionId: string | null | undefined): IPtyProvider - getFilesystemProvider(connectionId: string | null | undefined): IFilesystemProvider - getGitProvider(connectionId: string | null | undefined): IGitProvider -} diff --git a/src/main/rate-limits/grok-auth.ts b/src/main/rate-limits/grok-auth.ts index e055c022975..950ab1d3678 100644 --- a/src/main/rate-limits/grok-auth.ts +++ b/src/main/rate-limits/grok-auth.ts @@ -130,10 +130,6 @@ export function readGrokAuthSession(): GrokAuthReadResult { } } -export function hasGrokAuthSession(): boolean { - return readGrokAuthSession().status === 'ok' -} - const TOKEN_SKEW_MS = 5 * 60 * 1000 export function isGrokAccessTokenFresh(session: GrokAuthSession): boolean { diff --git a/src/main/runtime/orca-runtime-emulator.ts b/src/main/runtime/orca-runtime-emulator.ts index b1bcbe5c689..ac67464a533 100644 --- a/src/main/runtime/orca-runtime-emulator.ts +++ b/src/main/runtime/orca-runtime-emulator.ts @@ -321,21 +321,3 @@ export class RuntimeEmulatorCommands { return this.emulatorExec(params) } } - -// Singleton accessor pattern (mirror requireAgentBrowserBridge). -let emulatorBridgeInstance: EmulatorBridge | null = null - -export function setEmulatorBridge(bridge: EmulatorBridge | null): void { - emulatorBridgeInstance = bridge -} - -export function getEmulatorBridge(): EmulatorBridge | null { - return emulatorBridgeInstance -} - -export function requireEmulatorBridge(): EmulatorBridge { - if (!emulatorBridgeInstance) { - throw new EmulatorError('emulator_no_active', 'Emulator bridge not initialized') - } - return emulatorBridgeInstance -} diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index 698fd80f564..90792d63693 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -512,7 +512,7 @@ import { advanceTerminalTopologyRevision, hasHostAuthoritativeTerminalMembership } from './workspace-session-terminal-membership-authority' -import { RuntimeEmulatorCommands, setEmulatorBridge } from './orca-runtime-emulator' +import { RuntimeEmulatorCommands } from './orca-runtime-emulator' import type { EmulatorBridge } from '../emulator/emulator-bridge' import { RuntimeFileCommands } from './orca-runtime-files' import { RuntimeGitCommands } from './orca-runtime-git' @@ -5167,7 +5167,6 @@ export class OrcaRuntimeService { setEmulatorBridge(bridge: EmulatorBridge | null): void { this.emulatorBridge = bridge - setEmulatorBridge(bridge) } getEmulatorBridge(): EmulatorBridge | null { diff --git a/src/main/runtime/orchestration/groups.ts b/src/main/runtime/orchestration/groups.ts index 5d24f68a107..6f31f5df261 100644 --- a/src/main/runtime/orchestration/groups.ts +++ b/src/main/runtime/orchestration/groups.ts @@ -20,8 +20,6 @@ const AGENT_NAME_GROUPS = [ type AgentNameGroup = (typeof AGENT_NAME_GROUPS)[number] -export type GroupAddress = '@all' | '@idle' | `@${AgentNameGroup}` | `@worktree:${string}` - export function isGroupAddress(to: string): boolean { return to.startsWith('@') } diff --git a/src/main/runtime/rpc/schemas.ts b/src/main/runtime/rpc/schemas.ts index 5324695c090..fb7b09d8ceb 100644 --- a/src/main/runtime/rpc/schemas.ts +++ b/src/main/runtime/rpc/schemas.ts @@ -68,8 +68,6 @@ export const BrowserTarget = z.object({ page: OptionalString }) -export type BrowserTargetInput = z.infer - export function requiredString(message: string) { return z .unknown() diff --git a/src/main/shell-ready-marker-scanner.ts b/src/main/shell-ready-marker-scanner.ts index d0633fa1393..0e6250e1c4e 100644 --- a/src/main/shell-ready-marker-scanner.ts +++ b/src/main/shell-ready-marker-scanner.ts @@ -1,5 +1,4 @@ export const SHELL_READY_MARKER_PREFIX = '\x1b]777;orca-shell-ready' -export const SHELL_READY_MARKER = `${SHELL_READY_MARKER_PREFIX}\x07` export type ShellReadyScanState = { matchPos: number diff --git a/src/main/ssh/relay-protocol.ts b/src/main/ssh/relay-protocol.ts index 4a60237874a..4c5e258b01a 100644 --- a/src/main/ssh/relay-protocol.ts +++ b/src/main/ssh/relay-protocol.ts @@ -2,7 +2,6 @@ // 13-byte framing header matching VS Code's PersistentProtocol wire format. // See design-ssh-support.md § JSON-RPC Protocol Specification. -import { DEFAULT_SSH_RELAY_GRACE_PERIOD_SECONDS } from '../../shared/ssh-types' import { FrameDecoder, FrameDecoderContinuationError, @@ -41,9 +40,6 @@ export const MessageType = { export const KEEPALIVE_SEND_MS = 5_000 export const TIMEOUT_MS = 20_000 -/** Reconnection grace period (default, overridable by relay --grace-time). */ -export const DEFAULT_GRACE_TIME_MS = DEFAULT_SSH_RELAY_GRACE_PERIOD_SECONDS * 1000 - // ── Relay error codes ─────────────────────────────────────────────── export const RelayErrorCode = { diff --git a/src/main/ssh/sftp-namespace-resolution.ts b/src/main/ssh/sftp-namespace-resolution.ts index 5ee329a40c4..dd019a84d88 100644 --- a/src/main/ssh/sftp-namespace-resolution.ts +++ b/src/main/ssh/sftp-namespace-resolution.ts @@ -116,8 +116,8 @@ function realpathSftp(sftp: SFTPWrapper, remotePath: string): Promise { }) } -// Why: sftpPathExists collapses "absent" and "could not tell" into one boolean; -// namespace selection must never treat an inconclusive probe as absence. +// Why: namespace selection must never treat an inconclusive lstat as absence, +// so the probe reports "unknown" rather than collapsing to a boolean. function probeMarkerPath(sftp: SFTPWrapper, remotePath: string): Promise { return new Promise((resolve) => { sftp.lstat(remotePath, (err) => { diff --git a/src/main/ssh/sftp-upload.ts b/src/main/ssh/sftp-upload.ts index e962c7463fa..6344a7c1457 100644 --- a/src/main/ssh/sftp-upload.ts +++ b/src/main/ssh/sftp-upload.ts @@ -282,25 +282,3 @@ async function assertLocalUploadPathInsideRoot( throw new Error(`Path escaped upload root: ${candidatePath}`) } } - -/** - * Check whether a path exists on the remote via SFTP lstat. - * Returns true if the path exists (file, directory, or symlink). - */ -export function sftpPathExists(sftp: SFTPWrapper, remotePath: string): Promise { - return new Promise((resolve, reject) => { - sftp.lstat(remotePath, (err) => { - if (!err) { - resolve(true) - return - } - // Why: SFTP status code 2 = SSH_FX_NO_SUCH_FILE — the path does not - // exist, which is the expected "no collision" signal for deconfliction. - if ((err as { code?: number }).code === 2) { - resolve(false) - return - } - reject(err) - }) - }) -} diff --git a/src/main/ssh/ssh-pty-consumer-session.ts b/src/main/ssh/ssh-pty-consumer-session.ts index 4bfebc6360f..8bb272d7273 100644 --- a/src/main/ssh/ssh-pty-consumer-session.ts +++ b/src/main/ssh/ssh-pty-consumer-session.ts @@ -2,7 +2,6 @@ import { PTY_CONSUMER_SESSION_PROTOCOL_VERSION, type PtyConsumerSessionGrant } from '../../shared/pty-consumer-session' -import { DEFAULT_PTY_SOURCE_WINDOW_SU } from '../../shared/pty-source-credit-contract' import type { SshChannelMultiplexer } from './ssh-channel-multiplexer' export const SSH_PTY_OPEN_CLIENT_METHOD = 'pty.openClient' @@ -140,5 +139,3 @@ export async function openSshPtyConsumerSession( : {}) } } - -export const SSH_PTY_SOURCE_WINDOW_SU = DEFAULT_PTY_SOURCE_WINDOW_SU diff --git a/src/main/ssh/ssh-relay-session-managed-hooks.test.ts b/src/main/ssh/ssh-relay-session-managed-hooks.test.ts index 95492aa3a3a..b0acfeb29e2 100644 --- a/src/main/ssh/ssh-relay-session-managed-hooks.test.ts +++ b/src/main/ssh/ssh-relay-session-managed-hooks.test.ts @@ -15,7 +15,6 @@ const { muxRequestMock, openConsumerSessionMock } = vi.hoisted(() => ({ vi.mock('./ssh-relay-deploy', () => ({ deployAndLaunchRelay: vi.fn() })) vi.mock('./ssh-relay-deploy-helpers', () => ({ execCommand: vi.fn().mockResolvedValue('') })) vi.mock('./ssh-pty-consumer-session', () => ({ - SSH_PTY_SOURCE_WINDOW_SU: 256 * 1024, openSshPtyConsumerSession: openConsumerSessionMock })) vi.mock('./ssh-channel-multiplexer', () => ({ diff --git a/src/main/ssh/ssh-relay-session.test.ts b/src/main/ssh/ssh-relay-session.test.ts index eb81cda4cb1..570fca21fd4 100644 --- a/src/main/ssh/ssh-relay-session.test.ts +++ b/src/main/ssh/ssh-relay-session.test.ts @@ -21,7 +21,6 @@ vi.mock('./ssh-relay-deploy', () => ({ })) vi.mock('./ssh-pty-consumer-session', () => ({ - SSH_PTY_SOURCE_WINDOW_SU: 256 * 1024, openSshPtyConsumerSession: openConsumerSessionMock })) diff --git a/src/main/system-fonts.ts b/src/main/system-fonts.ts index 15ff9c28bde..20a8764548c 100644 --- a/src/main/system-fonts.ts +++ b/src/main/system-fonts.ts @@ -31,10 +31,6 @@ export async function listSystemFontFamilies(): Promise { return fontsPromise } -export function warmSystemFontFamilies(): void { - void listSystemFontFamilies() -} - function loadSystemFontFamilies(): Promise { if (process.platform === 'darwin') { return listMacFonts() diff --git a/src/main/telemetry/client.ts b/src/main/telemetry/client.ts index d1f2b2fa057..93bddaaab81 100644 --- a/src/main/telemetry/client.ts +++ b/src/main/telemetry/client.ts @@ -329,10 +329,6 @@ export function _setShuttingDownForTests(value: boolean): void { shuttingDown = value } -export function _getSessionIdForTests(): string | null { - return sessionId -} - export function _enableTransportForTests(enabled: boolean): void { testTransportEnabled = enabled } diff --git a/src/relay/fs-handler-file-read.ts b/src/relay/fs-handler-file-read.ts index 14672457cc6..4a6f663484d 100644 --- a/src/relay/fs-handler-file-read.ts +++ b/src/relay/fs-handler-file-read.ts @@ -2,12 +2,7 @@ import { open, readFile, stat } from 'node:fs/promises' import type { FileHandle } from 'node:fs/promises' import { extname } from 'node:path' import type { RelayDispatcher, RequestContext } from './dispatcher' -import { - MAX_CONCURRENT_STREAMS, - STREAM_ACK_WINDOW_CHUNKS, - STREAM_CHUNK_SIZE, - RelayErrorCode -} from './protocol' +import { MAX_CONCURRENT_STREAMS, STREAM_ACK_WINDOW_CHUNKS, STREAM_CHUNK_SIZE } from './protocol' import { TooManyStreamsError, type RelayStreamRegistry } from './fs-stream-registry' import { BINARY_PROBE_BYTES, @@ -360,7 +355,3 @@ async function readFullStreamChunk( } return totalRead } - -export function isTooManyStreamsError(err: unknown): err is TooManyStreamsError { - return err instanceof Error && (err as { code?: number }).code === RelayErrorCode.TooManyStreams -} diff --git a/src/renderer/src/components/automations/automation-page-parts.tsx b/src/renderer/src/components/automations/automation-page-parts.tsx index 7d96be1efa3..9b38cac5fc1 100644 --- a/src/renderer/src/components/automations/automation-page-parts.tsx +++ b/src/renderer/src/components/automations/automation-page-parts.tsx @@ -107,12 +107,3 @@ export function Field({ ) } - -export function Metric({ label, value }: { label: string; value: string }): React.JSX.Element { - return ( -
-
{label}
-
{value}
-
- ) -} diff --git a/src/renderer/src/components/browser-pane/browser-automation-visibility.ts b/src/renderer/src/components/browser-pane/browser-automation-visibility.ts index d18c331c801..6b092f2c950 100644 --- a/src/renderer/src/components/browser-pane/browser-automation-visibility.ts +++ b/src/renderer/src/components/browser-pane/browser-automation-visibility.ts @@ -72,11 +72,6 @@ export function isBrowserAutomationVisible(browserPageId: string): boolean { return (leaseCountsByPageId.get(browserPageId) ?? 0) > 0 } -export function useBrowserAutomationVisibility(browserPageId: string | null | undefined): boolean { - useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) - return Boolean(browserPageId && isBrowserAutomationVisible(browserPageId)) -} - export function useBrowserAutomationVisibilityForAny( browserPageIds: readonly (string | null | undefined)[] ): boolean { diff --git a/src/renderer/src/components/browser-pane/browser-page-zoom.ts b/src/renderer/src/components/browser-pane/browser-page-zoom.ts index 4a4f3924942..2a5233c5cf1 100644 --- a/src/renderer/src/components/browser-pane/browser-page-zoom.ts +++ b/src/renderer/src/components/browser-pane/browser-page-zoom.ts @@ -107,14 +107,6 @@ export function getBrowserPageZoomIndicatorState({ } } -export function dispatchBrowserPageZoomEvent(detail: BrowserPageZoomEventDetail): void { - window.dispatchEvent( - new CustomEvent(ORCA_BROWSER_PAGE_ZOOM_EVENT, { - detail - }) - ) -} - export function addBrowserPageZoomEventListener( callback: (detail: BrowserPageZoomEventDetail) => void ): () => void { diff --git a/src/renderer/src/components/dashboard/useDashboardData.ts b/src/renderer/src/components/dashboard/useDashboardData.ts index 71632ac93e6..65c57ab2f4e 100644 --- a/src/renderer/src/components/dashboard/useDashboardData.ts +++ b/src/renderer/src/components/dashboard/useDashboardData.ts @@ -1,18 +1,9 @@ -import { useMemo } from 'react' -import { useAppStore } from '@/store' -import { isExplicitAgentStatusFresh } from '@/lib/agent-status' -import { - AGENT_STATUS_STALE_AFTER_MS, - type AgentStatusEntry, - type AgentStatusState, - type AgentType, - type MigrationUnsupportedPtyEntry +import type { + AgentStatusEntry, + AgentStatusState, + AgentType } from '../../../../shared/agent-status-types' -import type { Repo, Worktree, TerminalTab } from '../../../../shared/types' -import { parsePaneKey } from '../../../../shared/stable-pane-id' -import { migrationUnsupportedToAgentStatusEntry } from '@/lib/migration-unsupported-agent-entry' - -// ─── Shared data types ──────────────────────────────────────────────────────── +import type { TerminalTab } from '../../../../shared/types' export type DashboardAgentRow = { /** Row identity. For 'subagent' rows this is a synthetic key (the child has @@ -37,171 +28,3 @@ export type DashboardAgentRow = { childCount: number } } - -// Why: the shape here is deliberately minimal. The per-card rendering pipeline -// is separate (WorktreeCardAgents + useWorktreeAgentRows read retained entries -// directly from the store). -export type DashboardWorktreeCard = { - repo: Repo - worktree: Worktree - agents: DashboardAgentRow[] -} - -export type DashboardProjectGroup = { - repo: Repo - worktrees: DashboardWorktreeCard[] -} - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -// Why: only surface agents that have reported state via a hook. A tab hosting -// a shell, a REPL before its first turn, or an agent we have no hook -// integration for will have no entry here — and that's correct. Agent rows -// represent *agent work in progress*, not "which terminals might contain an -// agent". -function buildAgentRowsForWorktree( - worktreeId: string, - tabsByWorktree: Record, - entriesByTabId: Map, - now: number -): DashboardAgentRow[] { - const tabs = tabsByWorktree[worktreeId] ?? [] - const rows: DashboardAgentRow[] = [] - - for (const tab of tabs) { - const explicitEntries = entriesByTabId.get(tab.id) ?? [] - for (const entry of explicitEntries) { - // Why: decay stale working/blocked/waiting entries to 'idle' when the hook - // stream has gone silent past AGENT_STATUS_STALE_AFTER_MS (30 min TTL). - // Without this, an agent process that exited without sending a final - // update would remain "working" forever — the Active/Blocked filters and - // the sidebar's running-agents count would mislead the user into chasing - // dead work. `done` is terminal and must NOT decay to idle: retention - // (collectRetainedAgentsOnDisappear) only retains rows whose prev state - // was 'done', so decaying a stale done → idle would silently drop the - // completion signal when the entry later disappears. - const isFresh = isExplicitAgentStatusFresh(entry, now, AGENT_STATUS_STALE_AFTER_MS) - const shouldDecay = - !isFresh && - (entry.state === 'working' || entry.state === 'blocked' || entry.state === 'waiting') - rows.push({ - paneKey: entry.paneKey, - entry, - tab, - agentType: entry.agentType ?? 'unknown', - rowSource: 'live', - state: shouldDecay ? 'idle' : entry.state, - // Why: the oldest stateHistory entry's startedAt is the agent's original - // "first seen" timestamp. When history is empty the entry has never - // transitioned state, so stateStartedAt (the moment the current — and - // only — state began) is the true first-seen timestamp. Do NOT fall back - // to updatedAt: it advances on every tool/prompt ping within the same - // state, which would corrupt oldest-first ordering and the "started … - // ago" display for long-running agents between state transitions. See - // agent-status.ts (stateStartedAt carry-forward on same-state pings). - startedAt: entry.stateHistory[0]?.startedAt ?? entry.stateStartedAt - }) - } - } - - return rows -} - -function buildDashboardData( - repos: Repo[], - worktreesByRepo: Record, - tabsByWorktree: Record, - agentStatusByPaneKey: Record, - migrationUnsupportedByPtyId: Record, - now: number -): DashboardProjectGroup[] { - // Why: build a tabId -> entries index once per computation instead of - // re-scanning every agent status entry inside the per-tab loop. paneKey is - // formatted as `${tabId}:${leafId}`; parsePaneKey also drops legacy numeric - // suffixes so stale rows do not remain routable after pane replay. - const entriesByTabId = new Map() - for (const [paneKey, entry] of Object.entries(agentStatusByPaneKey)) { - const parsed = parsePaneKey(paneKey) - if (!parsed) { - continue - } - const bucket = entriesByTabId.get(parsed.tabId) - if (bucket) { - bucket.push(entry) - } else { - entriesByTabId.set(parsed.tabId, [entry]) - } - } - for (const unsupported of Object.values(migrationUnsupportedByPtyId)) { - const entry = migrationUnsupportedToAgentStatusEntry(unsupported) - if (!entry) { - continue - } - const parsed = parsePaneKey(entry.paneKey) - if (!parsed) { - continue - } - const bucket = entriesByTabId.get(parsed.tabId) - if (bucket) { - bucket.push(entry) - } else { - entriesByTabId.set(parsed.tabId, [entry]) - } - } - - return repos.map((repo) => { - const worktrees = (worktreesByRepo[repo.id] ?? []) - .filter((w) => !w.isArchived) - .map((worktree) => { - const agents = buildAgentRowsForWorktree(worktree.id, tabsByWorktree, entriesByTabId, now) - return { repo, worktree, agents } satisfies DashboardWorktreeCard - }) - - return { repo, worktrees } satisfies DashboardProjectGroup - }) -} - -// ─── Hook ───────────────────────────────────────────────────────────────────── - -/** - * Cross-worktree aggregate of live agent rows. - * - * Not used to render anything directly — the inline list reads its own - * worktree-scoped slice via useWorktreeAgentRows. - */ -export function useDashboardData(): DashboardProjectGroup[] { - const repos = useAppStore((s) => s.repos) - const worktreesByRepo = useAppStore((s) => s.worktreesByRepo) - const tabsByWorktree = useAppStore((s) => s.tabsByWorktree) - const agentStatusByPaneKey = useAppStore((s) => s.agentStatusByPaneKey) - const migrationUnsupportedByPtyId = useAppStore((s) => s.migrationUnsupportedByPtyId) - // Why: agentStatusEpoch is included in the dependency array (but not in the - // computation itself) so the memo recomputes when freshness boundaries expire, - // even if no new PTY data arrives. - const agentStatusEpoch = useAppStore((s) => s.agentStatusEpoch) - - return useMemo( - // Why: Date.now() is read inside the memo (not as a dep) so stale-decay - // recalculates whenever agentStatusEpoch ticks. The epoch bumps when the - // freshness boundary crosses, driving re-evaluation without coupling to - // wall-clock time directly. - () => - buildDashboardData( - repos, - worktreesByRepo, - tabsByWorktree, - agentStatusByPaneKey, - migrationUnsupportedByPtyId, - Date.now() - ), - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - repos, - worktreesByRepo, - tabsByWorktree, - agentStatusByPaneKey, - migrationUnsupportedByPtyId, - agentStatusEpoch - ] - ) -} diff --git a/src/renderer/src/components/editor/NotesSendMenu.test.tsx b/src/renderer/src/components/editor/NotesSendMenu.test.tsx index d977ec5ae9b..a176594bc80 100644 --- a/src/renderer/src/components/editor/NotesSendMenu.test.tsx +++ b/src/renderer/src/components/editor/NotesSendMenu.test.tsx @@ -134,8 +134,7 @@ vi.mock('./ReviewNotesSendMenuContent', () => ({ vi.mock('@/lib/active-agent-note-send', () => ({ activeAgentNotesSendFailureMessage: (status: string) => status, getActiveTerminalNoteTarget: () => null, - sendNotesToActiveAgentSession: vi.fn(), - useCanSendNotesToActiveTerminal: () => false + sendNotesToActiveAgentSession: vi.fn() })) vi.mock('sonner', () => ({ diff --git a/src/renderer/src/components/editor/ReviewNotesSendMenuContent.test.tsx b/src/renderer/src/components/editor/ReviewNotesSendMenuContent.test.tsx index 2007b142702..92e7e21da80 100644 --- a/src/renderer/src/components/editor/ReviewNotesSendMenuContent.test.tsx +++ b/src/renderer/src/components/editor/ReviewNotesSendMenuContent.test.tsx @@ -91,8 +91,7 @@ vi.mock('@/lib/active-agent-note-send', () => ({ status: string, options: { explicitTarget?: boolean } = {} ) => (options.explicitTarget ? `selected:${status}` : status), - sendNotesToActiveAgentSession: harness.sendNotesToActiveAgentSession, - useCanSendNotesToActiveTerminal: () => true + sendNotesToActiveAgentSession: harness.sendNotesToActiveAgentSession })) vi.mock('@/lib/notes-send-agent-targets', () => ({ diff --git a/src/renderer/src/components/editor/markdown-round-trip.ts b/src/renderer/src/components/editor/markdown-round-trip.ts index fcfa39efdc0..2655ca225b3 100644 --- a/src/renderer/src/components/editor/markdown-round-trip.ts +++ b/src/renderer/src/components/editor/markdown-round-trip.ts @@ -7,11 +7,6 @@ import { createRichMarkdownHtmlSuperscriptLinkContext } from './rich-markdown-ht const roundTripCache = new Map() const MAX_CACHE_ENTRIES = 20 -export function canRoundTripRichMarkdown(content: string): boolean { - const output = getRichMarkdownRoundTripOutput(content) - return output !== null && normalizeMarkdown(content) === normalizeMarkdown(output) -} - export function getRichMarkdownRoundTripOutput(content: string): string | null { const cached = roundTripCache.get(content) if (cached !== undefined) { @@ -59,7 +54,3 @@ export function getRichMarkdownRoundTripOutput(content: string): string | null { return output } - -function normalizeMarkdown(content: string): string { - return content.replace(/\r\n/g, '\n').trimEnd() -} diff --git a/src/renderer/src/components/emulator-pane/emulator-keyboard-paste.ts b/src/renderer/src/components/emulator-pane/emulator-keyboard-paste.ts index ad6a371b334..dd641a83a52 100644 --- a/src/renderer/src/components/emulator-pane/emulator-keyboard-paste.ts +++ b/src/renderer/src/components/emulator-pane/emulator-keyboard-paste.ts @@ -57,10 +57,6 @@ export type PasteTextIntoEmulatorKeyboardOptions = { text: string } -export function getEmulatorKeyboardPasteByteLength(text: string): number { - return measureClipboardTextByteLength(text).byteLength -} - export function buildEmulatorKeyboardPastePlan( text: string, options: { diff --git a/src/renderer/src/components/emulator-pane/emulator-pane-types.ts b/src/renderer/src/components/emulator-pane/emulator-pane-types.ts index bd2b4589f48..80da92fc471 100644 --- a/src/renderer/src/components/emulator-pane/emulator-pane-types.ts +++ b/src/renderer/src/components/emulator-pane/emulator-pane-types.ts @@ -70,5 +70,3 @@ export function pickDefaultDevice(devices: SimulatorDeviceRow[]): SimulatorDevic null ) } - -export const isMacOs = typeof navigator !== 'undefined' && navigator.userAgent.includes('Mac') diff --git a/src/renderer/src/components/github-project/column-widths.ts b/src/renderer/src/components/github-project/column-widths.ts index 678de5de084..68106fb451b 100644 --- a/src/renderer/src/components/github-project/column-widths.ts +++ b/src/renderer/src/components/github-project/column-widths.ts @@ -70,17 +70,3 @@ export function resolveWidth( } return defaultWidthFor(field) } - -export function buildGridTemplate( - fields: GitHubProjectField[], - widths: Readonly> -): string { - // Why: emit `minmax(px, fr)` so the grid honors the - // user's relative weights AND fits within the container — pixel - // widths would let a wide TITLE blow past the parent and push the - // surrounding chrome out. The minmax floor is what stops a column - // from collapsing to zero when its weight is small. - const cols = fields.map((f) => `minmax(${MIN_COLUMN_WIDTH}px, ${resolveWidth(f, widths)}fr)`) - cols.push(`${ACTION_COLUMN_WIDTH}px`) - return cols.join(' ') -} diff --git a/src/renderer/src/components/github/github-issue-comment-helpers.ts b/src/renderer/src/components/github/github-issue-comment-helpers.ts index ab6c3ad4bfc..01f40093db5 100644 --- a/src/renderer/src/components/github/github-issue-comment-helpers.ts +++ b/src/renderer/src/components/github/github-issue-comment-helpers.ts @@ -1,80 +1,3 @@ -import { useAppStore } from '@/store' -import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client' -import type { GitHubIssueCloseReason, GlobalSettings } from '../../../../shared/types' -import type { TaskSourceContext } from '../../../../shared/task-source-context' - -export type GitHubIssueCommentProjectOrigin = { - owner: string - repo: string - cacheKey: string - projectItemId: string -} - -export async function runIssueStateUpdate(args: { - repoPath: string - repoId?: string | null - sourceContext?: TaskSourceContext | null - sourceSettings?: Pick | null | undefined - projectOrigin: GitHubIssueCommentProjectOrigin | undefined - number: number - updates: { - state: 'open' | 'closed' - stateReason?: GitHubIssueCloseReason - duplicateOf?: number - } -}): Promise { - if (args.projectOrigin) { - const target = getActiveRuntimeTarget(args.sourceSettings ?? useAppStore.getState().settings) - const updateArgs = { - owner: args.projectOrigin.owner, - repo: args.projectOrigin.repo, - number: args.number, - updates: args.updates - } - const res = - target.kind === 'environment' - ? await callRuntimeRpc>>( - target, - 'github.project.updateIssueBySlug', - updateArgs, - { timeoutMs: 30_000 } - ) - : await window.api.gh.updateIssueBySlug(updateArgs) - if (!res.ok) { - throw new Error(res.error.message) - } - return - } - const res = await window.api.gh.updateIssue({ - repoPath: args.repoPath, - repoId: args.repoId ?? undefined, - sourceContext: args.sourceContext, - number: args.number, - updates: args.updates - }) - if (!res.ok) { - throw new Error(res.error) - } -} - -export async function addIssueCommentForRepo(args: { - repoId?: string - repoPath: string - sourceContext?: TaskSourceContext | null - number: number - body: string - type?: 'issue' | 'pr' -}): Promise>> { - return window.api.gh.addIssueComment({ - repoPath: args.repoPath, - repoId: args.repoId, - sourceContext: args.sourceContext, - number: args.number, - body: args.body, - type: args.type - }) -} - export function githubAvatarUrl(login: string): string { return `https://github.com/${encodeURIComponent(login)}.png?size=64` } diff --git a/src/renderer/src/components/linear-issue-workspace-text.ts b/src/renderer/src/components/linear-issue-workspace-text.ts index b9a9a518e79..b4e88121bb8 100644 --- a/src/renderer/src/components/linear-issue-workspace-text.ts +++ b/src/renderer/src/components/linear-issue-workspace-text.ts @@ -25,21 +25,3 @@ export function formatLinearIssueRelativeTime(input: string): string { export function buildLinearIssueBranchName(issue: LinearIssue): string { return getUsableLinearBranchName(issue.branchName) ?? getLinearIssueWorkspaceName(issue) } - -export function buildLinearIssuePrompt(issue: LinearIssue): string { - const lines = [ - `Linear issue: ${issue.identifier} ${issue.title}`, - `URL: ${issue.url}`, - `State: ${issue.state.name}`, - `Assignee: ${issue.assignee?.displayName ?? 'Unassigned'}`, - `Team: ${issue.team.name}` - ] - if (issue.workspaceName) { - lines.push(`Workspace: ${issue.workspaceName}`) - } - const description = issue.description?.trim() - if (description) { - lines.push('', 'Description:', description) - } - return lines.join('\n') -} diff --git a/src/renderer/src/components/native-chat/native-chat-composer-state.ts b/src/renderer/src/components/native-chat/native-chat-composer-state.ts index c7c320af52e..a14bed089f6 100644 --- a/src/renderer/src/components/native-chat/native-chat-composer-state.ts +++ b/src/renderer/src/components/native-chat/native-chat-composer-state.ts @@ -24,8 +24,6 @@ export { type NativeChatSkillDiscoverySnapshot } from './native-chat-picker-items' -export type ComposerAutocompleteMode = 'none' | 'slash' | 'mention' | 'skill' - type PickerAutocomplete = { query: string items: NativeChatPickerItem[] diff --git a/src/renderer/src/components/onboarding/use-onboarding-flow.ts b/src/renderer/src/components/onboarding/use-onboarding-flow.ts index e02cb931855..b21b4c742e8 100644 --- a/src/renderer/src/components/onboarding/use-onboarding-flow.ts +++ b/src/renderer/src/components/onboarding/use-onboarding-flow.ts @@ -40,8 +40,6 @@ import { buildWindowsTerminalSnapshotPayload } from './windows-terminal-onboardi export { STEPS } from './use-onboarding-flow-types' export type { StepId, StepNumber } from './use-onboarding-flow-types' -export type OnboardingFlowController = ReturnType - type TaskSourcesSnapshotProps = EventProps<'onboarding_task_sources_snapshot'> type TaskSourcesGithubStatus = TaskSourcesSnapshotProps['github_status'] type TaskSourcesLinearStatus = TaskSourcesSnapshotProps['linear_status'] diff --git a/src/renderer/src/components/repo/repo-icon.tsx b/src/renderer/src/components/repo/repo-icon.tsx index 00d897383df..b82de9e1173 100644 --- a/src/renderer/src/components/repo/repo-icon.tsx +++ b/src/renderer/src/components/repo/repo-icon.tsx @@ -27,12 +27,6 @@ import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' import { createLocalizedCatalog } from '@/i18n/localized-catalog' -export type RepoLucideIconOption = { - name: string - label: string - icon: LucideIcon -} - export const getRepoLucideIconOptions = createLocalizedCatalog(() => [ { name: 'Folder', diff --git a/src/renderer/src/components/right-sidebar/CreateHostedReviewComposer.tsx b/src/renderer/src/components/right-sidebar/CreateHostedReviewComposer.tsx index 9656487a72d..7caf09dd128 100644 --- a/src/renderer/src/components/right-sidebar/CreateHostedReviewComposer.tsx +++ b/src/renderer/src/components/right-sidebar/CreateHostedReviewComposer.tsx @@ -33,13 +33,6 @@ import { const EMPTY_DROPDOWN_ITEMS: DropdownEntry[] = [] -export type CreateHostedReviewFields = { - base: string - title: string - body: string - draft: boolean -} - export type CreateHostedReviewComposerPrimaryAction = { disabled: boolean title: string diff --git a/src/renderer/src/components/right-sidebar/commit-failure-dialog-state.ts b/src/renderer/src/components/right-sidebar/commit-failure-dialog-state.ts index e3ed0bd27ea..cf53da6faae 100644 --- a/src/renderer/src/components/right-sidebar/commit-failure-dialog-state.ts +++ b/src/renderer/src/components/right-sidebar/commit-failure-dialog-state.ts @@ -3,10 +3,6 @@ export type CommitFailureDialogState = { open: boolean } -export function getCommitFailureDialogWorktreeKey(worktreeId: string | null | undefined): string { - return worktreeId ?? 'no-worktree' -} - export function shouldShowCommitFailureDialog( state: CommitFailureDialogState, worktreeKey: string, diff --git a/src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts b/src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts index cb94f03c978..5ef3c247a4d 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts @@ -128,16 +128,6 @@ export function getFileExplorerOperationRoute( } } -export function requireFileExplorerOperationRoute( - worktreeId: string | null | undefined -): FileExplorerOperationRoute { - const route = getFileExplorerOperationRoute(getFileExplorerOperationOwner(worktreeId)) - if (!route) { - throw new Error(getFileExplorerOwnerUnresolvedMessage()) - } - return route -} - export function requireMatchingFileExplorerOperationRoute( worktreeId: string | null | undefined, expectedOwner: FileExplorerOperationOwner | undefined diff --git a/src/renderer/src/components/right-sidebar/file-explorer-runtime-owner-boundary.test.ts b/src/renderer/src/components/right-sidebar/file-explorer-runtime-owner-boundary.test.ts index 8814019ab7a..766e0f11ade 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer-runtime-owner-boundary.test.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer-runtime-owner-boundary.test.ts @@ -24,7 +24,7 @@ describe('right sidebar file/git runtime ownership boundaries', () => { const text = source(path) expect(text).toMatch( - /getRightSidebarWorktreeRuntimeSettings|getSettingsForWorktreeRuntimeOwner|getFileExplorerOperationOwner|getFileExplorerOperationRoute|captureFileExplorerOperationGuard|requireFileExplorerOperationRoute|requireMatchingFileExplorerOperationRoute/ + /getRightSidebarWorktreeRuntimeSettings|getSettingsForWorktreeRuntimeOwner|getFileExplorerOperationOwner|getFileExplorerOperationRoute|captureFileExplorerOperationGuard|requireMatchingFileExplorerOperationRoute/ ) expect(text).not.toContain('settings: useAppStore.getState().settings') expect(text).not.toContain('const settings = useAppStore.getState().settings') diff --git a/src/renderer/src/components/right-sidebar/push-failure-summary.ts b/src/renderer/src/components/right-sidebar/push-failure-summary.ts deleted file mode 100644 index 4f20d751e13..00000000000 --- a/src/renderer/src/components/right-sidebar/push-failure-summary.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - PUSH_FAILURE_SUMMARY_SCAN_CODE_UNITS, - hasExpandedPushFailureDetails, - isPushHookFailure, - summarizePushFailure -} from '../../../../shared/source-control-push-failure' diff --git a/src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.ts b/src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.ts index 4c28a1062bb..42f14951f44 100644 --- a/src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.ts +++ b/src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.ts @@ -4,10 +4,6 @@ const EMPTY_OPEN_ROW_KEYS: ReadonlySet = new Set() const SIGNATURE_SEPARATOR = '::' -// Why: a plain edit tab carries no diff source, so it is matched against -// visible pending rows by path instead of a specific diff side. -export type ActiveOpenFileDiffSource = DiffSource | 'edit' - /** * Build the stable string that identifies the active editor file for highlight * matching: `${diffSource}::${relativePath}`. Kept as a primitive so the zustand diff --git a/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.test.ts b/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.test.ts deleted file mode 100644 index 030ac680492..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as SourceControlLaunchAgentSelectionModule from '@/lib/source-control-launch-agent-selection' - -const mocks = vi.hoisted(() => ({ - ensureDetectedAgents: vi.fn(), - ensureRemoteDetectedAgents: vi.fn(), - focusTerminalTabSurface: vi.fn(), - getConnectionId: vi.fn(), - launchAgentInNewTab: vi.fn(), - pickSourceControlLaunchAgent: vi.fn(), - toastError: vi.fn(), - toastSuccess: vi.fn() -})) - -vi.mock('sonner', () => ({ - toast: { - error: mocks.toastError, - success: mocks.toastSuccess - } -})) - -vi.mock('@/lib/connection-context', () => ({ - getConnectionId: mocks.getConnectionId -})) - -vi.mock('@/lib/focus-terminal-tab-surface', () => ({ - focusTerminalTabSurface: mocks.focusTerminalTabSurface -})) - -vi.mock('@/lib/launch-agent-in-new-tab', () => ({ - launchAgentInNewTab: mocks.launchAgentInNewTab -})) - -vi.mock('@/lib/source-control-launch-agent-selection', async () => { - const actual = await vi.importActual( - '@/lib/source-control-launch-agent-selection' - ) - return { - ...actual, - pickSourceControlLaunchAgent: mocks.pickSourceControlLaunchAgent - } -}) - -import { launchSourceControlRecoveryAgentWithDefault } from './source-control-ai-recovery-launch' - -const copy = { - promptUnavailable: 'Could not build the agent prompt.', - emptyPrompt: 'Recovery prompt is empty.', - savedAgentUnavailable: 'Saved AI agent is unavailable.', - noEnabledAgent: 'No enabled AI agents.', - launchCommandUnavailable: 'Could not build the agent launch command.', - connectionUnavailable: 'Unable to resolve the workspace connection.', - success: 'Started an AI agent for the recovery.' -} - -function launchRecovery( - overrides: Partial[0]> = {} -): Promise { - return launchSourceControlRecoveryAgentWithDefault({ - activeWorktreeId: 'wt-1', - activeGroupId: 'group-1', - activeSourceControlLaunchPlatform: 'darwin', - actionId: 'fixCommitFailure', - basePrompt: 'Fix this commit failure.', - getLaunchActionRecipe: () => ({ - commandInputTemplate: '{basePrompt}' - }), - getStoreState: () => ({ - settings: { defaultTuiAgent: 'codex', disabledTuiAgents: [] } as never, - ensureDetectedAgents: mocks.ensureDetectedAgents, - ensureRemoteDetectedAgents: mocks.ensureRemoteDetectedAgents - }), - copy, - ...overrides - }) -} - -describe('launchSourceControlRecoveryAgentWithDefault', () => { - beforeEach(() => { - vi.clearAllMocks() - mocks.getConnectionId.mockReturnValue(null) - mocks.ensureDetectedAgents.mockResolvedValue(['codex']) - mocks.ensureRemoteDetectedAgents.mockResolvedValue(['codex']) - mocks.pickSourceControlLaunchAgent.mockReturnValue('codex') - mocks.launchAgentInNewTab.mockReturnValue({ tabId: 'tab-1' }) - }) - - it('rejects invalid saved CLI arguments before detecting agents', async () => { - await expect( - launchRecovery({ - getLaunchActionRecipe: () => ({ - commandInputTemplate: '{basePrompt}', - agentArgs: '--model "unterminated' - }) - }) - ).resolves.toBe(false) - - expect(mocks.ensureDetectedAgents).not.toHaveBeenCalled() - expect(mocks.ensureRemoteDetectedAgents).not.toHaveBeenCalled() - expect(mocks.launchAgentInNewTab).not.toHaveBeenCalled() - expect(mocks.toastError).toHaveBeenCalledWith( - 'CLI arguments are invalid: Unclosed quote in command template.' - ) - }) - - it('treats a null worktree connection as proven local and does not fall back to SSH', async () => { - mocks.getConnectionId.mockReturnValue(null) - - await expect(launchRecovery({ sourceRepoConnectionId: 'ssh-1' })).resolves.toBe(true) - - expect(mocks.ensureDetectedAgents).toHaveBeenCalledTimes(1) - expect(mocks.ensureRemoteDetectedAgents).not.toHaveBeenCalled() - expect(mocks.launchAgentInNewTab).toHaveBeenCalledWith( - expect.objectContaining({ prompt: 'Fix this commit failure.' }) - ) - expect(mocks.focusTerminalTabSurface).toHaveBeenCalledWith('tab-1') - expect(mocks.toastSuccess).toHaveBeenCalledWith('Started an AI agent for the recovery.') - }) - - it('uses the owning SSH connection when the worktree has one', async () => { - mocks.getConnectionId.mockReturnValue('ssh-worktree') - - await expect(launchRecovery()).resolves.toBe(true) - - expect(mocks.ensureRemoteDetectedAgents).toHaveBeenCalledWith('ssh-worktree') - expect(mocks.ensureDetectedAgents).not.toHaveBeenCalled() - }) - - it('falls back to the repo connection only when the worktree connection is unresolved', async () => { - mocks.getConnectionId.mockReturnValue(undefined) - - await expect(launchRecovery({ sourceRepoConnectionId: 'ssh-repo' })).resolves.toBe(true) - - expect(mocks.ensureRemoteDetectedAgents).toHaveBeenCalledWith('ssh-repo') - }) - - it('errors when both worktree and repo connections are unresolved', async () => { - mocks.getConnectionId.mockReturnValue(undefined) - - await expect(launchRecovery({ sourceRepoConnectionId: undefined })).resolves.toBe(false) - - expect(mocks.toastError).toHaveBeenCalledWith('Unable to resolve the workspace connection.') - expect(mocks.ensureDetectedAgents).not.toHaveBeenCalled() - expect(mocks.ensureRemoteDetectedAgents).not.toHaveBeenCalled() - }) - - it('rejects empty rendered prompts before detecting agents', async () => { - await expect( - launchRecovery({ - getLaunchActionRecipe: () => ({ - commandInputTemplate: ' ' - }) - }) - ).resolves.toBe(false) - - expect(mocks.toastError).toHaveBeenCalledWith('Recovery prompt is empty.') - expect(mocks.ensureDetectedAgents).not.toHaveBeenCalled() - }) - - it('rejects unavailable saved agents before terminal launch', async () => { - mocks.ensureDetectedAgents.mockResolvedValue(['claude']) - - await expect( - launchRecovery({ - getLaunchActionRecipe: () => ({ - agentId: 'codex', - commandInputTemplate: '{basePrompt}' - }) - }) - ).resolves.toBe(false) - - expect(mocks.toastError).toHaveBeenCalledWith('Saved AI agent is unavailable.') - expect(mocks.launchAgentInNewTab).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.ts b/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.ts deleted file mode 100644 index a080f587db5..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control-ai-commit-failure-launch.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - SourceControlActionRecipe, - SourceControlLaunchActionId -} from '../../../../shared/source-control-ai-actions' -import { - getDefaultSourceControlRecoveryLaunchCopy, - launchSourceControlRecoveryAgentWithDefault -} from './source-control-ai-recovery-launch' -import type { AppState } from '@/store' - -type SourceControlAiLaunchStoreSnapshot = Pick< - AppState, - 'settings' | 'ensureDetectedAgents' | 'ensureRemoteDetectedAgents' -> - -export async function launchCommitFailureAgentWithDefault({ - activeWorktreeId, - activeGroupId, - activeSourceControlLaunchPlatform, - sourceRepoConnectionId, - commitFailureRecoveryPrompt, - promptOverride, - getLaunchActionRecipe, - getStoreState -}: { - activeWorktreeId: string - activeGroupId: string | null | undefined - activeSourceControlLaunchPlatform: NodeJS.Platform - sourceRepoConnectionId?: string | null - commitFailureRecoveryPrompt: string | null - promptOverride?: string - getLaunchActionRecipe: (actionId: SourceControlLaunchActionId) => SourceControlActionRecipe - getStoreState: () => SourceControlAiLaunchStoreSnapshot -}): Promise { - return launchSourceControlRecoveryAgentWithDefault({ - activeWorktreeId, - activeGroupId, - activeSourceControlLaunchPlatform, - sourceRepoConnectionId, - actionId: 'fixCommitFailure', - basePrompt: commitFailureRecoveryPrompt, - promptOverride, - getLaunchActionRecipe, - getStoreState, - copy: getDefaultSourceControlRecoveryLaunchCopy('commit') - }) -} diff --git a/src/renderer/src/components/right-sidebar/source-control-ai-push-failure-launch.ts b/src/renderer/src/components/right-sidebar/source-control-ai-push-failure-launch.ts deleted file mode 100644 index 33e8ab01580..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control-ai-push-failure-launch.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - SourceControlActionRecipe, - SourceControlLaunchActionId -} from '../../../../shared/source-control-ai-actions' -import { - getDefaultSourceControlRecoveryLaunchCopy, - launchSourceControlRecoveryAgentWithDefault -} from './source-control-ai-recovery-launch' -import type { AppState } from '@/store' - -type SourceControlAiLaunchStoreSnapshot = Pick< - AppState, - 'settings' | 'ensureDetectedAgents' | 'ensureRemoteDetectedAgents' -> - -export async function launchPushFailureAgentWithDefault({ - activeWorktreeId, - activeGroupId, - activeSourceControlLaunchPlatform, - sourceRepoConnectionId, - pushFailureRecoveryPrompt, - promptOverride, - getLaunchActionRecipe, - getStoreState -}: { - activeWorktreeId: string - activeGroupId: string | null | undefined - activeSourceControlLaunchPlatform: NodeJS.Platform - sourceRepoConnectionId?: string | null - pushFailureRecoveryPrompt: string | null - promptOverride?: string - getLaunchActionRecipe: (actionId: SourceControlLaunchActionId) => SourceControlActionRecipe - getStoreState: () => SourceControlAiLaunchStoreSnapshot -}): Promise { - return launchSourceControlRecoveryAgentWithDefault({ - activeWorktreeId, - activeGroupId, - activeSourceControlLaunchPlatform, - sourceRepoConnectionId, - actionId: 'fixPushFailure', - basePrompt: pushFailureRecoveryPrompt, - promptOverride, - getLaunchActionRecipe, - getStoreState, - copy: getDefaultSourceControlRecoveryLaunchCopy('push') - }) -} diff --git a/src/renderer/src/components/right-sidebar/source-control-create-pr-intent-state.ts b/src/renderer/src/components/right-sidebar/source-control-create-pr-intent-state.ts index b40976642e1..75c4e04a34e 100644 --- a/src/renderer/src/components/right-sidebar/source-control-create-pr-intent-state.ts +++ b/src/renderer/src/components/right-sidebar/source-control-create-pr-intent-state.ts @@ -1,14 +1,5 @@ import type { PrimaryAction } from './source-control-primary-action-types' -import { - resolveCreateReviewIntentEligibility, - type CreateReviewIntentEligibility, - type CreateReviewIntentKind -} from '../../../../shared/source-control-create-review-intent' - -// Why: renderer APIs keep PR terminology for compatibility, while shared logic -// uses provider-neutral review terminology for PR/MR hosts. -export type CreatePrIntentKind = CreateReviewIntentKind -export type CreatePrIntentEligibility = CreateReviewIntentEligibility +import { resolveCreateReviewIntentEligibility } from '../../../../shared/source-control-create-review-intent' export const resolveCreatePrIntentEligibility = resolveCreateReviewIntentEligibility diff --git a/src/renderer/src/components/right-sidebar/source-control-primary-action-types.ts b/src/renderer/src/components/right-sidebar/source-control-primary-action-types.ts index b3f79e8a69e..da34c8a253d 100644 --- a/src/renderer/src/components/right-sidebar/source-control-primary-action-types.ts +++ b/src/renderer/src/components/right-sidebar/source-control-primary-action-types.ts @@ -62,13 +62,3 @@ export type PrimaryActionInputs = { // while the request is in flight instead of flashing it in after ~1s. isHostedReviewCreationLoading?: boolean } - -export const PRIMARY_LABEL_BY_KIND: Record, string> = { - stage: 'Stage All', - push: 'Push', - pull: 'Pull', - sync: 'Sync', - publish: 'Publish Branch', - create_pr_intent: 'Create PR', - create_pr: 'Create PR' -} diff --git a/src/renderer/src/components/settings/SettingsConstants.ts b/src/renderer/src/components/settings/SettingsConstants.ts index 0c7706be3bc..260d8b48971 100644 --- a/src/renderer/src/components/settings/SettingsConstants.ts +++ b/src/renderer/src/components/settings/SettingsConstants.ts @@ -1,8 +1,6 @@ -import type { OrcaHooks } from '../../../../shared/types' import { DEFAULT_APP_FONT_FAMILY, getDefaultRepoHookSettings } from '../../../../shared/constants' import { DESKTOP_TERMINAL_SCROLLBACK_ROW_PRESETS } from '../../../../shared/terminal-scrollback-policy' -export type HookName = keyof OrcaHooks['scripts'] export const DEFAULT_REPO_HOOK_SETTINGS = getDefaultRepoHookSettings() export const MAX_THEME_RESULTS = 80 export const SCROLLBACK_PRESETS_ROWS = DESKTOP_TERMINAL_SCROLLBACK_ROW_PRESETS diff --git a/src/renderer/src/components/settings/general-search.ts b/src/renderer/src/components/settings/general-search.ts index 9cff3c527d9..a79f994d5fa 100644 --- a/src/renderer/src/components/settings/general-search.ts +++ b/src/renderer/src/components/settings/general-search.ts @@ -193,35 +193,6 @@ export const getGeneralUpdateSearchEntries = createLocalizedCatalog(() => [ } ]) -export const getGeneralAgentSearchEntries = createLocalizedCatalog(() => [ - { - title: translate('auto.components.settings.general.search.db11502270', 'Default Agent'), - description: translate( - 'auto.components.settings.general.search.e2da948f59', - 'Pre-select an AI coding agent in the new-workspace composer.' - ), - keywords: [ - ...translateSearchKeyword('auto.components.settings.general.search.8ea37a05bc', 'agent'), - ...translateSearchKeyword('auto.components.settings.general.search.41c2f9a025', 'default'), - ...translateSearchKeyword('auto.components.settings.general.search.95b63edde7', 'claude'), - ...translateSearchKeyword('auto.components.settings.general.search.aea7d2cccb', 'openclaude'), - ...translateSearchKeyword( - 'auto.components.settings.general.search.5baf51c4d9', - 'open claude' - ), - ...translateSearchKeyword('auto.components.settings.general.search.27d9b996ba', 'codex'), - ...translateSearchKeyword('auto.components.settings.general.search.882c4896fd', 'opencode'), - ...translateSearchKeyword('auto.components.settings.general.search.9b0bc30160', 'pi'), - ...translateSearchKeyword('auto.components.settings.general.search.5fdf1dc2d1', 'omp'), - ...translateSearchKeyword('auto.components.settings.general.search.3c30fe2d51', 'gemini'), - ...translateSearchKeyword('auto.components.settings.general.search.f472e97440', 'aider'), - ...translateSearchKeyword('auto.components.settings.general.search.5d9ba08673', 'copilot'), - ...translateSearchKeyword('auto.components.settings.general.search.c61b14be7c', 'grok'), - ...translateSearchKeyword('auto.lib.agent.catalog.fc80296033', 'devin') - ] - } -]) - type GeneralPaneSearchOptions = { includeProjectRuntime?: boolean } diff --git a/src/renderer/src/components/settings/terminal-windows-search.ts b/src/renderer/src/components/settings/terminal-windows-search.ts index 9aa765177b9..85bb8ed4d76 100644 --- a/src/renderer/src/components/settings/terminal-windows-search.ts +++ b/src/renderer/src/components/settings/terminal-windows-search.ts @@ -1,4 +1,3 @@ -import type { SettingsSearchEntry } from './settings-search' import { translate } from '@/i18n/i18n' import { translateSearchKeyword } from './settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' @@ -135,9 +134,3 @@ export const getTerminalRightClickToPasteSearchEntry = createLocalizedCatalog(() ] } ]) - -export const getTerminalWindowsSearchEntries = createLocalizedCatalog((): SettingsSearchEntry[] => [ - ...getTerminalWindowsShellSearchEntry(), - ...getTerminalWindowsPowershellImplementationSearchEntry(), - ...getTerminalRightClickToPasteSearchEntry() -]) diff --git a/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx b/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx index 43f53b76ede..2d2d081b067 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx @@ -1,11 +1,5 @@ import React from 'react' -import type { - PRState, - CheckStatus, - GitConflictOperation, - TerminalTab -} from '../../../../shared/types' -import type { AgentStatusEntry } from '../../../../shared/agent-status-types' +import type { CheckStatus, GitConflictOperation, TerminalTab } from '../../../../shared/types' // ── Pure helper functions ──────────────────────────────────────────── @@ -13,10 +7,6 @@ export function branchDisplayName(branch: string): string { return branch.replace(/^refs\/heads\//, '') } -export function prStateLabel(state: PRState): string { - return state.charAt(0).toUpperCase() + state.slice(1) -} - export function checksLabel(status: CheckStatus): string { switch (status) { case 'success': @@ -40,7 +30,6 @@ export const CONFLICT_OPERATION_LABELS: Record ) } - -export function WorktreeCardPorts({ ports }: WorktreeCardPortsProps): React.JSX.Element | null { - if (ports.length === 0) { - return null - } - - return ( - - - - - event.stopPropagation()} - onDoubleClick={(event) => event.stopPropagation()} - > - - - - - - ) -} diff --git a/src/renderer/src/components/sidebar/create-project-defaults.ts b/src/renderer/src/components/sidebar/create-project-defaults.ts index fd5175c8c22..9197a8a7d91 100644 --- a/src/renderer/src/components/sidebar/create-project-defaults.ts +++ b/src/renderer/src/components/sidebar/create-project-defaults.ts @@ -1,5 +1,3 @@ -export type RepoKind = 'git' | 'folder' - export type GitAvailability = 'checking' | 'available' | 'unavailable' | 'unknown' function pathSeparatorFor(pathValue: string): '/' | '\\' { diff --git a/src/renderer/src/components/sidebar/project-header-drop.ts b/src/renderer/src/components/sidebar/project-header-drop.ts index be012eb72ce..1f7418f805c 100644 --- a/src/renderer/src/components/sidebar/project-header-drop.ts +++ b/src/renderer/src/components/sidebar/project-header-drop.ts @@ -28,16 +28,6 @@ export function getProjectHeaderDragBucketKey( return repo.projectGroupId ? `group:${repo.projectGroupId}` : 'ungrouped' } -export function getSidebarOrderedRepoHeaderIds(rows: readonly Row[]): string[] { - const ids: string[] = [] - for (const row of rows) { - if (row.type === 'header' && row.repo) { - ids.push(row.repo.id) - } - } - return ids -} - export function getSidebarOrderedRepoHeaderIdsByBucket( rows: readonly Row[] ): Map { diff --git a/src/renderer/src/components/sidebar/sidebar-host-options.ts b/src/renderer/src/components/sidebar/sidebar-host-options.ts index 248bd98000d..dc764112bcc 100644 --- a/src/renderer/src/components/sidebar/sidebar-host-options.ts +++ b/src/renderer/src/components/sidebar/sidebar-host-options.ts @@ -118,13 +118,6 @@ export function buildSidebarHostScopeOptions( ] } -export function getSidebarHostScopeLabel( - scope: WorkspaceHostScope, - options: readonly SidebarHostScopeOption[] -): string { - return options.find((option) => option.id === scope)?.label ?? 'All hosts' -} - export function getSidebarHostVisibilityLabel( visibleHostIds: readonly ExecutionHostId[] | null | undefined, hosts: readonly SidebarHostOption[] diff --git a/src/renderer/src/components/sidebar/sidebar-nav-controls.tsx b/src/renderer/src/components/sidebar/sidebar-nav-controls.tsx index eb98669a39d..466355be56b 100644 --- a/src/renderer/src/components/sidebar/sidebar-nav-controls.tsx +++ b/src/renderer/src/components/sidebar/sidebar-nav-controls.tsx @@ -1,6 +1,5 @@ import React from 'react' import { EyeOff } from 'lucide-react' -import { cn } from '@/lib/utils' import { ContextMenuContent, ContextMenuItem } from '@/components/ui/context-menu' import { translate } from '@/i18n/i18n' @@ -14,37 +13,3 @@ export function HideSidebarMenu({ onHide }: { onHide: () => void }): React.JSX.E ) } - -export function TaskProviderShortcut({ - canBrowseTasks, - label, - onOpen, - children -}: { - canBrowseTasks: boolean - label: string - onOpen: () => void - children: React.ReactNode -}): React.JSX.Element { - return ( - { - e.stopPropagation() - if (!canBrowseTasks) { - return - } - onOpen() - }} - className={cn( - 'rounded p-0.5 text-muted-foreground/70', - canBrowseTasks ? 'transition-colors hover:text-foreground' : 'cursor-default' - )} - aria-label={canBrowseTasks ? label : undefined} - aria-hidden={canBrowseTasks ? undefined : true} - > - {children} - - ) -} diff --git a/src/renderer/src/components/sidebar/workspace-kanban-card-drag-preview-dom.ts b/src/renderer/src/components/sidebar/workspace-kanban-card-drag-preview-dom.ts index 58dafc5db3f..a78fa1230ad 100644 --- a/src/renderer/src/components/sidebar/workspace-kanban-card-drag-preview-dom.ts +++ b/src/renderer/src/components/sidebar/workspace-kanban-card-drag-preview-dom.ts @@ -25,18 +25,6 @@ export function setDragDocumentStyles(enabled: boolean): void { document.documentElement.toggleAttribute(POINTER_DRAGGING_ATTR, enabled) } -export function getDraggedCards( - board: HTMLElement, - worktreeIds: readonly string[], - fallbackCard: HTMLElement -): HTMLElement[] { - const ids = new Set(worktreeIds) - const cards = Array.from(board.querySelectorAll(CARD_SELECTOR)).filter((card) => - ids.has(card.dataset.workspaceBoardCardId ?? '') - ) - return cards.length > 0 ? cards : [fallbackCard] -} - export function setDraggedCardsDragging(args: { board: HTMLElement | null worktreeIds: readonly string[] diff --git a/src/renderer/src/components/tab-bar/shell-icons.tsx b/src/renderer/src/components/tab-bar/shell-icons.tsx index 8ea837e360f..a5a896154a9 100644 --- a/src/renderer/src/components/tab-bar/shell-icons.tsx +++ b/src/renderer/src/components/tab-bar/shell-icons.tsx @@ -3,8 +3,6 @@ import { WINDOWS_GIT_BASH_SHELL } from '../../../../shared/windows-terminal-shel import { translate } from '@/i18n/i18n' import gitForWindowsLogoUrl from '../../../../../resources/gwindows_logo.svg?url' -export type WindowsShell = 'powershell.exe' | 'cmd.exe' | 'wsl.exe' | typeof WINDOWS_GIT_BASH_SHELL - // Why: the "+" dropdown and per-tab tab strip both need a visual distinction // between PowerShell, CMD, Git Bash, and WSL sessions. Stock lucide glyphs don't // differentiate — every session rendered as the same generic chevron. These diff --git a/src/renderer/src/components/tab-bar/tab-create-entry-path-validation.ts b/src/renderer/src/components/tab-bar/tab-create-entry-path-validation.ts index a73a66da2b5..91a6415bdd6 100644 --- a/src/renderer/src/components/tab-bar/tab-create-entry-path-validation.ts +++ b/src/renderer/src/components/tab-bar/tab-create-entry-path-validation.ts @@ -3,18 +3,6 @@ import { isAbsolutePathLike } from '../editor/editor-panel-file-mode' export type TabEntryLocalPlatform = 'posix' | 'windows' -function hasPathSeparator(query: string): boolean { - return /[\\/]/.test(query) -} - -function hasFilenameExtension(query: string): boolean { - return /(?:^|[\\/])[^\\/]+\.[^\\/]+$/.test(query.trim()) -} - -export function isLikelyNewFileIntent(query: string): boolean { - return hasPathSeparator(query) || hasFilenameExtension(query) -} - export function isTabEntryAbsolutePathLike(query: string): boolean { return isAbsolutePathLike(query.trim()) } diff --git a/src/renderer/src/components/tab-group/tab-drag-context.tsx b/src/renderer/src/components/tab-group/tab-drag-context.tsx index 3cf10ee7e1a..189f583b293 100644 --- a/src/renderer/src/components/tab-group/tab-drag-context.tsx +++ b/src/renderer/src/components/tab-group/tab-drag-context.tsx @@ -31,7 +31,3 @@ export function TabDragProvider({ export function useTabDragActive(): boolean { return useContext(TabDragContext).isTabDragActive } - -export function useTabDragActiveRef(): RefObject { - return useContext(TabDragContext).isTabDragActiveRef -} diff --git a/src/renderer/src/components/terminal-pane/pty-delivery-interest.ts b/src/renderer/src/components/terminal-pane/pty-delivery-interest.ts index 56519c09e90..bb8e7d65a38 100644 --- a/src/renderer/src/components/terminal-pane/pty-delivery-interest.ts +++ b/src/renderer/src/components/terminal-pane/pty-delivery-interest.ts @@ -35,8 +35,3 @@ export function acquirePtyDeliveryInterest(ptyId: string): () => void { } } } - -/** Test seam: drop ref counts between tests (no IPC is sent). */ -export function _resetPtyDeliveryInterestForTest(): void { - ptyDeliveryInterestRefCounts.clear() -} diff --git a/src/renderer/src/components/terminal-pane/pty-pre-handler-buffer.ts b/src/renderer/src/components/terminal-pane/pty-pre-handler-buffer.ts index 83b3a10f1e2..eca3b7644b9 100644 --- a/src/renderer/src/components/terminal-pane/pty-pre-handler-buffer.ts +++ b/src/renderer/src/components/terminal-pane/pty-pre-handler-buffer.ts @@ -176,11 +176,6 @@ export function drainPreHandlerPtyExit(ptyId: string, handler: (code: number) => } } -export function clearPreHandlerPtyData(ptyId: string): void { - preHandlerPtyData.delete(ptyId) - warnedLostHandlerPtyIds.delete(ptyId) -} - export function clearPreHandlerPtyState(ptyId: string): void { preHandlerPtyData.delete(ptyId) preHandlerPtyExit.delete(ptyId) diff --git a/src/renderer/src/components/terminal-pane/terminal-hidden-delivery-gate.ts b/src/renderer/src/components/terminal-pane/terminal-hidden-delivery-gate.ts index 8123b83a9e1..367c42afe78 100644 --- a/src/renderer/src/components/terminal-pane/terminal-hidden-delivery-gate.ts +++ b/src/renderer/src/components/terminal-pane/terminal-hidden-delivery-gate.ts @@ -38,8 +38,3 @@ export function isRendererHiddenPtyDeliveryGateEnabled( // side-effect-authority sync read). return readPersistedHiddenDeliveryGateFlagSync() !== false } - -/** Test seam: reset the persisted-flag cache between tests. */ -export function _resetHiddenPtyDeliveryGateFlagCacheForTest(): void { - persistedGateFlagCache = undefined -} diff --git a/src/renderer/src/components/terminal/terminal-tab-actions.ts b/src/renderer/src/components/terminal/terminal-tab-actions.ts index 70b408a4b39..3dd2762f7f3 100644 --- a/src/renderer/src/components/terminal/terminal-tab-actions.ts +++ b/src/renderer/src/components/terminal/terminal-tab-actions.ts @@ -1,7 +1,5 @@ import { useAppStore } from '@/store' -import { TOGGLE_TERMINAL_PANE_EXPAND_EVENT } from '@/constants/terminal' import { - activateWebRuntimeSessionTab, closeWebRuntimeSessionTab, isWebRuntimeSessionActive, toHostSessionTabId @@ -226,38 +224,3 @@ export function closeTerminalTab( }) options?.onClosed?.() } - -export function activateTerminalTab(tabId: string): void { - const s = useAppStore.getState() - const owningWorktreeId = - Object.entries(s.tabsByWorktree).find(([, worktreeTabs]) => - worktreeTabs.some((tab) => tab.id === tabId) - )?.[0] ?? null - const worktreeRoute = resolveTerminalWorktreeRoute(s, owningWorktreeId) - if (!worktreeRoute) { - return - } - const runtimeEnvironmentId = worktreeRoute.runtimeEnvironmentId - if (owningWorktreeId && isWebRuntimeSessionActive(runtimeEnvironmentId)) { - // Why: activation needs to update the host's active tab as well as the - // local optimistic state, otherwise the next host snapshot snaps back. - void activateWebRuntimeSessionTab({ - worktreeId: owningWorktreeId, - tabId, - environmentId: runtimeEnvironmentId - }) - } - s.setActiveTab(tabId) - s.setActiveTabType('terminal') -} - -export function toggleTerminalPaneExpand(tabId: string): void { - useAppStore.getState().setActiveTab(tabId) - requestAnimationFrame(() => { - window.dispatchEvent( - new CustomEvent(TOGGLE_TERMINAL_PANE_EXPAND_EVENT, { - detail: { tabId } - }) - ) - }) -} diff --git a/src/renderer/src/constants/terminal.ts b/src/renderer/src/constants/terminal.ts index a5af687c466..3aea3426622 100644 --- a/src/renderer/src/constants/terminal.ts +++ b/src/renderer/src/constants/terminal.ts @@ -26,10 +26,6 @@ export const WAKE_HIBERNATED_AGENTS_WORKTREE_EVENT = 'orca-wake-hibernated-agent // per-pane ResizeObserver path instead. export const SYNC_FIT_PANES_EVENT = 'orca-sync-fit-panes' -export type ToggleTerminalPaneExpandDetail = { - tabId: string -} - export type FocusTerminalPaneDetail = { tabId: string /** Stable terminal layout leaf UUID. Numeric PaneManager ids are renderer-local diff --git a/src/renderer/src/hooks/useAutomationDispatchEvents.test.ts b/src/renderer/src/hooks/useAutomationDispatchEvents.test.ts index ae8f70d05dd..160496c3be8 100644 --- a/src/renderer/src/hooks/useAutomationDispatchEvents.test.ts +++ b/src/renderer/src/hooks/useAutomationDispatchEvents.test.ts @@ -3,7 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' const mockLaunchAgentBackgroundSession = vi.fn() const mockLaunchWorktreeBackgroundTerminals = vi.fn() -const mockSubmitPromptToAgentTab = vi.fn() const mockFindReusableAutomationSession = vi.fn() const mockObserveExistingAutomationSession = vi.fn() const mockCreateWorktree = vi.fn() @@ -125,9 +124,7 @@ vi.mock('@/lib/launch-worktree-background-terminals', () => ({ launchWorktreeBackgroundTerminals: mockLaunchWorktreeBackgroundTerminals })) -vi.mock('@/lib/agent-paste-draft', () => ({ - submitPromptToAgentTab: mockSubmitPromptToAgentTab -})) +vi.mock('@/lib/agent-paste-draft', () => ({})) vi.mock('@/lib/automation-session-reuse', () => ({ findReusableAutomationSession: mockFindReusableAutomationSession diff --git a/src/renderer/src/hooks/useIssueMetadata.ts b/src/renderer/src/hooks/useIssueMetadata.ts index 2635825f830..b694c5491dc 100644 --- a/src/renderer/src/hooks/useIssueMetadata.ts +++ b/src/renderer/src/hooks/useIssueMetadata.ts @@ -217,11 +217,6 @@ export function clearLinearMetadataCache(): void { clearMetadataRequestStore(linearMemberStore) } -export function clearGitHubMetadataCache(): void { - clearMetadataRequestStore(ghLabelStore) - clearMetadataRequestStore(ghAssigneeStore) -} - export function useTeamStates( teamId: string | null, settings?: RuntimeLinearSettings, diff --git a/src/renderer/src/hooks/useShortcutLabel.ts b/src/renderer/src/hooks/useShortcutLabel.ts index 95dd7320fd3..1d96ad8745a 100644 --- a/src/renderer/src/hooks/useShortcutLabel.ts +++ b/src/renderer/src/hooks/useShortcutLabel.ts @@ -61,18 +61,6 @@ export function useOptionalShortcutLabel(actionId: KeybindingActionId): string | return formatOptionalShortcutLabel(actionId, keybindings) } -export function formatShortcutKeys( - actionId: KeybindingActionId, - overrides?: KeybindingOverrides -): string[] { - return formatShortcutKeyComboDetails(actionId, overrides)[0]?.keys ?? [] -} - -export function useShortcutKeys(actionId: KeybindingActionId): string[] { - const keybindings = useAppStore((state) => state.keybindings) - return formatShortcutKeys(actionId, keybindings) -} - export function formatShortcutKeyComboDetails( actionId: KeybindingActionId, overrides?: KeybindingOverrides @@ -94,14 +82,3 @@ export function useShortcutKeyComboDetails( export function useShortcutKeyDetails(actionId: KeybindingActionId): ShortcutKeyComboDetails { return useShortcutKeyComboDetails(actionId)[0] ?? { keys: [], doubleTap: false } } - -export function formatShortcutKeyCombos( - actionId: KeybindingActionId, - overrides?: KeybindingOverrides -): string[][] { - return formatShortcutKeyComboDetails(actionId, overrides).map((combo) => combo.keys) -} - -export function useShortcutKeyCombos(actionId: KeybindingActionId): string[][] { - return useShortcutKeyComboDetails(actionId).map((combo) => combo.keys) -} diff --git a/src/renderer/src/lib/active-agent-note-send.ts b/src/renderer/src/lib/active-agent-note-send.ts index a2eb9c429d5..0089773c7d3 100644 --- a/src/renderer/src/lib/active-agent-note-send.ts +++ b/src/renderer/src/lib/active-agent-note-send.ts @@ -27,7 +27,6 @@ export { getActiveAgentRuntimeProbeDescriptor, getActiveTerminalNoteTarget, probeActiveAgentNoteTarget, - useCanSendNotesToActiveTerminal, type ActiveTerminalNoteTarget } from './active-agent-note-target' export { diff --git a/src/renderer/src/lib/active-agent-note-target.ts b/src/renderer/src/lib/active-agent-note-target.ts index 9ee0dfc511d..c9e7a833bad 100644 --- a/src/renderer/src/lib/active-agent-note-target.ts +++ b/src/renderer/src/lib/active-agent-note-target.ts @@ -1,11 +1,9 @@ -import { useEffect, useState } from 'react' import type { RuntimeTerminalListResult } from '../../../shared/runtime-types' import { AGENT_STATUS_STALE_AFTER_MS, type AgentStatusEntry } from '../../../shared/agent-status-types' import type { AppState } from '@/store/types' -import { useAppStore } from '@/store' import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client' import { getSettingsForWorktreeRuntimeOwner, @@ -80,48 +78,6 @@ export function getActiveTerminalNoteTarget( return leafId ? { tabId, leafId } : null } -export function useCanSendNotesToActiveTerminal(worktreeId: string): boolean { - const canSendFromRendererState = useAppStore( - (state) => getActiveAgentNoteTarget(state, worktreeId) !== null - ) - const probeKey = useAppStore( - (state) => getActiveAgentRuntimeProbeDescriptor(state, worktreeId)?.key ?? null - ) - const [runtimeProbe, setRuntimeProbe] = useState<{ key: string; canSend: boolean } | null>(null) - - useEffect(() => { - if (canSendFromRendererState || !probeKey) { - return - } - const probeDescriptor = getActiveAgentRuntimeProbeDescriptor(useAppStore.getState(), worktreeId) - if (!probeDescriptor || probeDescriptor.key !== probeKey) { - return - } - - let cancelled = false - void probeActiveAgentNoteTarget(probeDescriptor) - .then((canSend) => { - if (!cancelled) { - setRuntimeProbe({ key: probeKey, canSend }) - } - }) - .catch(() => { - if (!cancelled) { - setRuntimeProbe({ key: probeKey, canSend: false }) - } - }) - - return () => { - cancelled = true - } - }, [canSendFromRendererState, probeKey, worktreeId]) - - return ( - canSendFromRendererState || - (runtimeProbe !== null && runtimeProbe.key === probeKey && runtimeProbe.canSend) - ) -} - export function getActiveAgentNoteTarget( state: ActiveTerminalNoteTargetState, worktreeId: string, diff --git a/src/renderer/src/lib/agent-hibernation-coordinator.ts b/src/renderer/src/lib/agent-hibernation-coordinator.ts index 18948d86e08..b8dd54337b6 100644 --- a/src/renderer/src/lib/agent-hibernation-coordinator.ts +++ b/src/renderer/src/lib/agent-hibernation-coordinator.ts @@ -236,10 +236,6 @@ export function stopAgentHibernationCoordinator(): void { coordinator.confirmationState = {} } -export function isAgentHibernationCoordinatorRunning(): boolean { - return coordinator.interval !== null -} - export function resetAgentHibernationCoordinatorForTests(): void { stopAgentHibernationCoordinator() coordinator.shuttingDownCandidateIds.clear() diff --git a/src/renderer/src/lib/agent-paste-draft.ts b/src/renderer/src/lib/agent-paste-draft.ts index 44495c82e29..4e53f4ac102 100644 --- a/src/renderer/src/lib/agent-paste-draft.ts +++ b/src/renderer/src/lib/agent-paste-draft.ts @@ -167,24 +167,6 @@ export async function pasteDraftToAgentPtyWhenReady(args: { }) } -export async function submitPromptToAgentTab(args: { - tabId: string - content: string - timeoutMs?: number -}): Promise { - const { tabId, content, timeoutMs } = args - const ptyId = await waitForPtyId(tabId, timeoutMs ?? READINESS_TIMEOUT_MS) - if (!ptyId) { - return false - } - return await sendBracketedPasteToAgent({ - settings: getSettingsForAgentTabRuntimeOwner(tabId), - ptyId, - content, - submit: true - }) -} - export async function submitPromptToAgentPty(args: { tabId: string ptyId: string diff --git a/src/renderer/src/lib/crash-diagnostics.test.ts b/src/renderer/src/lib/crash-diagnostics.test.ts index 0b93aa15511..6e96efecf0c 100644 --- a/src/renderer/src/lib/crash-diagnostics.test.ts +++ b/src/renderer/src/lib/crash-diagnostics.test.ts @@ -156,18 +156,6 @@ describe('renderer crash diagnostics', () => { }) }) - it('disposes global listeners and the memory interval', () => { - diagnostics.installRendererCrashDiagnostics() - - diagnostics._disposeRendererCrashDiagnosticsForTests() - - expect(removeEventListenerMock).toHaveBeenCalledWith('error', expect.any(Function)) - expect(removeEventListenerMock).toHaveBeenCalledWith('unhandledrejection', expect.any(Function)) - expect(clearIntervalMock).toHaveBeenCalledWith(1) - expect(listeners.get('error')).toHaveLength(0) - expect(listeners.get('unhandledrejection')).toHaveLength(0) - }) - it('does not throw when preload is unavailable', () => { vi.stubGlobal('window', {}) @@ -223,8 +211,9 @@ describe('renderer crash diagnostics', () => { }) // Why: a heap that jumps straight past 80% must emit both levels at once. - diagnostics._disposeRendererCrashDiagnosticsForTests() + vi.resetModules() recordBreadcrumbMock.mockClear() + diagnostics = (await import('./crash-diagnostics')) as DiagnosticsModule diagnostics.installRendererCrashDiagnostics() expect(highwaterCalls()).toHaveLength(2) expect(getElementsByTagName).toHaveBeenCalledTimes(3) diff --git a/src/renderer/src/lib/crash-diagnostics.ts b/src/renderer/src/lib/crash-diagnostics.ts index 90fa45ebba0..95c9bfe213c 100644 --- a/src/renderer/src/lib/crash-diagnostics.ts +++ b/src/renderer/src/lib/crash-diagnostics.ts @@ -59,10 +59,6 @@ export function installRendererCrashDiagnostics(surface: RendererSurface = 'main } } -export function _disposeRendererCrashDiagnosticsForTests(): void { - disposeRendererCrashDiagnostics() -} - function disposeRendererCrashDiagnostics(): void { if (!rendererCrashDiagnosticsInstalled || typeof window === 'undefined') { return diff --git a/src/renderer/src/lib/ensure-simulator-tab.ts b/src/renderer/src/lib/ensure-simulator-tab.ts index 3c363a5e4d9..af2a1c4bf8f 100644 --- a/src/renderer/src/lib/ensure-simulator-tab.ts +++ b/src/renderer/src/lib/ensure-simulator-tab.ts @@ -4,8 +4,6 @@ import { cancelPendingSimulatorPaneShutdown } from './simulator-pane-shutdown-sc import { shouldShutdownSimulatorForPaneUnmountFromTabs } from './simulator-tab-shutdown' import { translate } from '@/i18n/i18n' -export const isMacOsHost = typeof navigator !== 'undefined' && navigator.userAgent.includes('Mac') - type EnsureSimulatorTabOptions = { targetGroupId?: string placement?: 'activeGroup' | 'rightSplit' @@ -105,20 +103,4 @@ export function ensureSimulatorTab( return tab.id } -export function countSimulatorTabs( - worktreeId: string, - options: { excludingTabId?: string } = {} -): number { - return (useAppStore.getState().unifiedTabsByWorktree[worktreeId] ?? []).filter( - (tab) => tab.contentType === 'simulator' && tab.id !== options.excludingTabId - ).length -} - -export function shouldShutdownSimulatorForPaneUnmount(worktreeId: string, tabId?: string): boolean { - return shouldShutdownSimulatorForPaneUnmountFromTabs( - useAppStore.getState().unifiedTabsByWorktree[worktreeId] ?? [], - tabId - ) -} - export { shouldShutdownSimulatorForPaneUnmountFromTabs } diff --git a/src/renderer/src/lib/keyboard-layout/input-source-id.ts b/src/renderer/src/lib/keyboard-layout/input-source-id.ts index 51c1e821f7c..d9f873e76e5 100644 --- a/src/renderer/src/lib/keyboard-layout/input-source-id.ts +++ b/src/renderer/src/lib/keyboard-layout/input-source-id.ts @@ -74,7 +74,3 @@ export function classifyInputSourceId(id: string | null | undefined): InputSourc // bug from recurring for any future Apple-shipped layout. return 'compose' } - -/** Test-only: exported so tests can assert the allowlist without - * reimporting the private constant. */ -export const __metaInputSourceIdsForTests: readonly string[] = META_INPUT_SOURCE_IDS diff --git a/src/renderer/src/lib/keyboard-layout/option-as-alt-probe.ts b/src/renderer/src/lib/keyboard-layout/option-as-alt-probe.ts index cfad8c55407..c96730959db 100644 --- a/src/renderer/src/lib/keyboard-layout/option-as-alt-probe.ts +++ b/src/renderer/src/lib/keyboard-layout/option-as-alt-probe.ts @@ -204,9 +204,3 @@ export function getOptionAsAltProbe(): OptionAsAltProbe { } return _singleton } - -/** Test-only: reset the singleton. */ -export function _resetOptionAsAltProbeForTests(): void { - _singleton?.dispose() - _singleton = null -} diff --git a/src/renderer/src/lib/open-mobile-emulator-tab.test.ts b/src/renderer/src/lib/open-mobile-emulator-tab.test.ts index 77865652df5..a3d942785b1 100644 --- a/src/renderer/src/lib/open-mobile-emulator-tab.test.ts +++ b/src/renderer/src/lib/open-mobile-emulator-tab.test.ts @@ -35,8 +35,7 @@ vi.mock('sonner', () => ({ vi.mock('./ensure-simulator-tab', () => ({ ensureSimulatorTab: vi.fn(), - getSimulatorTabForWorktree: vi.fn(), - isMacOsHost: true + getSimulatorTabForWorktree: vi.fn() })) const mockAttachResult = { diff --git a/src/renderer/src/lib/repo-slug-index.ts b/src/renderer/src/lib/repo-slug-index.ts index ed1dbe5f5e1..1d5f0a7f172 100644 --- a/src/renderer/src/lib/repo-slug-index.ts +++ b/src/renderer/src/lib/repo-slug-index.ts @@ -18,7 +18,6 @@ import { useAppStore } from '@/store' import type { Repo, GlobalSettings } from '../../../shared/types' import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client' import { - clearRepoSlugCacheValues, deleteRepoSlugCacheKey, nextRepoSlugFailureRetryDelay, readRepoSlugCache, @@ -69,15 +68,6 @@ export function clearRepoSlugCacheEntry(repoId: string): void { } } -/** Clear the entire slug cache. Useful for tests or full repo-list resets. */ -export function clearRepoSlugCache(): void { - clearRepoSlugCacheValues() - for (const key of slugResolutionInFlight.keys()) { - slugResolutionGeneration.set(key, (slugResolutionGeneration.get(key) ?? 0) + 1) - } - slugResolutionInFlight.clear() -} - async function resolveRepoSlug( repo: Repo, settings: Pick | null | undefined diff --git a/src/renderer/src/lib/setup-script-status.ts b/src/renderer/src/lib/setup-script-status.ts index 9f8dc0c4512..a23d0d1a06f 100644 --- a/src/renderer/src/lib/setup-script-status.ts +++ b/src/renderer/src/lib/setup-script-status.ts @@ -1,5 +1,5 @@ import { resolveHookCommandSourcePolicy } from '../../../shared/hook-command-source-policy' -import type { Repo, RepoHookSettings } from '../../../shared/types' +import type { Repo } from '../../../shared/types' import type { HookCheckResult } from '@/runtime/runtime-hooks-client' export function hasEffectiveSetupCommand(repo: Repo, hooksResult: HookCheckResult): boolean { @@ -20,28 +20,3 @@ export function hasEffectiveSetupCommand(repo: Repo, hooksResult: HookCheckResul return Boolean(sharedSetup) } - -export function buildImportedSetupHookSettings( - repo: Repo, - setup: string, - archive: string | undefined, - hasSharedHooks: boolean, - defaults: RepoHookSettings -): RepoHookSettings { - const current = repo.hookSettings - return { - ...defaults, - ...current, - setupRunPolicy: current?.setupRunPolicy ?? defaults.setupRunPolicy, - // Why: imported setup commands are stored as local settings. If a shared - // hook file exists, run-both preserves its archive hook; otherwise local - // settings need to be authoritative so the imported setup actually runs. - commandSourcePolicy: hasSharedHooks ? 'run-both' : 'local-only', - scripts: { - ...defaults.scripts, - ...current?.scripts, - setup, - archive: archive ?? current?.scripts?.archive ?? defaults.scripts.archive - } - } -} diff --git a/src/renderer/src/lib/workspace-port-actions.ts b/src/renderer/src/lib/workspace-port-actions.ts index 06970100cfd..07643a186a8 100644 --- a/src/renderer/src/lib/workspace-port-actions.ts +++ b/src/renderer/src/lib/workspace-port-actions.ts @@ -148,24 +148,6 @@ export async function openWorkspacePortInBrowser(args: { return { ok: true } } -export async function refreshWorkspacePortScanState(args: { - runtimeTarget: RuntimeClientTarget - setWorkspacePortScan: WorkspacePortScanSetter - setWorkspacePortScanRefreshing: WorkspacePortScanRefreshingSetter -}): Promise { - args.setWorkspacePortScanRefreshing(true) - try { - const scan = await scanWorkspacePortsForTarget(args.runtimeTarget) - args.setWorkspacePortScan({ - key: workspacePortScanKeyForTarget(args.runtimeTarget), - result: scan - }) - return scan - } finally { - args.setWorkspacePortScanRefreshing(false) - } -} - export async function refreshWorkspacePortScanAfterStop(args: { runtimeTarget: RuntimeClientTarget setWorkspacePortScan: WorkspacePortScanSetter diff --git a/src/renderer/src/store/slices/runtime-detected-agents.ts b/src/renderer/src/store/slices/runtime-detected-agents.ts index 7da5bef6da9..466c131f4e7 100644 --- a/src/renderer/src/store/slices/runtime-detected-agents.ts +++ b/src/renderer/src/store/slices/runtime-detected-agents.ts @@ -35,10 +35,6 @@ export function _getRuntimeDetectPromiseCountForTest(): number { return runtimeDetectPromises.size } -export function _getRuntimeRefreshPromiseCountForTest(): number { - return runtimeRefreshPromises.size -} - export const createRuntimeDetectedAgentsSlice: StateCreator< AppState, [], diff --git a/src/shared/agent-session-host-authority.ts b/src/shared/agent-session-host-authority.ts index bd138cefd62..96e079afb63 100644 --- a/src/shared/agent-session-host-authority.ts +++ b/src/shared/agent-session-host-authority.ts @@ -28,8 +28,6 @@ export const AGENT_SESSION_RPC_ERROR_CODES = [ 'execution_owner_unavailable' ] as const -export type AgentSessionRpcErrorCode = (typeof AGENT_SESSION_RPC_ERROR_CODES)[number] - export const AGENT_SESSION_CLAIM_DIGEST_VERSION = 1 as const export const AGENT_SESSION_EXECUTION_OWNER_PROTOCOL_VERSION = 2 as const diff --git a/src/shared/agent-session-option-catalog.ts b/src/shared/agent-session-option-catalog.ts index b5520afaa51..6ba6ee65043 100644 --- a/src/shared/agent-session-option-catalog.ts +++ b/src/shared/agent-session-option-catalog.ts @@ -49,14 +49,6 @@ export function findCatalogOption( return model?.options.find((option) => option.id === optionId) } -export function catalogDefaultModel(catalog: AgentSessionOptionCatalog): CatalogModel | undefined { - return catalog.models.find((model) => model.isDefault) ?? catalog.models[0] -} - -export function catalogDefaultValues(model: CatalogModel): Record { - return Object.fromEntries(model.options.map((option) => [option.id, option.kind.defaultValue])) -} - /** Merge live rows over the static seed while retaining only option shapes Orca * can actually map. Newly discovered ids remain model-only until cataloged. */ export function mergeCatalogModels( diff --git a/src/shared/commit-message-generation.ts b/src/shared/commit-message-generation.ts index cd3f8d72a62..3c5c92ad835 100644 --- a/src/shared/commit-message-generation.ts +++ b/src/shared/commit-message-generation.ts @@ -11,14 +11,6 @@ export type CommitMessageDraftContext = { linkedIssue?: number | null } -export type CommitMessageDraftOptions = { - agentId: CommitMessageDraftAgent - model: string - thinkingLevel?: string - customPrompt?: string - customAgentCommand?: string -} - export type GeneratedCommitMessage = { subject: string body: string diff --git a/src/shared/custom-pet-media-limits.ts b/src/shared/custom-pet-media-limits.ts deleted file mode 100644 index f3778069e40..00000000000 --- a/src/shared/custom-pet-media-limits.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const MAX_CUSTOM_PET_FILE_BYTES = 64 * 1024 * 1024 - -// Why: sprite processing holds decoded image, canvas, ImageData, PNG, and -// optional bitmap copies at once, so encoded bytes alone are not a safe bound. -export const MAX_CUSTOM_PET_SHEET_PIXELS = 4 * 1024 * 1024 -export const MAX_CUSTOM_PET_SHEET_DIMENSION = 8_192 -export const MAX_CUSTOM_PET_DETECTED_FRAMES = 128 - -export function isCustomPetSheetSizeSafe(width: number, height: number): boolean { - return ( - Number.isSafeInteger(width) && - Number.isSafeInteger(height) && - width > 0 && - height > 0 && - width <= MAX_CUSTOM_PET_SHEET_DIMENSION && - height <= MAX_CUSTOM_PET_SHEET_DIMENSION && - width * height <= MAX_CUSTOM_PET_SHEET_PIXELS - ) -} diff --git a/src/shared/daemon-audit-eligibility.ts b/src/shared/daemon-audit-eligibility.ts index 5e3bde6fcd0..4e89af300eb 100644 --- a/src/shared/daemon-audit-eligibility.ts +++ b/src/shared/daemon-audit-eligibility.ts @@ -69,7 +69,6 @@ export const DAEMON_AUDIT_PROCESS_REASON_VALUES = [ 'windows_named_pipe_missing' ] as const -export type DaemonAuditState = (typeof DAEMON_AUDIT_STATE_VALUES)[number] export type DaemonAuditTrigger = (typeof DAEMON_AUDIT_TRIGGER_VALUES)[number] export type DaemonAuditFailureTrigger = Exclude export type DaemonEvidenceSource = (typeof DAEMON_EVIDENCE_SOURCE_VALUES)[number] diff --git a/src/shared/daemon-lifecycle-telemetry.ts b/src/shared/daemon-lifecycle-telemetry.ts index 5904195316a..1ec13c985f6 100644 --- a/src/shared/daemon-lifecycle-telemetry.ts +++ b/src/shared/daemon-lifecycle-telemetry.ts @@ -17,13 +17,11 @@ export const DAEMON_RETIRE_REASONS = ['died_respawn'] as const export type DaemonRetireReason = (typeof DAEMON_RETIRE_REASONS)[number] export const DAEMON_LIFECYCLE_TRANSITIONS = ['replaced', 'retired'] as const -export type DaemonLifecycleTransition = (typeof DAEMON_LIFECYCLE_TRANSITIONS)[number] export const DAEMON_LIFECYCLE_REASONS = [ ...DAEMON_REPLACE_REASONS, ...DAEMON_RETIRE_REASONS ] as const -export type DaemonLifecycleReason = (typeof DAEMON_LIFECYCLE_REASONS)[number] // Bucketed, never raw: exact live-session counts could fingerprint heavy users. `unknown` when // the count couldn't be verified (null) — e.g. a wedged daemon or an already-dead respawn target. diff --git a/src/shared/ephemeral-vm-recipes.ts b/src/shared/ephemeral-vm-recipes.ts index 071f1e89c46..9cb4349d928 100644 --- a/src/shared/ephemeral-vm-recipes.ts +++ b/src/shared/ephemeral-vm-recipes.ts @@ -1,10 +1,6 @@ import { z } from 'zod' import { parsePairingCode } from './pairing' -import { - DEFAULT_SSH_RELAY_GRACE_PERIOD_SECONDS, - MAX_SSH_RELAY_GRACE_PERIOD_SECONDS, - MIN_SSH_RELAY_GRACE_PERIOD_SECONDS -} from './ssh-types' +import { MAX_SSH_RELAY_GRACE_PERIOD_SECONDS, MIN_SSH_RELAY_GRACE_PERIOD_SECONDS } from './ssh-types' import { assertJsonTextStructureWithinLimits } from './json-text-structure-limit' const JsonValueSchema: z.ZodType = z.lazy(() => @@ -67,8 +63,6 @@ export const EphemeralVmRecipeSshTargetSchema = z }) .strict() -export type EphemeralVmRecipeSshTarget = z.infer - const EphemeralVmRecipeOrcaServerConnectionSchema = z .object({ type: z.literal('orca-server'), @@ -114,10 +108,6 @@ export const EphemeralVmRecipeResultSchema = z.union([ EphemeralVmRecipeConnectionResultSchema ]) -export type EphemeralVmRecipeLegacyResult = z.infer -export type EphemeralVmRecipeConnectionResult = z.infer< - typeof EphemeralVmRecipeConnectionResultSchema -> export type EphemeralVmRecipeResult = z.infer export type EphemeralVmRecipeResultParseResult = @@ -190,16 +180,6 @@ export function getEphemeralVmRecipeResultPairingCode( return connection.type === 'orca-server' ? connection.pairingCode : null } -export function getEphemeralVmRecipeResultUserData( - result: EphemeralVmRecipeResult -): Record | undefined { - return result.userData -} - -export function getDefaultSshRelayGracePeriodSeconds(): number { - return DEFAULT_SSH_RELAY_GRACE_PERIOD_SECONDS -} - export function isAbsoluteRuntimePath(path: string): boolean { return ( path.startsWith('/') || diff --git a/src/shared/ephemeral-vm-runtimes.ts b/src/shared/ephemeral-vm-runtimes.ts index 517897fcd44..196c3e08184 100644 --- a/src/shared/ephemeral-vm-runtimes.ts +++ b/src/shared/ephemeral-vm-runtimes.ts @@ -27,10 +27,6 @@ export type EphemeralVmCleanupStatus = z.infer - const EphemeralVmRuntimeRecipeSchema = z .object({ id: z.string().min(1), diff --git a/src/shared/folder-workspaces.ts b/src/shared/folder-workspaces.ts index 9798db1f67d..675117dd3eb 100644 --- a/src/shared/folder-workspaces.ts +++ b/src/shared/folder-workspaces.ts @@ -1,4 +1,4 @@ -import type { FolderWorkspace, FolderWorkspaceLinkedTask, ProjectGroup } from './types' +import type { FolderWorkspace, ProjectGroup } from './types' import { isTuiAgent } from './tui-agent-config' import { normalizeStoredTaskSourceContext } from './task-source-context' import { normalizeWorkspaceLinkedItem } from './workspace-linked-item' @@ -12,12 +12,6 @@ export function normalizeFolderWorkspaceName( return trimmed.length > 0 ? trimmed : fallback } -export function normalizeFolderWorkspaceLinkedTask( - value: unknown -): FolderWorkspaceLinkedTask | null { - return normalizeWorkspaceLinkedItem(value) -} - export function normalizeFolderWorkspaces( value: unknown, projectGroups: readonly ProjectGroup[] diff --git a/src/shared/hermes-run-output-limits.ts b/src/shared/hermes-run-output-limits.ts index 99d16feaff0..d456effe8a8 100644 --- a/src/shared/hermes-run-output-limits.ts +++ b/src/shared/hermes-run-output-limits.ts @@ -1,8 +1,6 @@ -export const HERMES_PRIMARY_OUTPUT_MAX_BYTES = 5 * 1024 * 1024 export const HERMES_SESSION_TRANSCRIPT_MAX_BYTES = 8 * 1024 * 1024 export const HERMES_SESSION_TRANSCRIPT_MAX_MESSAGES = 10_000 export const HERMES_RUN_PAGE_MAX_RETAINED_BYTES = 32 * 1024 * 1024 -export const HERMES_RUN_PAGE_MAX_RUNS = 100 export const HERMES_RUN_HYDRATION_CONCURRENCY = 2 export const HERMES_SESSION_RUN_METADATA_MAX_BYTES = 16 * 1024 export const HERMES_RUN_PAGE_OUTPUT_OMITTED_ERROR = @@ -10,7 +8,6 @@ export const HERMES_RUN_PAGE_OUTPUT_OMITTED_ERROR = export const HERMES_SESSION_TRANSCRIPT_TRUNCATED_ERROR = 'Session transcript truncated because it exceeds the 8 MiB or 10,000-message history limit' -const HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES = HERMES_SESSION_TRANSCRIPT_MAX_BYTES + 1 const HERMES_SESSION_TRANSCRIPT_TRUNCATION_NOTICE = `[${HERMES_SESSION_TRANSCRIPT_TRUNCATED_ERROR}.]` const HERMES_SESSION_TRANSCRIPT_SOURCE_TRUNCATED_COLUMN = 'orca_source_truncated' @@ -29,24 +26,6 @@ export const HERMES_SESSION_RUN_SELECT_SQL = `SELECT FROM sessions WHERE id = ?` -export const HERMES_SESSION_TRANSCRIPT_SELECT_SQL = `SELECT - CAST(substr(CAST(role AS BLOB), 1, ${HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES}) AS TEXT) AS role, - CAST(substr(CAST(content AS BLOB), 1, ${HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES}) AS TEXT) AS content, - CAST(substr(CAST(tool_name AS BLOB), 1, ${HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES}) AS TEXT) AS tool_name, - CAST(substr(CAST(reasoning AS BLOB), 1, ${HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES}) AS TEXT) AS reasoning, - CAST(substr(CAST(reasoning_content AS BLOB), 1, ${HERMES_SESSION_TRANSCRIPT_FIELD_MAX_BYTES}) AS TEXT) AS reasoning_content, - CASE WHEN - length(CAST(role AS BLOB)) > ${HERMES_SESSION_TRANSCRIPT_MAX_BYTES} - OR length(CAST(content AS BLOB)) > ${HERMES_SESSION_TRANSCRIPT_MAX_BYTES} - OR length(CAST(tool_name AS BLOB)) > ${HERMES_SESSION_TRANSCRIPT_MAX_BYTES} - OR length(CAST(reasoning AS BLOB)) > ${HERMES_SESSION_TRANSCRIPT_MAX_BYTES} - OR length(CAST(reasoning_content AS BLOB)) > ${HERMES_SESSION_TRANSCRIPT_MAX_BYTES} - THEN 1 ELSE 0 END AS ${HERMES_SESSION_TRANSCRIPT_SOURCE_TRUNCATED_COLUMN} -FROM messages -WHERE session_id = ? -ORDER BY timestamp, id -LIMIT ${HERMES_SESSION_TRANSCRIPT_MAX_MESSAGES + 1}` - type HermesRunPageLimits = { maxConcurrent?: number maxRetainedBytes?: number diff --git a/src/shared/hermes-run-ref-retention.ts b/src/shared/hermes-run-ref-retention.ts index 15aaefe3b13..6d4834db320 100644 --- a/src/shared/hermes-run-ref-retention.ts +++ b/src/shared/hermes-run-ref-retention.ts @@ -1,14 +1,6 @@ export const HERMES_RUN_REF_MAX_ENTRIES = 10_000 export const HERMES_RUN_REF_ID_MAX_BYTES = 4 * 1024 -export const HERMES_SESSION_RUN_REFS_SELECT_SQL = `SELECT - CASE WHEN length(CAST(id AS BLOB)) <= ${HERMES_RUN_REF_ID_MAX_BYTES} THEN id ELSE NULL END AS id, - CASE WHEN typeof(started_at) IN ('integer', 'real') THEN started_at ELSE NULL END AS started_at -FROM sessions -WHERE id LIKE ? ESCAPE '\\' -ORDER BY started_at DESC -LIMIT ${HERMES_RUN_REF_MAX_ENTRIES + 1}` - export type HermesSortableRunRef = { id: string run_at: string | null diff --git a/src/shared/hosted-review.ts b/src/shared/hosted-review.ts index da6584fe574..90a97d0afb9 100644 --- a/src/shared/hosted-review.ts +++ b/src/shared/hosted-review.ts @@ -202,14 +202,6 @@ export type HostedReviewQueueSummary = { draft?: boolean } -export type HostedReviewQueueKey = - | 'mine' - | 'requested' - | 'agent' - | 'teammate' - | 'needs-response' - | 'ready-to-merge' - export type HostedReviewQueueState = 'mine' | 'requested' | 'agent' | 'teammate' export type HostedReviewQueueClassification = { diff --git a/src/shared/html-to-pdf-memory-limit.ts b/src/shared/html-to-pdf-memory-limit.ts deleted file mode 100644 index 061d60eb430..00000000000 --- a/src/shared/html-to-pdf-memory-limit.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { measureUtf8ByteLength } from './utf8-byte-limits' - -export const HTML_TO_PDF_MAX_INPUT_BYTES = 32 * 1024 * 1024 -export const HTML_TO_PDF_MEMORY_LIMIT_ERROR = 'HTML export exceeds the PDF memory limit' - -export function assertHtmlToPdfInputWithinMemoryLimit( - html: string, - maxBytes = HTML_TO_PDF_MAX_INPUT_BYTES -): void { - if (measureUtf8ByteLength(html, { stopAfterBytes: maxBytes }).exceededLimit) { - throw new Error(HTML_TO_PDF_MEMORY_LIMIT_ERROR) - } -} diff --git a/src/shared/left-sidebar-appearance.ts b/src/shared/left-sidebar-appearance.ts index bb77476ba36..8b2f6c58739 100644 --- a/src/shared/left-sidebar-appearance.ts +++ b/src/shared/left-sidebar-appearance.ts @@ -1,5 +1,4 @@ import { HEX_COLOR_RE } from './color-validation' -import type { LeftSidebarAppearanceMode } from './types' export const LEFT_SIDEBAR_APPEARANCE_MODES = ['default', 'match-terminal', 'tinted'] as const @@ -7,12 +6,6 @@ export const DEFAULT_LEFT_SIDEBAR_TINT_COLOR = '#18181b' export const DEFAULT_LEFT_SIDEBAR_TINT_OPACITY = 0.08 export const MAX_LEFT_SIDEBAR_TINT_OPACITY = 0.35 -export function normalizeLeftSidebarAppearanceMode(value: unknown): LeftSidebarAppearanceMode { - return LEFT_SIDEBAR_APPEARANCE_MODES.includes(value as LeftSidebarAppearanceMode) - ? (value as LeftSidebarAppearanceMode) - : 'default' -} - export function normalizeLeftSidebarTintColor(value: unknown): string { if (typeof value !== 'string') { return DEFAULT_LEFT_SIDEBAR_TINT_COLOR diff --git a/src/shared/linear-agent-access.ts b/src/shared/linear-agent-access.ts index eb49dee4af4..2ffa17cb406 100644 --- a/src/shared/linear-agent-access.ts +++ b/src/shared/linear-agent-access.ts @@ -109,11 +109,6 @@ export type LinearWriteTargetRequest = { context?: LinearCurrentIssueContextHints } -export type LinearTeamDiscoveryRequest = { - teamInput?: string - workspaceId?: string | 'all' -} - export type LinearIssueListFilter = 'assigned' | 'created' | 'all' | 'completed' | 'open' export type LinearIssueListRequest = { diff --git a/src/shared/linux-proc-port-scan-limits.ts b/src/shared/linux-proc-port-scan-limits.ts index dd1f9fd5c6e..af6148cd283 100644 --- a/src/shared/linux-proc-port-scan-limits.ts +++ b/src/shared/linux-proc-port-scan-limits.ts @@ -1,7 +1,6 @@ import { readNodeFileWithinLimit } from './node-bounded-file-reader' export const LINUX_PROC_NETWORK_TABLE_MAX_BYTES = 8 * 1024 * 1024 -export const LINUX_PROC_LISTENING_SOCKET_MAX_ENTRIES = 2_048 export const LINUX_PROC_PROCESS_METADATA_MAX_BYTES = 8 * 1024 * 1024 export const LINUX_PROC_PROCESS_METADATA_FILE_MAX_BYTES = 64 * 1024 diff --git a/src/shared/native-file-drop.ts b/src/shared/native-file-drop.ts index 101dfd37d83..4a7ee1860d1 100644 --- a/src/shared/native-file-drop.ts +++ b/src/shared/native-file-drop.ts @@ -135,14 +135,6 @@ export function resolveNativeFileDropPath( return null } -export function measureNativeFileDropPathBytes(paths: readonly string[]): number { - let byteLength = 0 - for (const path of paths) { - byteLength += measureClipboardTextByteLength(path).byteLength - } - return byteLength -} - export function validateNativeFileDropPaths( paths: readonly string[], options: { diff --git a/src/shared/persisted-state-file-bounds.ts b/src/shared/persisted-state-file-bounds.ts index 4fe8a977bb3..8ad92c95435 100644 --- a/src/shared/persisted-state-file-bounds.ts +++ b/src/shared/persisted-state-file-bounds.ts @@ -1,11 +1,7 @@ import { randomUUID, type Hash } from 'node:crypto' import { mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs' import { dirname } from 'node:path' -import { - NodeFileReadTooLargeError, - readNodeFileSyncWithinLimit, - type BoundedNodeFileRead -} from './node-bounded-file-reader' +import { readNodeFileSyncWithinLimit, type BoundedNodeFileRead } from './node-bounded-file-reader' import { JsonStringifyByteLimitError, stringifyJsonWithinByteLimit @@ -38,12 +34,6 @@ export class PersistedStateSecretCapacityError extends Error { } } -export function isPersistedStateFileCapacityError( - error: unknown -): error is NodeFileReadTooLargeError { - return error instanceof NodeFileReadTooLargeError -} - export function readPersistedStateJsonFileSync( filePath: string, maxBytes = ORCA_PERSISTED_STATE_MAX_BYTES, @@ -86,14 +76,6 @@ export function stringifyPrettyPersistedStateWithinLimit( return stringifyJsonWithinByteLimit(value, maxBytes, 2) } -export function encodePersistedStateJsonStringContent( - value: string, - maxBytes = ORCA_PERSISTED_STATE_MAX_BYTES -): string { - const { serialized } = stringifyJsonWithinByteLimit(value, maxBytes) - return serialized.slice(1, -1) -} - export function assertPersistedStateSecretWithinLimit( value: string, maxBytes = ORCA_PERSISTED_STATE_SECRET_MAX_BYTES diff --git a/src/shared/pr-refresh-memory-limits.ts b/src/shared/pr-refresh-memory-limits.ts deleted file mode 100644 index 8b695adede7..00000000000 --- a/src/shared/pr-refresh-memory-limits.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const PR_REFRESH_QUEUE_ENTRY_LIMIT = 1_024 -export const PR_REFRESH_ALIAS_LIMIT = 64 -export const PR_REFRESH_VISIBLE_CANDIDATE_LIMIT = 1_024 -export const PR_REFRESH_RETRY_STATE_LIMIT = 1_024 -export const PR_REFRESH_ACTIVE_SCOPE_LIMIT = 1_024 diff --git a/src/shared/pty-source-credit-contract.ts b/src/shared/pty-source-credit-contract.ts index a97b66ec4a2..4c3a643b32c 100644 --- a/src/shared/pty-source-credit-contract.ts +++ b/src/shared/pty-source-credit-contract.ts @@ -1,4 +1,3 @@ -export const PTY_SOURCE_CREDIT_VERSION = 1 export const DEFAULT_PTY_SOURCE_WINDOW_SU = 256 * 1024 export const MAX_PTY_ACK_ENTRIES = 64 diff --git a/src/shared/relay-json-admission.ts b/src/shared/relay-json-admission.ts deleted file mode 100644 index a9d256ba904..00000000000 --- a/src/shared/relay-json-admission.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { assertJsonTextStructureWithinLimits } from './json-text-structure-limit' - -export const RELAY_JSON_MAX_STRUCTURAL_TOKENS = 1_000_000 -export const RELAY_JSON_MAX_NESTING_DEPTH = 128 - -export function parseRelayJsonText(text: string): T { - assertJsonTextStructureWithinLimits(text, { - structuralTokens: RELAY_JSON_MAX_STRUCTURAL_TOKENS, - nestingDepth: RELAY_JSON_MAX_NESTING_DEPTH - }) - return JSON.parse(text) as T -} diff --git a/src/shared/remote-runtime-memory-limits.ts b/src/shared/remote-runtime-memory-limits.ts index 62cb62d03cf..8152f58ef66 100644 --- a/src/shared/remote-runtime-memory-limits.ts +++ b/src/shared/remote-runtime-memory-limits.ts @@ -17,7 +17,6 @@ export const REMOTE_RUNTIME_MAX_PROCESS_PENDING_RPC_BYTES = REMOTE_RUNTIME_MAX_P export const REMOTE_RUNTIME_MAX_READY_WAITERS = REMOTE_RUNTIME_MAX_PENDING_REQUESTS + REMOTE_RUNTIME_MAX_SUBSCRIPTIONS export const REMOTE_RUNTIME_MAX_OUTBOUND_BINARY_FRAME_BYTES = 8 * 1024 * 1024 -export const REMOTE_RUNTIME_MAX_SUBSCRIPTION_ID_BYTES = 4 * 1024 export function serializeRemoteRuntimePayload(value: unknown): string { try { diff --git a/src/shared/remote-runtime-shared-control-state.ts b/src/shared/remote-runtime-shared-control-state.ts index 4f62d8298ff..c74c50916c9 100644 --- a/src/shared/remote-runtime-shared-control-state.ts +++ b/src/shared/remote-runtime-shared-control-state.ts @@ -77,22 +77,6 @@ export function refreshSharedControlPendingRequestTimeouts( } } -export function waitForSharedControlReady(ready: Promise, timeoutMs: number): Promise { - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(remoteRuntimeUnavailableError()), timeoutMs) - void ready.then( - () => { - clearTimeout(timeout) - resolve() - }, - (error) => { - clearTimeout(timeout) - reject(error) - } - ) - }) -} - export function rejectAllSharedControlPendingRequests( pendingRequests: Map>, error?: Error diff --git a/src/shared/remote-workspace-types.ts b/src/shared/remote-workspace-types.ts index 419bb765116..3c08f272864 100644 --- a/src/shared/remote-workspace-types.ts +++ b/src/shared/remote-workspace-types.ts @@ -31,11 +31,6 @@ export type RemoteWorkspaceConnectedClient = { isCurrent?: boolean } -export type RemoteWorkspacePatch = { - kind: 'replace-session' - session: RemoteWorkspaceSession -} - export type RemoteWorkspacePatchResult = | { ok: true diff --git a/src/shared/runtime-bootstrap.ts b/src/shared/runtime-bootstrap.ts index efa224d749b..993ab5e023d 100644 --- a/src/shared/runtime-bootstrap.ts +++ b/src/shared/runtime-bootstrap.ts @@ -1,5 +1,4 @@ import { join } from 'node:path' -import { assertJsonTextStructureWithinLimits } from './json-text-structure-limit' export type RuntimeTransportMetadata = | { @@ -23,18 +22,6 @@ export type RuntimeMetadata = { startedAt: number } -export const MAX_RUNTIME_METADATA_FILE_BYTES = 64 * 1024 -export const MAX_RUNTIME_METADATA_JSON_STRUCTURAL_TOKENS = 16 * 1024 -export const MAX_RUNTIME_METADATA_JSON_NESTING_DEPTH = 32 - -export function parseRuntimeMetadataJson(serialized: string): RuntimeMetadata { - assertJsonTextStructureWithinLimits(serialized, { - structuralTokens: MAX_RUNTIME_METADATA_JSON_STRUCTURAL_TOKENS, - nestingDepth: MAX_RUNTIME_METADATA_JSON_NESTING_DEPTH - }) - return JSON.parse(serialized) as RuntimeMetadata -} - // Why: the CLI must handle metadata files written by older Orca versions that // used a singular `transport` field. This helper extracts the first transport // matching the given kinds from either the new `transports` array or the diff --git a/src/shared/runtime-environments.ts b/src/shared/runtime-environments.ts index 131414631c2..f1cfd64085b 100644 --- a/src/shared/runtime-environments.ts +++ b/src/shared/runtime-environments.ts @@ -10,8 +10,6 @@ export const RuntimeAccessEndpointSchema = z.object({ publicKeyB64: z.string().min(1) }) -export type RuntimeAccessEndpoint = z.infer - export const PublicRuntimeAccessEndpointSchema = RuntimeAccessEndpointSchema.omit({ deviceToken: true, publicKeyB64: true diff --git a/src/shared/runtime-types.ts b/src/shared/runtime-types.ts index d0f98e68360..c357ff63d45 100644 --- a/src/shared/runtime-types.ts +++ b/src/shared/runtime-types.ts @@ -545,10 +545,6 @@ export type RuntimeTerminalOrphanAdoptionResult = { snapshot: RuntimeMobileSessionTabsResult } -export type RuntimeWorktreeTerminalSleepFailure = - | 'terminal_liveness_unavailable' - | 'terminal_worktree_sleep_still_live' - export type RuntimeWorktreeTerminalSleepResult = { stopped: number stoppedPtyIds: string[] @@ -1194,18 +1190,10 @@ export type BrowserCaptureStopResult = { stopped: boolean } -export type BrowserExecResult = { - output: unknown -} - export type BrowserTabCreateResult = { browserPageId: string } -export type BrowserTabCloseResult = { - closed: boolean -} - export type BrowserErrorCode = | 'browser_no_tab' | 'browser_tab_not_found' @@ -1222,13 +1210,6 @@ export type BrowserErrorCode = | 'browser_timeout' | 'browser_error' -export type EmulatorErrorCode = - | 'emulator_no_active' - | 'emulator_device_not_found' - | 'emulator_helper_failed' - | 'emulator_not_macos' - | 'emulator_error' - // Keep the broad runtime-types import surface stable while letting computer-use // CI watch a narrow contract file instead of every runtime type change. export * from './computer-use-runtime-types' diff --git a/src/shared/serve-update-handoff.ts b/src/shared/serve-update-handoff.ts index 3dddd2a38da..4d2e1934852 100644 --- a/src/shared/serve-update-handoff.ts +++ b/src/shared/serve-update-handoff.ts @@ -1,11 +1,7 @@ import { join } from 'node:path' -import { assertJsonTextStructureWithinLimits } from './json-text-structure-limit' export const SERVE_UPDATE_HANDOFF_PATH_ENV = 'ORCA_SERVE_UPDATE_HANDOFF_PATH' export const SERVE_UPDATE_HANDOFF_FILE = 'serve-update-handoff.json' -export const MAX_SERVE_UPDATE_HANDOFF_FILE_BYTES = 64 * 1024 -export const MAX_SERVE_UPDATE_HANDOFF_JSON_STRUCTURAL_TOKENS = 16 * 1024 -export const MAX_SERVE_UPDATE_HANDOFF_JSON_NESTING_DEPTH = 32 export type ServeUpdateHandoffState = | { @@ -65,14 +61,6 @@ export function parseServeUpdateHandoffState(value: unknown): ServeUpdateHandoff return state as ServeUpdateHandoffState } -export function parseServeUpdateHandoffJson(serialized: string): ServeUpdateHandoffState | null { - assertJsonTextStructureWithinLimits(serialized, { - structuralTokens: MAX_SERVE_UPDATE_HANDOFF_JSON_STRUCTURAL_TOKENS, - nestingDepth: MAX_SERVE_UPDATE_HANDOFF_JSON_NESTING_DEPTH - }) - return parseServeUpdateHandoffState(JSON.parse(serialized)) -} - export function parseServeSupervisorMessage(value: unknown): ServeSupervisorMessage | null { if (!value || typeof value !== 'object') { return null diff --git a/src/shared/source-control-create-review-intent.ts b/src/shared/source-control-create-review-intent.ts index b7266708b25..45ee2210c7e 100644 --- a/src/shared/source-control-create-review-intent.ts +++ b/src/shared/source-control-create-review-intent.ts @@ -2,7 +2,6 @@ import { isBehindOnlyUpstream, shouldForcePushWithLeaseForUpstream } from './git import type { HostedReviewCreationEligibility } from './hosted-review' import { supportsHostedReviewCreation } from './hosted-review-creation-providers' import type { GitUpstreamStatus } from './git-status-types' -import type { SourceControlPrimaryActionDecision } from './source-control-primary-action-decision-types' export type CreateReviewIntentKind = | 'dirty' @@ -91,13 +90,3 @@ export function resolveCreateReviewIntentEligibility({ return { eligible: false, kind: null } } - -export function resolveVisibleCreateReviewHeaderAction({ - createPrHeaderAction -}: { - createPrHeaderAction: SourceControlPrimaryActionDecision | null -}): SourceControlPrimaryActionDecision | null { - // Why: keep a stable header anchor; disable Create Review when the branch is - // not ready instead of hiding it and shifting toolbar layout. - return createPrHeaderAction -} diff --git a/src/shared/ssh-retained-payload-admission.ts b/src/shared/ssh-retained-payload-admission.ts index cc173c577d5..ba018eb0f5b 100644 --- a/src/shared/ssh-retained-payload-admission.ts +++ b/src/shared/ssh-retained-payload-admission.ts @@ -10,7 +10,6 @@ import { clampUtf8TextPrefix, measureUtf8ByteLength } from './utf8-byte-limits' export const SSH_RETAINED_IDENTIFIER_MAX_UTF8_BYTES = 1024 export const SSH_CONNECTION_ERROR_MAX_UTF8_BYTES = 16 * 1024 export const SSH_PROVIDER_EPOCH_MAX_UTF8_BYTES = 128 -export const SSH_CREDENTIAL_DETAIL_MAX_UTF8_BYTES = 16 * 1024 export const SSH_DETECTED_PORTS_MAX_ENTRIES = 50 export const SSH_DETECTED_PORT_HOST_MAX_UTF8_BYTES = 1024 export const SSH_DETECTED_PORT_PROCESS_NAME_MAX_UTF8_BYTES = 4 * 1024 diff --git a/src/shared/task-source-context.ts b/src/shared/task-source-context.ts index 38e1fb3388f..e1576e2de92 100644 --- a/src/shared/task-source-context.ts +++ b/src/shared/task-source-context.ts @@ -192,12 +192,6 @@ export function buildWorkspaceRunContext(args: { } } -export function getWorkspaceRunRuntimeSettings( - context: Pick | null | undefined -): Pick { - return getTaskSourceRuntimeSettings(context ? { hostId: context.hostId } : null) -} - function getRepoHostId(repo: Pick): ExecutionHostId { const explicit = normalizeExecutionHostId(repo.executionHostId) if (explicit) { diff --git a/src/shared/telemetry-events.ts b/src/shared/telemetry-events.ts index bcf92ef54bc..18f7cd8986f 100644 --- a/src/shared/telemetry-events.ts +++ b/src/shared/telemetry-events.ts @@ -124,7 +124,6 @@ export const addRepoSetupStepActionSchema = z.enum([ 'open_existing', 'back' ]) -export type AddRepoSetupStepAction = z.infer export const addRepoExistingWorkspaceSourceSchema = z.enum([ 'local_folder_picker', @@ -164,7 +163,6 @@ export const addRepoDefaultCheckoutHandoffReasonSchema = z.enum([ ]) export const setupScriptImportProviderSchema = z.enum(SETUP_SCRIPT_IMPORT_PROVIDERS) -export type SetupScriptImportProviderTelemetry = z.infer // Separate enum from `errorClassSchema` — different domain (git/filesystem worktree-create failures); merging would couple the two forever. export const workspaceCreateErrorClassSchema = z.enum([ @@ -215,7 +213,6 @@ export const featureWallTileIdSchema = z.enum([ 'tile-11', 'tile-12' ]) -export type FeatureWallTileIdTelemetry = z.infer export const featureWallOpenSourceSchema = z.enum(['help_menu', 'popup', 'onboarding', 'unknown']) export type FeatureWallOpenSourceTelemetry = z.infer @@ -227,13 +224,10 @@ export const featureWallWorkflowIdSchema = z.enum([ 'workbench', 'review' ]) -export type FeatureWallWorkflowIdTelemetry = z.infer export const featureWallTourDepthStepSchema = z.enum(FEATURE_WALL_TOUR_DEPTH_STEPS) -export type FeatureWallTourDepthStepTelemetry = z.infer export const featureWallExitActionSchema = z.enum(FEATURE_WALL_EXIT_ACTIONS) -export type FeatureWallExitActionTelemetry = z.infer // `env_var` absent — env-var/CI paths override consent at runtime only, never firing an opt-in/out event. // `first_launch_notice` absent — the new-user cohort has no first-launch surface; those opt-outs come via `'settings'`. @@ -1542,7 +1536,6 @@ function eventsWithShapeKey(key: string): ReadonlySet { // Cohort injection is gated on this derived set because `.strict()` schemas drop events that don't declare `nth_repo_added`. const COHORT_EXTENDED_SET = eventsWithShapeKey('nth_repo_added') -export const COHORT_EXTENDED: readonly EventName[] = Array.from(COHORT_EXTENDED_SET) // Compile-time roster guarding the runtime injection set against silent schema drift. type _CohortExtendedRoster = diff --git a/src/shared/terminal-custom-themes.ts b/src/shared/terminal-custom-themes.ts index df6b15f5745..f91b7d8e22e 100644 --- a/src/shared/terminal-custom-themes.ts +++ b/src/shared/terminal-custom-themes.ts @@ -15,8 +15,6 @@ export type TerminalCustomTheme = { unsupportedFeatures?: string[] } -export type TerminalThemeSelection = string - export type WarpThemeImportSource = | { kind: 'auto' } | { kind: 'chooseFile' } diff --git a/src/shared/terminal-scrollback-policy.ts b/src/shared/terminal-scrollback-policy.ts index 3c831234113..0e8753593e9 100644 --- a/src/shared/terminal-scrollback-policy.ts +++ b/src/shared/terminal-scrollback-policy.ts @@ -4,10 +4,6 @@ export const DESKTOP_TERMINAL_SCROLLBACK_ROWS_MAX = 50_000 export const DESKTOP_TERMINAL_SCROLLBACK_ROW_PRESETS = [5_000, 10_000, 25_000, 50_000] as const export const LEGACY_TERMINAL_SCROLLBACK_BYTES_1_MB = 1_000_000 -export const LEGACY_TERMINAL_SCROLLBACK_BYTES_10_MB = 10_000_000 -export const LEGACY_TERMINAL_SCROLLBACK_BYTES_25_MB = 25_000_000 -export const LEGACY_TERMINAL_SCROLLBACK_BYTES_50_MB = 50_000_000 -export const LEGACY_TERMINAL_SCROLLBACK_BYTES_100_MB = 100_000_000 export const LEGACY_TERMINAL_SCROLLBACK_BUCKET_5K_MAX_BYTES = 17_500_000 export const LEGACY_TERMINAL_SCROLLBACK_BUCKET_10K_MAX_BYTES = 37_500_000 diff --git a/src/shared/terminal-size-limits.ts b/src/shared/terminal-size-limits.ts deleted file mode 100644 index fca3d9c8754..00000000000 --- a/src/shared/terminal-size-limits.ts +++ /dev/null @@ -1,34 +0,0 @@ -export const MAX_TERMINAL_COLS = 500 -export const MAX_TERMINAL_ROWS = 500 - -export function terminalSizeAdmissionError( - cols: unknown, - rows: unknown, - field: string, - options: { allowMissing?: boolean } = {} -): string | null { - for (const [name, value, max] of [ - ['cols', cols, MAX_TERMINAL_COLS], - ['rows', rows, MAX_TERMINAL_ROWS] - ] as const) { - if (options.allowMissing && value === undefined) { - continue - } - if (!Number.isSafeInteger(value) || (value as number) < 1 || (value as number) > max) { - return `${field}.${name} must be an integer from 1 through ${max}` - } - } - return null -} - -export function isValidTerminalSize(cols: unknown, rows: unknown): boolean { - return terminalSizeAdmissionError(cols, rows, 'terminal size') === null -} - -export function normalizeTerminalSize( - cols: unknown, - rows: unknown, - fallback: { cols: number; rows: number } = { cols: 80, rows: 24 } -): { cols: number; rows: number } { - return isValidTerminalSize(cols, rows) ? { cols: cols as number, rows: rows as number } : fallback -} diff --git a/src/shared/terminal-title-display.ts b/src/shared/terminal-title-display.ts deleted file mode 100644 index a0f2ad6894c..00000000000 --- a/src/shared/terminal-title-display.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - AGY_AGENT_NAME_RE, - DROID_AGENT_NAME_RE, - HERMES_AGENT_NAME_RE, - titleHasAnyLegacyAgentName -} from './agent-name-token-match' -import { - GEMINI_IDLE, - GEMINI_PERMISSION, - GEMINI_SILENT_WORKING, - GEMINI_WORKING, - isGeminiTerminalTitle, - isGrokRotatingWorkingTitle, - isPiAgentTitle -} from './terminal-title-agent-type' -import { - detectAgentStatusFromTitle, - STRONG_WORKING_KEYWORDS_RE_GLOBAL -} from './terminal-title-status' - -function containsAgentName(title: string): boolean { - return ( - titleHasAnyLegacyAgentName(title) || - AGY_AGENT_NAME_RE.test(title) || - DROID_AGENT_NAME_RE.test(title) || - HERMES_AGENT_NAME_RE.test(title) - ) -} - -/** - * Strip working-status indicators from a title so that - * `detectAgentStatusFromTitle` will no longer return 'working'. - * Used to clear stale titles when an agent exits without resetting its title. - */ -export function clearWorkingIndicators(title: string): string { - let cleaned = title - - // Gemini working symbols - cleaned = cleaned.replace(GEMINI_WORKING, '') - cleaned = cleaned.replace(GEMINI_SILENT_WORKING, '') - - // Braille spinner characters (U+2800–U+28FF) - // eslint-disable-next-line no-control-regex -- intentional unicode range - cleaned = cleaned.replace(/[\u2800-\u28FF]/g, '') - - // Claude Code ". " working prefix - if (cleaned.startsWith('. ')) { - cleaned = cleaned.slice(2) - } - - // Strip working keywords that detectAgentStatusFromTitle would pick up - // when the title also contains an agent name. - if (containsAgentName(cleaned)) { - cleaned = cleaned.replace(STRONG_WORKING_KEYWORDS_RE_GLOBAL, '') - } - - // Collapse whitespace after removals - cleaned = cleaned.replace(/\s{2,}/g, ' ').trim() - - return cleaned || title -} - -/** - * Normalize high-churn agent titles into stable display labels before storing - * them in app state. Gemini CLI can emit per-keystroke title updates, which - * otherwise causes broad rerenders and visible flashing. - */ -export function normalizeTerminalTitle(title: string): string { - if (!title) { - return title - } - - if (isGeminiTerminalTitle(title)) { - const status = detectAgentStatusFromTitle(title) - if (status === 'permission') { - return `${GEMINI_PERMISSION} Gemini CLI` - } - if (status === 'working') { - return `${GEMINI_WORKING} Gemini CLI` - } - if (status === 'idle') { - return `${GEMINI_IDLE} Gemini CLI` - } - } - - // Why: Pi's titlebar extension animates every 80ms with different braille - // frames. Collapsing those frames into one stable label avoids renderer - // churn while preserving the working/idle transition Orca keys off. - if (isPiAgentTitle(title)) { - const status = detectAgentStatusFromTitle(title) - if (status === 'working') { - return '\u280b Pi' - } - if (status === 'idle') { - return 'Pi' - } - } - - // Why: Grok Build interpolates a rotating status/tool phrase between the - // spinner and its name, so its working frames change the title many times per - // turn. Collapse them to one stable label; idle/session titles carry no - // spinner and pass through, so the meaningful final title still shows. - if (isGrokRotatingWorkingTitle(title)) { - return '\u280b Grok' - } - - return title -} diff --git a/src/shared/terminal-title-status.ts b/src/shared/terminal-title-status.ts deleted file mode 100644 index e0fe8312468..00000000000 --- a/src/shared/terminal-title-status.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { - AGY_AGENT_NAME_RE, - DROID_AGENT_NAME_RE, - HERMES_AGENT_NAME_RE, - titleHasAnyLegacyAgentName -} from './agent-name-token-match' -import { getPiCompatibleSyntheticAgentStatus } from './pi-compatible-synthetic-title' -import { - CLAUDE_IDLE, - containsBrailleSpinner, - GEMINI_IDLE, - GEMINI_PERMISSION, - GEMINI_SILENT_WORKING, - GEMINI_WORKING, - isClaudeManagementTitle, - isPiTerminalTitle -} from './terminal-title-agent-type' - -export type AgentStatus = 'working' | 'permission' | 'idle' - -// Idle-status keywords; `as const` gives consumers literal-union types. -const STRONG_IDLE_KEYWORDS = ['ready', 'idle', 'done'] as const - -// Working-status keywords, shared with `clearWorkingIndicators` so detection and stripping stay in lock-step. -const STRONG_WORKING_KEYWORDS = ['working', 'thinking', 'running'] as const - -// Why: `\b` fails since "-" is a non-word char (`\bready\b` matches "is-ready-cap"); lookarounds are asymmetric — reject path chars left, allow trailing punctuation ("Codex done.") right. -export const STRONG_IDLE_KEYWORDS_RE = new RegExp( - `(? lower.includes(word)) -} - -/** - * Tracks agent status transitions from terminal title changes. - * Fires `onBecameIdle` on working→idle/permission — the trigger for unread notifications. - */ -export function createAgentStatusTracker( - onBecameIdle: (title: string) => void, - onBecameWorking?: () => void, - onAgentExited?: () => void -): { - handleTitle: (title: string) => void - /** Clear status so a stale working→idle transition can't fire after teardown. */ - reset: () => void -} { - let lastStatus: AgentStatus | null = null - - return { - handleTitle(title: string): void { - const newStatus = detectAgentStatusFromTitle(title) - if (lastStatus === 'working' && newStatus !== null && newStatus !== 'working') { - onBecameIdle(title) - } - if (lastStatus !== 'working' && newStatus === 'working') { - onBecameWorking?.() - } - // Why: null title = reverted to a plain shell prompt (agent exited); skip when 'working' since active agents briefly flash shell titles. - if (lastStatus !== null && lastStatus !== 'working' && newStatus === null) { - lastStatus = null - onAgentExited?.() - } - if (newStatus !== null) { - lastStatus = newStatus - } - }, - reset(): void { - lastStatus = null - } - } -} - -// Why: cursor's native title is constant and carries no working/idle info; keep it a no-op so per-turn re-emissions can't stomp Orca-synthesized state. -const CURSOR_NATIVE_TITLE_LOWER = 'cursor agent' - -export function detectAgentStatusFromTitle(title: string): AgentStatus | null { - if (!title) { - return null - } - if (isClaudeManagementTitle(title)) { - return null - } - // Why: exact "Cursor Agent" is cursor's info-free native title; titles with extra tokens are Orca-synthesized and worth classifying. - if (title.trim().toLowerCase() === CURSOR_NATIVE_TITLE_LOWER) { - return null - } - - // Gemini CLI symbols are the most specific and should take precedence. - if (title.includes(GEMINI_PERMISSION)) { - return 'permission' - } - if (title.includes(GEMINI_WORKING) || title.includes(GEMINI_SILENT_WORKING)) { - return 'working' - } - if (title.includes(GEMINI_IDLE)) { - return 'idle' - } - - // Why: resolve synthetic Pi/OMP labels before the broader Pi/braille checks below. - const piCompatibleSyntheticAgentStatus = getPiCompatibleSyntheticAgentStatus(title) - if (piCompatibleSyntheticAgentStatus) { - return piCompatibleSyntheticAgentStatus - } - - // Claude Code uses ✳ idle prefix; check before braille/agent-name since the title is the task description, not "Claude Code". - if (title.startsWith(`${CLAUDE_IDLE} `) || title === CLAUDE_IDLE) { - return 'idle' - } - - if (isPiTerminalTitle(title)) { - return 'idle' - } - - if (containsBrailleSpinner(title)) { - return 'working' - } - - const hasDroidAgentName = DROID_AGENT_NAME_RE.test(title) - const hasHermesAgentName = HERMES_AGENT_NAME_RE.test(title) - const hasAgyAgentName = AGY_AGENT_NAME_RE.test(title) - const hasLegacyAgentName = titleHasAnyLegacyAgentName(title) - if (hasLegacyAgentName || hasDroidAgentName || hasHermesAgentName || hasAgyAgentName) { - if (containsAny(title, ['action required', 'permission', 'waiting'])) { - return 'permission' - } - // Why: boundary match (not substring) so "already" ⊃ "ready" isn't classified idle. See STRONG_IDLE_KEYWORDS_RE. - if (STRONG_IDLE_KEYWORDS_RE.test(title)) { - return 'idle' - } - // Why: false 'working' is worse than false 'idle' (drives active-agent UI); boundary match avoids "reworking" ⊃ "working". - if (STRONG_WORKING_KEYWORDS_RE.test(title)) { - return 'working' - } - - // Claude Code title prefixes: ". " = working, "* " = idle - if (title.startsWith('. ')) { - return 'working' - } - if (title.startsWith('* ')) { - return 'idle' - } - - // Why: Droid's hook events are authoritative; don't treat a name-only native title as a completion. - if (hasDroidAgentName && !hasLegacyAgentName) { - return null - } - - return 'idle' - } - - return null -} diff --git a/src/shared/work-items.ts b/src/shared/work-items.ts index 0a770058cc4..489fe372776 100644 --- a/src/shared/work-items.ts +++ b/src/shared/work-items.ts @@ -26,19 +26,10 @@ export function isGitHubWorkItemsSshRemoteRequiredError(error: unknown): boolean return message.includes(GITHUB_WORK_ITEMS_SSH_REMOTE_REQUIRED_MESSAGE) } -// Why: generic over item shape for the same cross-caller reasons as -// sortWorkItemsByUpdatedAt. Sorting by number descending matches GitHub's -// default Issues view (newest issue number first). +// Why: generic over item shape because main-process callers emit items without +// repoId (stamped by the renderer after IPC), while renderer callers carry the +// full GitHubWorkItem. Sorting by number descending matches GitHub's default +// Issues view (newest issue number first). export function sortWorkItemsByNumber(items: T[]): T[] { return [...items].sort((left, right) => right.number - left.number) } - -// Why: generic over the item shape because main-process callers emit items -// without repoId (stamped by the renderer after IPC), while renderer callers -// carry the full GitHubWorkItem. Both share only the updatedAt field needed -// here. -export function sortWorkItemsByUpdatedAt(items: T[]): T[] { - return [...items].sort((left, right) => { - return new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime() - }) -} diff --git a/src/shared/workspace-scope.ts b/src/shared/workspace-scope.ts index 6aed11b6e2d..4993c0599d7 100644 --- a/src/shared/workspace-scope.ts +++ b/src/shared/workspace-scope.ts @@ -8,12 +8,6 @@ export function folderWorkspaceKey(folderWorkspaceId: string): WorkspaceKey { return `folder:${folderWorkspaceId}` } -export function workspaceKeyFromScope(scope: WorkspaceScope): WorkspaceKey { - return scope.type === 'worktree' - ? worktreeWorkspaceKey(scope.worktreeId) - : folderWorkspaceKey(scope.folderWorkspaceId) -} - export function parseWorkspaceKey(value: string): WorkspaceScope | null { if (value.startsWith('worktree:')) { const worktreeId = value.slice('worktree:'.length) diff --git a/src/shared/wsl-hook-relay-contract.ts b/src/shared/wsl-hook-relay-contract.ts index ed6e147de18..87f099e6e0d 100644 --- a/src/shared/wsl-hook-relay-contract.ts +++ b/src/shared/wsl-hook-relay-contract.ts @@ -44,16 +44,6 @@ export const WSL_HOOK_FS_METHODS = { mkdir: 'wslfs.mkdir' } as const -/** Hard guest-side ceilings. Host requests may lower them but cannot raise them. */ -export const WSL_HOOK_FS_MAX_READ_BYTES = 64 * 1024 * 1024 -export const WSL_HOOK_FS_MAX_DIRECTORY_ENTRIES = 10_000 -export const WSL_HOOK_FS_MAX_DIRECTORY_RETAINED_BYTES = 2 * 1024 * 1024 - -export type WslHookFsDirectoryLimits = { - maxEntries: number - maxRetainedBytes: number -} - /** Result envelope for every fs-bridge method. Errors travel as data (not * JSON-RPC faults) so the host adapter can map POSIX errno onto the ssh2 * status codes the shared installer error-classifiers already understand. */