diff --git a/packages/@ant/computer-use-mcp/src/executor.ts b/packages/@ant/computer-use-mcp/src/executor.ts index a454631c3..5b070298b 100644 --- a/packages/@ant/computer-use-mcp/src/executor.ts +++ b/packages/@ant/computer-use-mcp/src/executor.ts @@ -5,6 +5,8 @@ export interface DisplayGeometry { scaleFactor: number originX: number originY: number + label?: string + isPrimary?: boolean } export interface ScreenshotResult { @@ -42,6 +44,7 @@ export interface ResolvePrepareCaptureResult extends ScreenshotResult { hidden: string[] activated?: string displayId: number + captureError?: string } export interface ComputerExecutorCapabilities { diff --git a/packages/@ant/computer-use-mcp/src/toolCalls.ts b/packages/@ant/computer-use-mcp/src/toolCalls.ts index f40302fcf..af20a3efd 100644 --- a/packages/@ant/computer-use-mcp/src/toolCalls.ts +++ b/packages/@ant/computer-use-mcp/src/toolCalls.ts @@ -88,6 +88,8 @@ export type CuErrorKind = | "state_conflict" // wrong state for action (call sequence, mouse already held) | "grant_flag_required" // action needs a grant flag (systemKeyCombos, clipboard*) from request_access | "display_error" // display enumeration failed (platform) + | "launch_failed" // failed to launch an external process (e.g. terminal) + | "element_not_found" // UI element not found (e.g. window, automation element) | "other"; /** @@ -906,9 +908,10 @@ async function handleRequestAccess( ); } + const perms = recheck as { granted: false; accessibility: boolean; screenRecording: boolean }; const missing: string[] = []; - if (!recheck.accessibility) missing.push("Accessibility"); - if (!recheck.screenRecording) missing.push("Screen Recording"); + if (!perms.accessibility) missing.push("Accessibility"); + if (!perms.screenRecording) missing.push("Screen Recording"); return errorResult( `macOS ${missing.join(" and ")} permission(s) not yet granted. ` + `The permission panel has been shown. Once the user grants the ` + @@ -1423,9 +1426,10 @@ async function handleRequestTeachAccess( ); } + const perms = recheck as { granted: false; accessibility: boolean; screenRecording: boolean }; const missing: string[] = []; - if (!recheck.accessibility) missing.push("Accessibility"); - if (!recheck.screenRecording) missing.push("Screen Recording"); + if (!perms.accessibility) missing.push("Accessibility"); + if (!perms.screenRecording) missing.push("Screen Recording"); return errorResult( `macOS ${missing.join(" and ")} permission(s) not yet granted. ` + `The permission panel has been shown. Once the user grants the ` + @@ -4082,8 +4086,8 @@ export async function handleToolCall( ); } tccState = { - accessibility: osPerms.accessibility, - screenRecording: osPerms.screenRecording, + accessibility: (osPerms as { granted: false; accessibility: boolean; screenRecording: boolean }).accessibility, + screenRecording: (osPerms as { granted: false; accessibility: boolean; screenRecording: boolean }).screenRecording, }; } diff --git a/packages/@ant/computer-use-swift/src/backends/darwin.ts b/packages/@ant/computer-use-swift/src/backends/darwin.ts index 6e3c24933..4bf6d5fa9 100644 --- a/packages/@ant/computer-use-swift/src/backends/darwin.ts +++ b/packages/@ant/computer-use-swift/src/backends/darwin.ts @@ -14,6 +14,17 @@ import type { SwiftBackend, WindowDisplayInfo, } from '../types.js' +export type { + DisplayGeometry, + PrepareDisplayResult, + AppInfo, + InstalledApp, + RunningApp, + ScreenshotResult, + ResolvePrepareCaptureResult, + WindowDisplayInfo, +} from '../types.js' + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- diff --git a/packages/@ant/computer-use-swift/src/types.ts b/packages/@ant/computer-use-swift/src/types.ts index 5dc199ecd..767a0fcde 100644 --- a/packages/@ant/computer-use-swift/src/types.ts +++ b/packages/@ant/computer-use-swift/src/types.ts @@ -3,6 +3,8 @@ export interface DisplayGeometry { height: number scaleFactor: number displayId: number + label?: string + isPrimary?: boolean } export interface PrepareDisplayResult { @@ -37,6 +39,9 @@ export interface ResolvePrepareCaptureResult { base64: string width: number height: number + captureError?: string + displayId?: number + hidden?: string[] } export interface WindowDisplayInfo { diff --git a/packages/@ant/ink/src/components/Box.tsx b/packages/@ant/ink/src/components/Box.tsx index d07a2dd3d..895f82578 100644 --- a/packages/@ant/ink/src/components/Box.tsx +++ b/packages/@ant/ink/src/components/Box.tsx @@ -92,14 +92,14 @@ function Box({ tabIndex={tabIndex} autoFocus={autoFocus} onClick={onClick} - onFocus={onFocus} - onFocusCapture={onFocusCapture} - onBlur={onBlur} - onBlurCapture={onBlurCapture} + onFocus={onFocus as unknown as (event: React.FocusEvent) => void} + onFocusCapture={onFocusCapture as unknown as (event: React.FocusEvent) => void} + onBlur={onBlur as unknown as (event: React.FocusEvent) => void} + onBlurCapture={onBlurCapture as unknown as (event: React.FocusEvent) => void} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} - onKeyDown={onKeyDown} - onKeyDownCapture={onKeyDownCapture} + onKeyDown={onKeyDown as unknown as (event: React.KeyboardEvent) => void} + onKeyDownCapture={onKeyDownCapture as unknown as (event: React.KeyboardEvent) => void} style={{ flexWrap, flexDirection, diff --git a/packages/@ant/ink/src/core/ink.tsx b/packages/@ant/ink/src/core/ink.tsx index f18f0ddec..12f0c92a5 100644 --- a/packages/@ant/ink/src/core/ink.tsx +++ b/packages/@ant/ink/src/core/ink.tsx @@ -352,8 +352,7 @@ export default class Ink { } } - // @ts-expect-error @types/react-reconciler@0.32.3 declares 11 args with transitionCallbacks, - // but react-reconciler 0.33.0 source only accepts 10 args (no transitionCallbacks) + // @ts-ignore createContainer arg count varies across react-reconciler versions this.container = reconciler.createContainer( this.rootNode, ConcurrentRoot, @@ -367,6 +366,7 @@ export default class Ink { noop, // onDefaultTransitionIndicator ) + // @ts-ignore MACRO-replaced comparison — always false in production builds if ("production" === 'development') { reconciler.injectIntoDevTools({ bundleType: 0, @@ -952,7 +952,7 @@ export default class Ink { pause(): void { // Flush pending React updates and render before pausing. - // @ts-expect-error flushSyncFromReconciler exists in react-reconciler 0.31 but not in @types/react-reconciler + // @ts-ignore flushSyncFromReconciler exists in react-reconciler but not in @types reconciler.flushSyncFromReconciler() this.onRender() @@ -1701,9 +1701,9 @@ export default class Ink { ) - // @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler + // @ts-ignore updateContainerSync exists in react-reconciler but not in @types reconciler.updateContainerSync(tree, this.container, null, noop) - // @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler + // @ts-ignore flushSyncWork exists in react-reconciler but not in @types reconciler.flushSyncWork() } @@ -1773,9 +1773,9 @@ export default class Ink { this.drainTimer = null } - // @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler + // @ts-ignore updateContainerSync exists in react-reconciler but not in @types reconciler.updateContainerSync(null, this.container, null, noop) - // @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler + // @ts-ignore flushSyncWork exists in react-reconciler but not in @types reconciler.flushSyncWork() instances.delete(this.options.stdout) diff --git a/packages/@ant/ink/src/hooks/useSearchInput.ts b/packages/@ant/ink/src/hooks/useSearchInput.ts index 9935256c7..943d0a8a7 100644 --- a/packages/@ant/ink/src/hooks/useSearchInput.ts +++ b/packages/@ant/ink/src/hooks/useSearchInput.ts @@ -7,7 +7,9 @@ */ import { useCallback, useState } from 'react' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' +import { KeyboardEvent } from '../core/events/keyboard-event.js' +import type { Key, InputEvent } from '../core/events/input-event.js' +import type { ParsedKey } from '../core/parse-keypress.js' import useInput from './use-input.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' @@ -212,8 +214,8 @@ export function useSearchInput({ // Bridge: subscribe via useInput and adapt to KeyboardEvent useInput( - (_input: string, _key: unknown, event: { keypress: string }) => { - handleKeyDown(new KeyboardEvent(event.keypress)) + (_input: string, _key: Key, event: InputEvent) => { + handleKeyDown(new KeyboardEvent(event.keypress as ParsedKey)) }, { isActive }, ) diff --git a/packages/@ant/ink/src/keybindings/KeybindingSetup.tsx b/packages/@ant/ink/src/keybindings/KeybindingSetup.tsx index 36acd9154..91c047de8 100644 --- a/packages/@ant/ink/src/keybindings/KeybindingSetup.tsx +++ b/packages/@ant/ink/src/keybindings/KeybindingSetup.tsx @@ -10,7 +10,8 @@ import type { InputEvent } from '../core/events/input-event.js' // ChordInterceptor intentionally uses useInput to intercept all keystrokes before // other handlers process them - this is required for chord sequence support // eslint-disable-next-line custom-rules/prefer-use-keybindings -import useInput, { type Key } from '../hooks/use-input.js' +import useInput from '../hooks/use-input.js' +import type { Key } from '../core/events/input-event.js' import { KeybindingProvider } from './KeybindingContext.js' import { resolveKeyWithChordState } from './resolver.js' import type { diff --git a/packages/@ant/ink/utils/systemThemeWatcher.ts b/packages/@ant/ink/utils/systemThemeWatcher.ts new file mode 100644 index 000000000..460cde765 --- /dev/null +++ b/packages/@ant/ink/utils/systemThemeWatcher.ts @@ -0,0 +1,12 @@ +import type { SystemTheme } from '../src/theme/systemTheme.js' + +/** + * Watch for live terminal theme changes via OSC 11 polling. + * Stub implementation for the standalone @anthropic/ink package. + */ +export function watchSystemTheme( + _querier: unknown, + _setTheme: React.Dispatch>, +): () => void { + return () => {} +} diff --git a/src/cli/handlers/mcp.tsx b/src/cli/handlers/mcp.tsx index b9c030da9..ab543af11 100644 --- a/src/cli/handlers/mcp.tsx +++ b/src/cli/handlers/mcp.tsx @@ -222,9 +222,10 @@ export async function mcpListHandler(): Promise { // biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${name}: ${server.url} - ${status}`) } else if (!server.type || server.type === 'stdio') { - const args = Array.isArray(server.args) ? server.args : [] + const stdioServer = server as { command: string; args: string[]; type?: string } + const args = Array.isArray(stdioServer.args) ? stdioServer.args : [] // biome-ignore lint/suspicious/noConsole:: intentional console output - console.log(`${name}: ${server.command} ${args.join(' ')} - ${status}`) + console.log(`${name}: ${stdioServer.command} ${args.join(' ')} - ${status}`) } } } diff --git a/src/cli/transports/SSETransport.ts b/src/cli/transports/SSETransport.ts index fd5b11f53..ca9c396da 100644 --- a/src/cli/transports/SSETransport.ts +++ b/src/cli/transports/SSETransport.ts @@ -192,6 +192,7 @@ export class SSETransport implements Transport { // Liveness detection private livenessTimer: NodeJS.Timeout | null = null + private lastActivityTime = 0 // POST URL (derived from SSE URL) private postUrl: string diff --git a/src/cli/transports/ccrClient.ts b/src/cli/transports/ccrClient.ts index 3e40ae1bb..b24256089 100644 --- a/src/cli/transports/ccrClient.ts +++ b/src/cli/transports/ccrClient.ts @@ -119,7 +119,7 @@ export function createStreamAccumulator(): StreamAccumulatorState { function scopeKey(m: { session_id: string - parent_tool_use_id: string | null + parent_tool_use_id?: string | null }): string { return `${m.session_id}:${m.parent_tool_use_id ?? ''}` } @@ -148,9 +148,10 @@ export function accumulateStreamEvents( // rewrite the same entry instead of emitting one event per delta. const touched = new Map() for (const msg of buffer) { - switch (msg.event.type) { + const evt = msg.event as Record + switch (evt.type) { case 'message_start': { - const id = msg.event.message.id + const id = (evt.message as { id: string }).id const prevId = state.scopeToMessage.get(scopeKey(msg)) if (prevId) state.byMessage.delete(prevId) state.scopeToMessage.set(scopeKey(msg), id) @@ -159,7 +160,8 @@ export function accumulateStreamEvents( break } case 'content_block_delta': { - if (msg.event.delta.type !== 'text_delta') { + const delta = evt.delta as Record + if (delta.type !== 'text_delta') { out.push(msg) break } @@ -173,11 +175,12 @@ export function accumulateStreamEvents( out.push(msg) break } - const chunks = (blocks[msg.event.index] ??= []) - chunks.push(msg.event.delta.text) + const idx = evt.index as number + const chunks = (blocks[idx] ??= []) + chunks.push(delta.text as string) const existing = touched.get(chunks) if (existing) { - existing.event.delta.text = chunks.join('') + ;(existing.event as Record).delta = { type: 'text_delta', text: chunks.join('') } break } const snapshot: CoalescedStreamEvent = { @@ -187,7 +190,7 @@ export function accumulateStreamEvents( parent_tool_use_id: msg.parent_tool_use_id, event: { type: 'content_block_delta', - index: msg.event.index, + index: idx, delta: { type: 'text_delta', text: chunks.join('') }, }, } @@ -745,7 +748,7 @@ export class CCRClient { } await this.flushStreamEventBuffer() if (message.type === 'assistant') { - clearStreamAccumulatorForMessage(this.streamTextAccumulator, message) + clearStreamAccumulatorForMessage(this.streamTextAccumulator, message as { session_id: string; parent_tool_use_id: string | null; message: { id: string } }) } await this.eventUploader.enqueue(this.toClientEvent(message)) } diff --git a/src/commands/chrome/chrome.tsx b/src/commands/chrome/chrome.tsx index 1fe1b1470..83f34c33d 100644 --- a/src/commands/chrome/chrome.tsx +++ b/src/commands/chrome/chrome.tsx @@ -136,7 +136,7 @@ function ClaudeInChromeMenu({ ) const isDisabled = - isWSL || ("external" !== 'ant' && !isClaudeAISubscriber) + isWSL || ((process.env.USER_TYPE as string) !== 'ant' && !isClaudeAISubscriber) return ( Claude in Chrome requires a claude.ai subscription. diff --git a/src/commands/install-github-app/OAuthFlowStep.tsx b/src/commands/install-github-app/OAuthFlowStep.tsx index b13493f18..f207b00ea 100644 --- a/src/commands/install-github-app/OAuthFlowStep.tsx +++ b/src/commands/install-github-app/OAuthFlowStep.tsx @@ -127,7 +127,7 @@ export function OAuthFlowStep({ setOAuthStatus({ state: 'success', token: accessToken }) // Auto-continue after brief delay to show success const timer2 = setTimeout(onSuccess, 1000, accessToken) - timersRef.current.add(timer2) + timersRef.current.add(timer2 as unknown as NodeJS.Timeout) }, 100, setOAuthStatus, diff --git a/src/commands/plugin/BrowseMarketplace.tsx b/src/commands/plugin/BrowseMarketplace.tsx index 8e3205f5e..0f1653538 100644 --- a/src/commands/plugin/BrowseMarketplace.tsx +++ b/src/commands/plugin/BrowseMarketplace.tsx @@ -177,7 +177,7 @@ export function BrowseMarketplace({ // Count how many plugins from this marketplace are installed const installedFromThisMarketplace = count( marketplace.plugins, - plugin => isPluginInstalled(createPluginId(plugin.name, name)), + plugin => isPluginInstalled(createPluginId((plugin as { name: string }).name, name)), ) marketplaceInfos.push({ @@ -409,7 +409,7 @@ export function BrowseMarketplace({ failureCount++ newFailedPlugins.push({ name: plugin.entry.name, - reason: result.error, + reason: (result as { success: false; error: string }).error, }) } } @@ -484,7 +484,7 @@ export function BrowseMarketplace({ setParentViewState({ type: 'menu' }) } else { setIsInstalling(false) - setInstallError(result.error) + setInstallError((result as { success: false; error: string }).error) } } diff --git a/src/commands/plugin/DiscoverPlugins.tsx b/src/commands/plugin/DiscoverPlugins.tsx index 4c3435de1..53cb0466f 100644 --- a/src/commands/plugin/DiscoverPlugins.tsx +++ b/src/commands/plugin/DiscoverPlugins.tsx @@ -305,7 +305,7 @@ export function DiscoverPlugins({ failureCount++ newFailedPlugins.push({ name: plugin.entry.name, - reason: result.error, + reason: (result as { success: false; error: string }).error, }) } } @@ -374,7 +374,7 @@ export function DiscoverPlugins({ setParentViewState({ type: 'menu' }) } else { setIsInstalling(false) - setInstallError(result.error) + setInstallError((result as { success: false; error: string }).error) } } diff --git a/src/commands/plugin/PluginSettings.tsx b/src/commands/plugin/PluginSettings.tsx index 444bf9761..e3a798e8a 100644 --- a/src/commands/plugin/PluginSettings.tsx +++ b/src/commands/plugin/PluginSettings.tsx @@ -66,7 +66,7 @@ function MarketplaceList({ } function McpRedirectBanner(): React.ReactNode { - if ("external" !== 'ant') { + if ((process.env.USER_TYPE as string) !== 'ant') { return null } diff --git a/src/commands/remote-setup/remote-setup.tsx b/src/commands/remote-setup/remote-setup.tsx index e511f064b..3a68f2908 100644 --- a/src/commands/remote-setup/remote-setup.tsx +++ b/src/commands/remote-setup/remote-setup.tsx @@ -118,11 +118,12 @@ function Web({ onDone }: { onDone: LocalJSXCommandOnDone }) { const result = await importGithubToken(token) if (!result.ok) { + const err = (result as { ok: false; error: ImportTokenError }).error logEvent('tengu_remote_setup_result', { result: 'import_failed' as SafeString, - error_kind: result.error.kind as SafeString, + error_kind: err.kind as SafeString, }) - onDone(errorMessage(result.error, getCodeWebUrl())) + onDone(errorMessage(err, getCodeWebUrl())) return } diff --git a/src/commands/resume/resume.tsx b/src/commands/resume/resume.tsx index 795c05077..dbfb7f96a 100644 --- a/src/commands/resume/resume.tsx +++ b/src/commands/resume/resume.tsx @@ -152,7 +152,7 @@ function ResumeCommand({ } // Different project - show command instead of resuming - const raw = await setClipboard(crossProjectCheck.command) + const raw = await setClipboard((crossProjectCheck as { command: string }).command) if (raw) process.stdout.write(raw) // Format the output message @@ -161,7 +161,7 @@ function ResumeCommand({ 'This conversation is from a different directory.', '', 'To resume, run:', - ` ${crossProjectCheck.command}`, + ` ${(crossProjectCheck as { command: string }).command}`, '', '(Command copied to clipboard)', '', diff --git a/src/commands/ultraplan.tsx b/src/commands/ultraplan.tsx index b15d61d71..f794f01e0 100644 --- a/src/commands/ultraplan.tsx +++ b/src/commands/ultraplan.tsx @@ -335,11 +335,11 @@ async function launchDetached(opts: { if (!eligibility.eligible) { logEvent('tengu_ultraplan_create_failed', { reason: 'precondition' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, - precondition_errors: eligibility.errors + precondition_errors: (eligibility as { errors: Array<{ type: string }> }).errors .map(e => e.type) .join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, }); - const reasons = eligibility.errors.map(formatPreconditionError).join('\n'); + const reasons = (eligibility as { errors: Array<{ type: string }> }).errors.map(formatPreconditionError).join('\n'); enqueuePendingNotification({ value: `ultraplan: cannot launch remote session —\n${reasons}`, mode: 'task-notification', diff --git a/src/components/AutoModeOptInDialog.tsx b/src/components/AutoModeOptInDialog.tsx index a6d7f6a12..95c7eaac3 100644 --- a/src/components/AutoModeOptInDialog.tsx +++ b/src/components/AutoModeOptInDialog.tsx @@ -61,7 +61,7 @@ export function AutoModeOptInDialog({