@@ -97,8 +97,8 @@ import { logError } from '../utils/log.js';
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
const useVoiceIntegration : typeof import ( '../hooks/useVoiceIntegration.js' ) . useVoiceIntegration = feature ( 'VOICE_MODE' ) ? require ( '../hooks/useVoiceIntegration.js' ) . useVoiceIntegration : ( ) = > ( {
const useVoiceIntegration : typeof import ( '../hooks/useVoiceIntegration.js' ) . useVoiceIntegration = feature ( 'VOICE_MODE' ) ? require ( '../hooks/useVoiceIntegration.js' ) . useVoiceIntegration : ( ) = > ( {
stripTrailing : ( ) = > 0 ,
stripTrailing : ( ) = > 0 ,
handleKeyEvent : ( ) = > { } ,
handleKeyEvent : ( ) = > { } ,
resetAnchor : ( ) = > { }
resetAnchor : ( ) = > { }
} ) ;
} ) ;
const VoiceKeybindingHandler : typeof import ( '../hooks/useVoiceIntegration.js' ) . VoiceKeybindingHandler = feature ( 'VOICE_MODE' ) ? require ( '../hooks/useVoiceIntegration.js' ) . VoiceKeybindingHandler : ( ) = > null ;
const VoiceKeybindingHandler : typeof import ( '../hooks/useVoiceIntegration.js' ) . VoiceKeybindingHandler = feature ( 'VOICE_MODE' ) ? require ( '../hooks/useVoiceIntegration.js' ) . VoiceKeybindingHandler : ( ) = > null ;
// Frustration detection is ant-only (dogfooding). Conditional require so external
// Frustration detection is ant-only (dogfooding). Conditional require so external
@@ -106,11 +106,11 @@ const VoiceKeybindingHandler: typeof import('../hooks/useVoiceIntegration.js').V
// on every messages change, plus the GrowthBook fetch).
// on every messages change, plus the GrowthBook fetch).
const useFrustrationDetection : typeof import ( '../components/FeedbackSurvey/useFrustrationDetection.js' ) . useFrustrationDetection = "external" === 'ant' ? require ( '../components/FeedbackSurvey/useFrustrationDetection.js' ) . useFrustrationDetection : ( ) = > ( {
const useFrustrationDetection : typeof import ( '../components/FeedbackSurvey/useFrustrationDetection.js' ) . useFrustrationDetection = "external" === 'ant' ? require ( '../components/FeedbackSurvey/useFrustrationDetection.js' ) . useFrustrationDetection : ( ) = > ( {
state : 'closed' ,
state : 'closed' ,
handleTranscriptSelect : ( ) = > { }
handleTranscriptSelect : ( ) = > { }
} ) ;
} ) ;
// Ant-only org warning. Conditional require so the org UUID list is
// Ant-only org warning. Conditional require so the org UUID list is
// eliminated from external builds (one UUID is on excluded-strings).
// eliminated from external builds (one UUID is on excluded-strings).
const useAntOrgWarningNotification : typeof import ( '../hooks/notifs/useAntOrgWarningNotification.js' ) . useAntOrgWarningNotification = "external" === 'ant' ? require ( '../hooks/notifs/useAntOrgWarningNotification.js' ) . useAntOrgWarningNotification : ( ) = > { } ;
const useAntOrgWarningNotification : typeof import ( '../hooks/notifs/useAntOrgWarningNotification.js' ) . useAntOrgWarningNotification = "external" === 'ant' ? require ( '../hooks/notifs/useAntOrgWarningNotification.js' ) . useAntOrgWarningNotification : ( ) = > { } ;
// Dead code elimination: conditional import for coordinator mode
// Dead code elimination: conditional import for coordinator mode
const getCoordinatorUserContext : ( mcpClients : ReadonlyArray < {
const getCoordinatorUserContext : ( mcpClients : ReadonlyArray < {
name : string ;
name : string ;
@@ -192,7 +192,7 @@ import { useInboxPoller } from '../hooks/useInboxPoller.js';
// Dead code elimination: conditional import for loop mode
// Dead code elimination: conditional import for loop mode
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable @typescript-eslint/no-require-imports */
const proactiveModule = feature ( 'PROACTIVE' ) || feature ( 'KAIROS' ) ? require ( '../proactive/index.js' ) : null ;
const proactiveModule = feature ( 'PROACTIVE' ) || feature ( 'KAIROS' ) ? require ( '../proactive/index.js' ) : null ;
const PROACTIVE_NO_OP_SUBSCRIBE = ( _cb : ( ) = > void ) = > ( ) = > { } ;
const PROACTIVE_NO_OP_SUBSCRIBE = ( _cb : ( ) = > void ) = > ( ) = > { } ;
const PROACTIVE_FALSE = ( ) = > false ;
const PROACTIVE_FALSE = ( ) = > false ;
const SUGGEST_BG_PR_NOOP = ( _p : string , _n : string ) : boolean = > false ;
const SUGGEST_BG_PR_NOOP = ( _p : string , _n : string ) : boolean = > false ;
const useProactive = feature ( 'PROACTIVE' ) || feature ( 'KAIROS' ) ? require ( '../proactive/useProactive.js' ) . useProactive : null ;
const useProactive = feature ( 'PROACTIVE' ) || feature ( 'KAIROS' ) ? require ( '../proactive/useProactive.js' ) . useProactive : null ;
@@ -297,7 +297,7 @@ const EMPTY_MCP_CLIENTS: MCPServerConnection[] = [];
// Stable stub for useAssistantHistory's non-KAIROS branch — avoids a new
// Stable stub for useAssistantHistory's non-KAIROS branch — avoids a new
// function identity each render, which would break composedOnScroll's memo.
// function identity each render, which would break composedOnScroll's memo.
const HISTORY_STUB = {
const HISTORY_STUB = {
maybeLoadOlder : ( _ : ScrollBoxHandle ) = > { }
maybeLoadOlder : ( _ : ScrollBoxHandle ) = > { }
} ;
} ;
// Window after a user-initiated scroll during which type-into-empty does NOT
// Window after a user-initiated scroll during which type-into-empty does NOT
// repin to bottom. Josh Rosen's workflow: Claude emits long output → scroll
// repin to bottom. Josh Rosen's workflow: Claude emits long output → scroll
@@ -448,28 +448,28 @@ function TranscriptSearchBar({
const off = cursorOffset ;
const off = cursorOffset ;
const cursorChar = off < query . length ? query [ off ] : ' ' ;
const cursorChar = off < query . length ? query [ off ] : ' ' ;
return < Box borderTopDimColor borderBottom = { false } borderLeft = { false } borderRight = { false } borderStyle = "single" marginTop = { 1 } paddingLeft = { 2 } width = "100%"
return < Box borderTopDimColor borderBottom = { false } borderLeft = { false } borderRight = { false } borderStyle = "single" marginTop = { 1 } paddingLeft = { 2 } width = "100%"
// applySearchHighlight scans the whole screen buffer. The query
// applySearchHighlight scans the whole screen buffer. The query
// text rendered here IS on screen — /foo matches its own 'foo' in
// text rendered here IS on screen — /foo matches its own 'foo' in
// the bar. With no content matches that's the ONLY visible match →
// the bar. With no content matches that's the ONLY visible match →
// gets CURRENT → underlined. noSelect makes searchHighlight.ts:76
// gets CURRENT → underlined. noSelect makes searchHighlight.ts:76
// skip these cells (same exclusion as gutters). You can't text-
// skip these cells (same exclusion as gutters). You can't text-
// select the bar either; it's transient chrome, fine.
// select the bar either; it's transient chrome, fine.
noSelect >
noSelect >
< Text > / < / Text >
< Text > / < / Text >
< Text > { query . slice ( 0 , off ) } < / Text >
< Text > { query . slice ( 0 , off ) } < / Text >
< Text inverse > { cursorChar } < / Text >
< Text inverse > { cursorChar } < / Text >
{ off < query . length && < Text > { query . slice ( off + 1 ) } < / Text > }
{ off < query . length && < Text > { query . slice ( off + 1 ) } < / Text > }
< Box flexGrow = { 1 } / >
< Box flexGrow = { 1 } / >
{ indexStatus === 'building' ? < Text dimColor > indexing … < / Text > : indexStatus ? < Text dimColor > indexed in { indexStatus . ms } ms < / Text > : count === 0 && query ? < Text color = "error" > no matches < / Text > : count > 0 ?
{ indexStatus === 'building' ? < Text dimColor > indexing … < / Text > : indexStatus ? < Text dimColor > indexed in { indexStatus . ms } ms < / Text > : count === 0 && query ? < Text color = "error" > no matches < / Text > : count > 0 ?
// Engine-counted (indexOf on extractSearchText). May drift from
// Engine-counted (indexOf on extractSearchText). May drift from
// render-count for ghost/phantom messages — badge is a rough
// render-count for ghost/phantom messages — badge is a rough
// location hint. scanElement gives exact per-message positions
// location hint. scanElement gives exact per-message positions
// but counting ALL would cost ~1-3ms × matched-messages.
// but counting ALL would cost ~1-3ms × matched-messages.
< Text dimColor >
< Text dimColor >
{ current } / { count }
{ current } / { count }
{ ' ' }
{ ' ' }
< / Text > : null }
< / Text > : null }
< / Box > ;
< / Box > ;
}
}
const TITLE_ANIMATION_FRAMES = [ '⠂' , '⠐' ] ;
const TITLE_ANIMATION_FRAMES = [ '⠂' , '⠐' ] ;
const TITLE_STATIC_PREFIX = '✳' ;
const TITLE_STATIC_PREFIX = '✳' ;
@@ -605,8 +605,8 @@ export function REPL({
const moreRightEnabled = useMemo ( ( ) = > "external" === 'ant' && isEnvTruthy ( process . env . CLAUDE_MORERIGHT ) , [ ] ) ;
const moreRightEnabled = useMemo ( ( ) = > "external" === 'ant' && isEnvTruthy ( process . env . CLAUDE_MORERIGHT ) , [ ] ) ;
const disableVirtualScroll = useMemo ( ( ) = > isEnvTruthy ( process . env . CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL ) , [ ] ) ;
const disableVirtualScroll = useMemo ( ( ) = > isEnvTruthy ( process . env . CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL ) , [ ] ) ;
const disableMessageActions = feature ( 'MESSAGE_ACTIONS' ) ?
const disableMessageActions = feature ( 'MESSAGE_ACTIONS' ) ?
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
useMemo ( ( ) = > isEnvTruthy ( process . env . CLAUDE_CODE_DISABLE_MESSAGE_ACTIONS ) , [ ] ) : false ;
useMemo ( ( ) = > isEnvTruthy ( process . env . CLAUDE_CODE_DISABLE_MESSAGE_ACTIONS ) , [ ] ) : false ;
// Agent definition is state so /resume can update it mid-session
// Agent definition is state so /resume can update it mid-session
const [ mainThreadAgentDefinition , setMainThreadAgentDefinition ] = useState ( initialMainThreadAgentDefinition ) ;
const [ mainThreadAgentDefinition , setMainThreadAgentDefinition ] = useState ( initialMainThreadAgentDefinition ) ;
@@ -865,11 +865,11 @@ export function REPL({
// Ref for the bridge result callback — set after useReplBridge initializes,
// Ref for the bridge result callback — set after useReplBridge initializes,
// read in the onQuery finally block to notify mobile clients that a turn ended.
// read in the onQuery finally block to notify mobile clients that a turn ended.
const sendBridgeResultRef = useRef < ( ) = > void > ( ( ) = > { } ) ;
const sendBridgeResultRef = useRef < ( ) = > void > ( ( ) = > { } ) ;
// Ref for the synchronous restore callback — set after restoreMessageSync is
// Ref for the synchronous restore callback — set after restoreMessageSync is
// defined, read in the onQuery finally block for auto-restore on interrupt.
// defined, read in the onQuery finally block for auto-restore on interrupt.
const restoreMessageSyncRef = useRef < ( m : UserMessage ) = > void > ( ( ) = > { } ) ;
const restoreMessageSyncRef = useRef < ( m : UserMessage ) = > void > ( ( ) = > { } ) ;
// Ref to the fullscreen layout's scroll box for keyboard scrolling.
// Ref to the fullscreen layout's scroll box for keyboard scrolling.
// Null when fullscreen mode is disabled (ref never attached).
// Null when fullscreen mode is disabled (ref never attached).
@@ -1127,7 +1127,7 @@ export function REPL({
// session from mid-conversation context.
// session from mid-conversation context.
const haikuTitleAttemptedRef = useRef ( ( initialMessages ? . length ? ? 0 ) > 0 ) ;
const haikuTitleAttemptedRef = useRef ( ( initialMessages ? . length ? ? 0 ) > 0 ) ;
const agentTitle = mainThreadAgentDefinition ? . agentType ;
const agentTitle = mainThreadAgentDefinition ? . agentType ;
const terminalTitle = sessionTitle ? ? agentTitle ? ? haikuTitle ? ? 'Claude Co de' ;
const terminalTitle = sessionTitle ? ? agentTitle ? ? haikuTitle ? ? 'Open Clau de' ;
const isWaitingForApproval = toolUseConfirmQueue . length > 0 || promptQueue . length > 0 || pendingWorkerRequest || pendingSandboxRequest ;
const isWaitingForApproval = toolUseConfirmQueue . length > 0 || promptQueue . length > 0 || pendingWorkerRequest || pendingSandboxRequest ;
// Local-jsx commands (like /plugin, /config) show user-facing dialogs that
// Local-jsx commands (like /plugin, /config) show user-facing dialogs that
// wait for input. Require jsx != null — if the flag is stuck true but jsx
// wait for input. Require jsx != null — if the flag is stuck true but jsx
@@ -1246,8 +1246,8 @@ export function REPL({
const cursorNavRef = useRef < MessageActionsNav | null > ( null ) ;
const cursorNavRef = useRef < MessageActionsNav | null > ( null ) ;
// Memoized so Messages' React.memo holds.
// Memoized so Messages' React.memo holds.
const unseenDivider = useMemo ( ( ) = > computeUnseenDivider ( messages , dividerIndex ) ,
const unseenDivider = useMemo ( ( ) = > computeUnseenDivider ( messages , dividerIndex ) ,
// eslint-disable-next-line react-hooks/exhaustive-deps -- length change covers appends; useUnseenDivider's count-drop guard clears dividerIndex on replace/rewind
// eslint-disable-next-line react-hooks/exhaustive-deps -- length change covers appends; useUnseenDivider's count-drop guard clears dividerIndex on replace/rewind
[ dividerIndex , messages . length ] ) ;
[ dividerIndex , messages . length ] ) ;
// Re-pin scroll to bottom and clear the unseen-messages baseline. Called
// Re-pin scroll to bottom and clear the unseen-messages baseline. Called
// on any user-driven return-to-live action (submit, type-into-empty,
// on any user-driven return-to-live action (submit, type-into-empty,
// overlay appear/dismiss).
// overlay appear/dismiss).
@@ -1276,13 +1276,13 @@ export function REPL({
const {
const {
maybeLoadOlder
maybeLoadOlder
} = feature ( 'KAIROS' ) ?
} = feature ( 'KAIROS' ) ?
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
useAssistantHistory ( {
useAssistantHistory ( {
config : remoteSessionConfig ,
config : remoteSessionConfig ,
setMessages ,
setMessages ,
scrollRef ,
scrollRef ,
onPrepend : shiftDivider
onPrepend : shiftDivider
} ) : HISTORY_STUB ;
} ) : HISTORY_STUB ;
// Compose useUnseenDivider's callbacks with the lazy-load trigger.
// Compose useUnseenDivider's callbacks with the lazy-load trigger.
const composedOnScroll = useCallback ( ( sticky : boolean , handle : ScrollBoxHandle ) = > {
const composedOnScroll = useCallback ( ( sticky : boolean , handle : ScrollBoxHandle ) = > {
lastUserScrollTsRef . current = Date . now ( ) ;
lastUserScrollTsRef . current = Date . now ( ) ;
@@ -1593,12 +1593,12 @@ export function REPL({
swarmStartTimeRef . current = null ;
swarmStartTimeRef . current = null ;
swarmBudgetInfoRef . current = undefined ;
swarmBudgetInfoRef . current = undefined ;
setMessages ( prev = > [ . . . prev , createTurnDurationMessage ( totalMs , deferredBudget ,
setMessages ( prev = > [ . . . prev , createTurnDurationMessage ( totalMs , deferredBudget ,
// Count only what recordTranscript will persist — ephemeral
// Count only what recordTranscript will persist — ephemeral
// progress ticks and non-ant attachments are filtered by
// progress ticks and non-ant attachments are filtered by
// isLoggableMessage and never reach disk. Using raw prev.length
// isLoggableMessage and never reach disk. Using raw prev.length
// would make checkResumeConsistency report false delta<0 for
// would make checkResumeConsistency report false delta<0 for
// every turn that ran a progress-emitting tool.
// every turn that ran a progress-emitting tool.
count ( prev , isLoggableMessage ) ) ] ) ;
count ( prev , isLoggableMessage ) ) ] ) ;
}
}
} , [ hasRunningTeammates , setMessages ] ) ;
} , [ hasRunningTeammates , setMessages ] ) ;
@@ -1665,19 +1665,19 @@ export function REPL({
setToolJSX
setToolJSX
} ) ;
} ) ;
const showSpinner = ( ! toolJSX || toolJSX . showSpinner === true ) && toolUseConfirmQueue . length === 0 && promptQueue . length === 0 && (
const showSpinner = ( ! toolJSX || toolJSX . showSpinner === true ) && toolUseConfirmQueue . length === 0 && promptQueue . length === 0 && (
// Show spinner during input processing, API call, while teammates are running,
// Show spinner during input processing, API call, while teammates are running,
// or while pending task notifications are queued (prevents spinner bounce between consecutive notifications)
// or while pending task notifications are queued (prevents spinner bounce between consecutive notifications)
isLoading || userInputOnProcessing || hasRunningTeammates ||
isLoading || userInputOnProcessing || hasRunningTeammates ||
// Keep spinner visible while task notifications are queued for processing.
// Keep spinner visible while task notifications are queued for processing.
// Without this, the spinner briefly disappears between consecutive notifications
// Without this, the spinner briefly disappears between consecutive notifications
// (e.g., multiple background agents completing in rapid succession) because
// (e.g., multiple background agents completing in rapid succession) because
// isLoading goes false momentarily between processing each one.
// isLoading goes false momentarily between processing each one.
getCommandQueueLength ( ) > 0 ) &&
getCommandQueueLength ( ) > 0 ) &&
// Hide spinner when waiting for leader to approve permission request
// Hide spinner when waiting for leader to approve permission request
! pendingWorkerRequest && ! onlySleepToolActive && (
! pendingWorkerRequest && ! onlySleepToolActive && (
// Hide spinner when streaming text is visible (the text IS the feedback),
// Hide spinner when streaming text is visible (the text IS the feedback),
// but keep it when isBriefOnly suppresses the streaming text display
// but keep it when isBriefOnly suppresses the streaming text display
! visibleStreamingText || isBriefOnly ) ;
! visibleStreamingText || isBriefOnly ) ;
// Check if any permission or ask question prompt is currently visible
// Check if any permission or ask question prompt is currently visible
// This is used to prevent the survey from opening while prompts are active
// This is used to prevent the survey from opening while prompts are active
@@ -2323,9 +2323,9 @@ export function REPL({
addNotification ( {
addNotification ( {
key : 'sandbox-unavailable' ,
key : 'sandbox-unavailable' ,
jsx : < >
jsx : < >
< Text color = "warning" > sandbox disabled < / Text >
< Text color = "warning" > sandbox disabled < / Text >
< Text dimColor > · / sandbox < / Text >
< Text dimColor > · / sandbox < / Text >
< / > ,
< / > ,
priority : 'medium'
priority : 'medium'
} ) ;
} ) ;
} , [ addNotification ] ) ;
} , [ addNotification ] ) ;
@@ -2676,7 +2676,7 @@ export function REPL({
// useDeferredHookMessages) and attachment messages (appended by
// useDeferredHookMessages) and attachment messages (appended by
// processTextPrompt) — both pushed length past 1 on turn one, so the
// processTextPrompt) — both pushed length past 1 on turn one, so the
// title silently fell through to the "Claude Code" default.
// title silently fell through to the "Claude Code" default.
if ( getAPIProvider ( ) === 'firstParty' && ! titleDisabled && ! sessionTitle && ! agentTitle && ! haikuTitleAttemptedRef . current ) {
if ( ! titleDisabled && ! sessionTitle && ! agentTitle && ! haikuTitleAttemptedRef . current ) {
const firstUserMessage = newMessages . find ( m = > m . type === 'user' && ! m . isMeta ) ;
const firstUserMessage = newMessages . find ( m = > m . type === 'user' && ! m . isMeta ) ;
const text = firstUserMessage ? . type === 'user' ? getContentText ( firstUserMessage . message . content ) : null ;
const text = firstUserMessage ? . type === 'user' ? getContentText ( firstUserMessage . message . content ) : null ;
// Skip synthetic breadcrumbs — slash-command output, prompt-skill
// Skip synthetic breadcrumbs — slash-command output, prompt-skill
@@ -2686,7 +2686,7 @@ export function REPL({
if ( text && ! text . startsWith ( ` < ${ LOCAL_COMMAND_STDOUT_TAG } > ` ) && ! text . startsWith ( ` < ${ COMMAND_MESSAGE_TAG } > ` ) && ! text . startsWith ( ` < ${ COMMAND_NAME_TAG } > ` ) && ! text . startsWith ( ` < ${ BASH_INPUT_TAG } > ` ) ) {
if ( text && ! text . startsWith ( ` < ${ LOCAL_COMMAND_STDOUT_TAG } > ` ) && ! text . startsWith ( ` < ${ COMMAND_MESSAGE_TAG } > ` ) && ! text . startsWith ( ` < ${ COMMAND_NAME_TAG } > ` ) && ! text . startsWith ( ` < ${ BASH_INPUT_TAG } > ` ) ) {
haikuTitleAttemptedRef . current = true ;
haikuTitleAttemptedRef . current = true ;
void generateSessionTitle ( text , new AbortController ( ) . signal ) . then ( title = > {
void generateSessionTitle ( text , new AbortController ( ) . signal ) . then ( title = > {
if ( title ) setHaikuTitle ( title ) ; else haikuTitleAttemptedRef . current = false ;
if ( title ) setHaikuTitle ( title ) ; else haikuTitleAttemptedRef . current = false ;
} , ( ) = > {
} , ( ) = > {
haikuTitleAttemptedRef . current = false ;
haikuTitleAttemptedRef . current = false ;
} ) ;
} ) ;
@@ -2760,11 +2760,11 @@ export function REPL({
} ) ;
} ) ;
}
}
queryCheckpoint ( 'query_context_loading_start' ) ;
queryCheckpoint ( 'query_context_loading_start' ) ;
const [ , , defaultSystemPrompt , baseUserContext , systemContext ] = await Promise . all ( [
const [ , , defaultSystemPrompt , baseUserContext , systemContext ] = await Promise . all ( [
// IMPORTANT: do this after setMessages() above, to avoid UI jank
// IMPORTANT: do this after setMessages() above, to avoid UI jank
checkAndDisableBypassPermissionsIfNeeded ( toolPermissionContext , setAppState ) ,
checkAndDisableBypassPermissionsIfNeeded ( toolPermissionContext , setAppState ) ,
// Gated on TRANSCRIPT_CLASSIFIER so GrowthBook kill switch runs wherever auto mode is built in
// Gated on TRANSCRIPT_CLASSIFIER so GrowthBook kill switch runs wherever auto mode is built in
feature ( 'TRANSCRIPT_CLASSIFIER' ) ? checkAndDisableAutoModeIfNeeded ( toolPermissionContext , setAppState , store . getState ( ) . fastMode ) : undefined , getSystemPrompt ( freshTools , mainLoopModelParam , Array . from ( toolPermissionContext . additionalWorkingDirectories . keys ( ) ) , freshMcpClients ) , getUserContext ( ) , getSystemContext ( ) ] ) ;
feature ( 'TRANSCRIPT_CLASSIFIER' ) ? checkAndDisableAutoModeIfNeeded ( toolPermissionContext , setAppState , store . getState ( ) . fastMode ) : undefined , getSystemPrompt ( freshTools , mainLoopModelParam , Array . from ( toolPermissionContext . additionalWorkingDirectories . keys ( ) ) , freshMcpClients ) , getUserContext ( ) , getSystemContext ( ) ] ) ;
const userContext = {
const userContext = {
. . . baseUserContext ,
. . . baseUserContext ,
. . . getCoordinatorUserContext ( freshMcpClients , isScratchpadEnabled ( ) ? getScratchpadDir ( ) : undefined ) ,
. . . getCoordinatorUserContext ( freshMcpClients , isScratchpadEnabled ( ) ? getScratchpadDir ( ) : undefined ) ,
@@ -3110,9 +3110,9 @@ export function REPL({
if ( typeof content === 'string' && ! initialMsg . message . planContent ) {
if ( typeof content === 'string' && ! initialMsg . message . planContent ) {
// Route through onSubmit for proper processing including UserPromptSubmit hooks
// Route through onSubmit for proper processing including UserPromptSubmit hooks
void onSubmit ( content , {
void onSubmit ( content , {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ) ;
} ) ;
} else {
} else {
// Plan messages or complex content (images, etc.) - send directly to model
// Plan messages or complex content (images, etc.) - send directly to model
@@ -3121,10 +3121,10 @@ export function REPL({
const newAbortController = createAbortController ( ) ;
const newAbortController = createAbortController ( ) ;
setAbortController ( newAbortController ) ;
setAbortController ( newAbortController ) ;
void onQuery ( [ initialMsg . message ] , newAbortController , true ,
void onQuery ( [ initialMsg . message ] , newAbortController , true ,
// shouldQuery
// shouldQuery
[ ] ,
[ ] ,
// additionalAllowedTools
// additionalAllowedTools
mainLoopModel ) ;
mainLoopModel ) ;
}
}
// Reset ref after a delay to allow new initial messages
// Reset ref after a delay to allow new initial messages
@@ -3526,18 +3526,18 @@ export function REPL({
setStashedPrompt ( undefined ) ;
setStashedPrompt ( undefined ) ;
}
}
} , [ queryGuard ,
} , [ queryGuard ,
// isLoading is read at the !isLoading checks above for input-clearing
// isLoading is read at the !isLoading checks above for input-clearing
// and submitCount gating. It's derived from isQueryActive || isExternalLoading,
// and submitCount gating. It's derived from isQueryActive || isExternalLoading,
// so including it here ensures the closure captures the fresh value.
// so including it here ensures the closure captures the fresh value.
isLoading , isExternalLoading , inputMode , commands , setInputValue , setInputMode , setPastedContents , setSubmitCount , setIDESelection , setToolJSX , getToolUseContext ,
isLoading , isExternalLoading , inputMode , commands , setInputValue , setInputMode , setPastedContents , setSubmitCount , setIDESelection , setToolJSX , getToolUseContext ,
// messages is read via messagesRef.current inside the callback to
// messages is read via messagesRef.current inside the callback to
// keep onSubmit stable across message updates (see L2384/L2400/L2662).
// keep onSubmit stable across message updates (see L2384/L2400/L2662).
// Without this, each setMessages call (~30× per turn) recreates
// Without this, each setMessages call (~30× per turn) recreates
// onSubmit, pinning the REPL render scope (1776B) + that render's
// onSubmit, pinning the REPL render scope (1776B) + that render's
// messages array in downstream closures (PromptInput, handleAutoRunIssue).
// messages array in downstream closures (PromptInput, handleAutoRunIssue).
// Heap analysis showed ~9 REPL scopes and ~15 messages array versions
// Heap analysis showed ~9 REPL scopes and ~15 messages array versions
// accumulating after #20174/#20175, all traced to this dep.
// accumulating after #20174/#20175, all traced to this dep.
mainLoopModel , pastedContents , ideSelection , setUserInputOnProcessing , setAbortController , addNotification , onQuery , stashedPrompt , setStashedPrompt , setAppState , onBeforeQuery , canUseTool , remoteSession , setMessages , awaitPendingHooks , repinScroll ] ) ;
mainLoopModel , pastedContents , ideSelection , setUserInputOnProcessing , setAbortController , addNotification , onQuery , stashedPrompt , setStashedPrompt , setAppState , onBeforeQuery , canUseTool , remoteSession , setMessages , awaitPendingHooks , repinScroll ] ) ;
// Callback for when user submits input while viewing a teammate's transcript
// Callback for when user submits input while viewing a teammate's transcript
const onAgentSubmit = useCallback ( async ( input : string , task : InProcessTeammateTaskState | LocalAgentTaskState , helpers : PromptInputHelpers ) = > {
const onAgentSubmit = useCallback ( async ( input : string , task : InProcessTeammateTaskState | LocalAgentTaskState , helpers : PromptInputHelpers ) = > {
@@ -3558,8 +3558,8 @@ export function REPL({
addNotification ( {
addNotification ( {
key : ` resume-agent-failed- ${ task . id } ` ,
key : ` resume-agent-failed- ${ task . id } ` ,
jsx : < Text color = "error" >
jsx : < Text color = "error" >
Failed to resume agent : { errorMessage ( err ) }
Failed to resume agent : { errorMessage ( err ) }
< / Text > ,
< / Text > ,
priority : 'low'
priority : 'low'
} ) ;
} ) ;
} ) ;
} ) ;
@@ -3577,9 +3577,9 @@ export function REPL({
const command = autoRunIssueReason ? getAutoRunCommand ( autoRunIssueReason ) : '/issue' ;
const command = autoRunIssueReason ? getAutoRunCommand ( autoRunIssueReason ) : '/issue' ;
setAutoRunIssueReason ( null ) ; // Clear the state
setAutoRunIssueReason ( null ) ; // Clear the state
onSubmit ( command , {
onSubmit ( command , {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ) . catch ( err = > {
} ) . catch ( err = > {
logForDebugging ( ` Auto-run ${ command } failed: ${ errorMessage ( err ) } ` ) ;
logForDebugging ( ` Auto-run ${ command } failed: ${ errorMessage ( err ) } ` ) ;
} ) ;
} ) ;
@@ -3592,9 +3592,9 @@ export function REPL({
const handleSurveyRequestFeedback = useCallback ( ( ) = > {
const handleSurveyRequestFeedback = useCallback ( ( ) = > {
const command = "external" === 'ant' ? '/issue' : '/feedback' ;
const command = "external" === 'ant' ? '/issue' : '/feedback' ;
onSubmit ( command , {
onSubmit ( command , {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ) . catch ( err = > {
} ) . catch ( err = > {
logForDebugging ( ` Survey feedback request failed: ${ err instanceof Error ? err.message : String ( err ) } ` ) ;
logForDebugging ( ` Survey feedback request failed: ${ err instanceof Error ? err.message : String ( err ) } ` ) ;
} ) ;
} ) ;
@@ -3609,9 +3609,9 @@ export function REPL({
onSubmitRef . current = onSubmit ;
onSubmitRef . current = onSubmit ;
const handleOpenRateLimitOptions = useCallback ( ( ) = > {
const handleOpenRateLimitOptions = useCallback ( ( ) = > {
void onSubmitRef . current ( '/rate-limit-options' , {
void onSubmitRef . current ( '/rate-limit-options' , {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ) ;
} ) ;
} , [ ] ) ;
} , [ ] ) ;
const handleExit = useCallback ( async ( ) = > {
const handleExit = useCallback ( async ( ) = > {
@@ -3628,14 +3628,14 @@ export function REPL({
}
}
const showWorktree = getCurrentWorktreeSession ( ) !== null ;
const showWorktree = getCurrentWorktreeSession ( ) !== null ;
if ( showWorktree ) {
if ( showWorktree ) {
setExitFlow ( < ExitFlow showWorktree onDone = { ( ) = > { } } onCancel = { ( ) = > {
setExitFlow ( < ExitFlow showWorktree onDone = { ( ) = > { } } onCancel = { ( ) = > {
setExitFlow ( null ) ;
setExitFlow ( null ) ;
setIsExiting ( false ) ;
setIsExiting ( false ) ;
} } / > ) ;
} } / > ) ;
return ;
return ;
}
}
const exitMod = await exit . load ( ) ;
const exitMod = await exit . load ( ) ;
const exitFlowResult = await exitMod . call ( ( ) = > { } ) ;
const exitFlowResult = await exitMod . call ( ( ) = > { } ) ;
setExitFlow ( exitFlowResult ) ;
setExitFlow ( exitFlowResult ) ;
// If call() returned without killing the process (bg session detach),
// If call() returned without killing the process (bg session detach),
// clear isExiting so the UI is usable on reattach. No-op on the normal
// clear isExiting so the UI is usable on reattach. No-op on the normal
@@ -3749,18 +3749,18 @@ export function REPL({
} ;
} ;
const messageActionCaps : MessageActionCaps = {
const messageActionCaps : MessageActionCaps = {
copy : text = >
copy : text = >
// setClipboard RETURNS OSC 52 — caller must stdout.write (tmux side-effects load-buffer, but that's tmux-only).
// setClipboard RETURNS OSC 52 — caller must stdout.write (tmux side-effects load-buffer, but that's tmux-only).
void setClipboard ( text ) . then ( raw = > {
void setClipboard ( text ) . then ( raw = > {
if ( raw ) process . stdout . write ( raw ) ;
if ( raw ) process . stdout . write ( raw ) ;
addNotification ( {
addNotification ( {
// Same key as text-selection copy — repeated copies replace toast, don't queue.
// Same key as text-selection copy — repeated copies replace toast, don't queue.
key : 'selection-copied' ,
key : 'selection-copied' ,
text : 'copied' ,
text : 'copied' ,
color : 'success' ,
color : 'success' ,
priority : 'immediate' ,
priority : 'immediate' ,
timeoutMs : 2000
timeoutMs : 2000
} ) ;
} ) ;
} ) ,
} ) ,
edit : async msg = > {
edit : async msg = > {
// Same skip-confirm check as /rewind: lossless → direct, else confirm dialog.
// Same skip-confirm check as /rewind: lossless → direct, else confirm dialog.
const rawIdx = findRawIndex ( msg . uuid ) ;
const rawIdx = findRawIndex ( msg . uuid ) ;
@@ -3856,14 +3856,14 @@ export function REPL({
const executeQueuedInput = useCallback ( async ( queuedCommands : QueuedCommand [ ] ) = > {
const executeQueuedInput = useCallback ( async ( queuedCommands : QueuedCommand [ ] ) = > {
await handlePromptSubmit ( {
await handlePromptSubmit ( {
helpers : {
helpers : {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ,
} ,
queryGuard ,
queryGuard ,
commands ,
commands ,
onInputChange : ( ) = > { } ,
onInputChange : ( ) = > { } ,
setPastedContents : ( ) = > { } ,
setPastedContents : ( ) = > { } ,
setToolJSX ,
setToolJSX ,
getToolUseContext ,
getToolUseContext ,
messages ,
messages ,
@@ -3924,8 +3924,8 @@ export function REPL({
// User hasn't interacted since response ended, check other conditions
// User hasn't interacted since response ended, check other conditions
const idleTimeSinceResponse = Date . now ( ) - lastQueryCompletionTime ;
const idleTimeSinceResponse = Date . now ( ) - lastQueryCompletionTime ;
if ( ! isLoading && ! toolJSX &&
if ( ! isLoading && ! toolJSX &&
// Use ref to get current dialog state, avoiding stale closure
// Use ref to get current dialog state, avoiding stale closure
focusedInputDialogRef . current === undefined && idleTimeSinceResponse >= getGlobalConfig ( ) . messageIdleNotifThresholdMs ) {
focusedInputDialogRef . current === undefined && idleTimeSinceResponse >= getGlobalConfig ( ) . messageIdleNotifThresholdMs ) {
void sendNotification ( {
void sendNotification ( {
message : 'Claude is waiting for your input' ,
message : 'Claude is waiting for your input' ,
notificationType : 'idle_prompt'
notificationType : 'idle_prompt'
@@ -3957,13 +3957,13 @@ export function REPL({
addNotif ( {
addNotif ( {
key : 'idle-return-hint' ,
key : 'idle-return-hint' ,
jsx : mode === 'hint_v2' ? < >
jsx : mode === 'hint_v2' ? < >
< Text dimColor > new task ? < / Text >
< Text dimColor > new task ? < / Text >
< Text color = "suggestion" > / clear < / Text >
< Text color = "suggestion" > / clear < / Text >
< Text dimColor > to save < / Text >
< Text dimColor > to save < / Text >
< Text color = "suggestion" > { formattedTokens } tokens < / Text >
< Text color = "suggestion" > { formattedTokens } tokens < / Text >
< / > : < Text color = "warning" >
< / > : < Text color = "warning" >
new task ? / c l e a r t o s a v e { f o r m a t t e d T o k e n s } t o k e n s
new task ? / c l e a r t o s a v e { f o r m a t t e d T o k e n s } t o k e n s
< / Text > ,
< / Text > ,
priority : 'medium' ,
priority : 'medium' ,
// Persist until submit — the hint fires at T+75min idle, user may
// Persist until submit — the hint fires at T+75min idle, user may
// not return for hours. removeNotification in useEffect cleanup
// not return for hours. removeNotification in useEffect cleanup
@@ -4015,17 +4015,17 @@ export function REPL({
// Voice input integration (VOICE_MODE builds only)
// Voice input integration (VOICE_MODE builds only)
const voice = feature ( 'VOICE_MODE' ) ?
const voice = feature ( 'VOICE_MODE' ) ?
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
useVoiceIntegration ( {
useVoiceIntegration ( {
setInputValueRaw ,
setInputValueRaw ,
inputValueRef ,
inputValueRef ,
insertTextRef
insertTextRef
} ) : {
} ) : {
stripTrailing : ( ) = > 0 ,
stripTrailing : ( ) = > 0 ,
handleKeyEvent : ( ) = > { } ,
handleKeyEvent : ( ) = > { } ,
resetAnchor : ( ) = > { } ,
resetAnchor : ( ) = > { } ,
interimRange : null
interimRange : null
} ;
} ;
useInboxPoller ( {
useInboxPoller ( {
enabled : isAgentSwarmsEnabled ( ) ,
enabled : isAgentSwarmsEnabled ( ) ,
isLoading ,
isLoading ,
@@ -4228,11 +4228,11 @@ export function REPL({
event . stopImmediatePropagation ( ) ;
event . stopImmediatePropagation ( ) ;
}
}
} ,
} ,
// Search needs virtual scroll (jumpRef drives VirtualMessageList). [
// Search needs virtual scroll (jumpRef drives VirtualMessageList). [
// kills it, so !dumpMode — after [ there's nothing to jump in.
// kills it, so !dumpMode — after [ there's nothing to jump in.
{
{
isActive : screen === 'transcript' && virtualScrollActive && ! searchOpen && ! dumpMode
isActive : screen === 'transcript' && virtualScrollActive && ! searchOpen && ! dumpMode
} ) ;
} ) ;
const {
const {
setQuery : setHighlight ,
setQuery : setHighlight ,
scanElement ,
scanElement ,
@@ -4323,12 +4323,12 @@ export function REPL({
} ) ( ) ;
} ) ( ) ;
}
}
} ,
} ,
// !searchOpen: typing 'v' or '[' in the search bar is search input, not
// !searchOpen: typing 'v' or '[' in the search bar is search input, not
// a command. No !dumpMode here — v should work after [ (the [ handler
// a command. No !dumpMode here — v should work after [ (the [ handler
// guards itself inline).
// guards itself inline).
{
{
isActive : screen === 'transcript' && virtualScrollActive && ! searchOpen
isActive : screen === 'transcript' && virtualScrollActive && ! searchOpen
} ) ;
} ) ;
// Fresh `less` per transcript entry. Prevents stale highlights matching
// Fresh `less` per transcript entry. Prevents stale highlights matching
// unrelated normal-mode text (overlay is alt-screen-global) and avoids
// unrelated normal-mode text (overlay is alt-screen-global) and avoids
@@ -4396,78 +4396,78 @@ export function REPL({
const transcriptScrollRef = isFullscreenEnvEnabled ( ) && ! disableVirtualScroll && ! dumpMode ? scrollRef : undefined ;
const transcriptScrollRef = isFullscreenEnvEnabled ( ) && ! disableVirtualScroll && ! dumpMode ? scrollRef : undefined ;
const transcriptMessagesElement = < Messages messages = { transcriptMessages } tools = { tools } commands = { commands } verbose = { true } toolJSX = { null } toolUseConfirmQueue = { [ ] } inProgressToolUseIDs = { inProgressToolUseIDs } isMessageSelectorVisible = { false } conversationId = { conversationId } screen = { screen } agentDefinitions = { agentDefinitions } streamingToolUses = { transcriptStreamingToolUses } showAllInTranscript = { showAllInTranscript } onOpenRateLimitOptions = { handleOpenRateLimitOptions } isLoading = { isLoading } hidePastThinking = { true } streamingThinking = { streamingThinking } scrollRef = { transcriptScrollRef } jumpRef = { jumpRef } onSearchMatchesChange = { onSearchMatchesChange } scanElement = { scanElement } setPositions = { setPositions } disableRenderCap = { dumpMode } / > ;
const transcriptMessagesElement = < Messages messages = { transcriptMessages } tools = { tools } commands = { commands } verbose = { true } toolJSX = { null } toolUseConfirmQueue = { [ ] } inProgressToolUseIDs = { inProgressToolUseIDs } isMessageSelectorVisible = { false } conversationId = { conversationId } screen = { screen } agentDefinitions = { agentDefinitions } streamingToolUses = { transcriptStreamingToolUses } showAllInTranscript = { showAllInTranscript } onOpenRateLimitOptions = { handleOpenRateLimitOptions } isLoading = { isLoading } hidePastThinking = { true } streamingThinking = { streamingThinking } scrollRef = { transcriptScrollRef } jumpRef = { jumpRef } onSearchMatchesChange = { onSearchMatchesChange } scanElement = { scanElement } setPositions = { setPositions } disableRenderCap = { dumpMode } / > ;
const transcriptToolJSX = toolJSX && < Box flexDirection = "column" width = "100%" >
const transcriptToolJSX = toolJSX && < Box flexDirection = "column" width = "100%" >
{ toolJSX . jsx }
{ toolJSX . jsx }
< / Box > ;
< / Box > ;
const transcriptReturn = < KeybindingSetup >
const transcriptReturn = < KeybindingSetup >
< AnimatedTerminalTitle isAnimating = { titleIsAnimating } title = { terminalTitle } disabled = { titleDisabled } noPrefix = { showStatusInTerminalTab } / >
< AnimatedTerminalTitle isAnimating = { titleIsAnimating } title = { terminalTitle } disabled = { titleDisabled } noPrefix = { showStatusInTerminalTab } / >
< GlobalKeybindingHandlers { ...globalKeybindingProps } / >
< GlobalKeybindingHandlers { ...globalKeybindingProps } / >
{ feature ( 'VOICE_MODE' ) ? < VoiceKeybindingHandler voiceHandleKeyEvent = { voice . handleKeyEvent } stripTrailing = { voice . stripTrailing } resetAnchor = { voice . resetAnchor } isActive = { ! toolJSX ? . isLocalJSXCommand } / > : null }
{ feature ( 'VOICE_MODE' ) ? < VoiceKeybindingHandler voiceHandleKeyEvent = { voice . handleKeyEvent } stripTrailing = { voice . stripTrailing } resetAnchor = { voice . resetAnchor } isActive = { ! toolJSX ? . isLocalJSXCommand } / > : null }
< CommandKeybindingHandlers onSubmit = { onSubmit } isActive = { ! toolJSX ? . isLocalJSXCommand } / >
< CommandKeybindingHandlers onSubmit = { onSubmit } isActive = { ! toolJSX ? . isLocalJSXCommand } / >
{ transcriptScrollRef ?
{ transcriptScrollRef ?
// ScrollKeybindingHandler must mount before CancelRequestHandler so
// ScrollKeybindingHandler must mount before CancelRequestHandler so
// ctrl+c-with-selection copies instead of cancelling the active task.
// ctrl+c-with-selection copies instead of cancelling the active task.
// Its raw useInput handler only stops propagation when a selection
// Its raw useInput handler only stops propagation when a selection
// exists — without one, ctrl+c falls through to CancelRequestHandler.
// exists — without one, ctrl+c falls through to CancelRequestHandler.
< ScrollKeybindingHandler scrollRef = { scrollRef }
< ScrollKeybindingHandler scrollRef = { scrollRef }
// Yield wheel/ctrl+u/d to UltraplanChoiceDialog's own scroll
// Yield wheel/ctrl+u/d to UltraplanChoiceDialog's own scroll
// handler while the modal is showing.
// handler while the modal is showing.
isActive = { focusedInputDialog !== 'ultraplan-choice' }
isActive = { focusedInputDialog !== 'ultraplan-choice' }
// g/G/j/k/ctrl+u/ctrl+d would eat keystrokes the search bar
// g/G/j/k/ctrl+u/ctrl+d would eat keystrokes the search bar
// wants. Off while searching.
// wants. Off while searching.
isModal = { ! searchOpen }
isModal = { ! searchOpen }
// Manual scroll exits the search context — clear the yellow
// Manual scroll exits the search context — clear the yellow
// current-match marker. Positions are (msg, rowOffset)-keyed;
// current-match marker. Positions are (msg, rowOffset)-keyed;
// j/k changes scrollTop so rowOffset is stale → wrong row
// j/k changes scrollTop so rowOffset is stale → wrong row
// gets yellow. Next n/N re-establishes via step()→jump().
// gets yellow. Next n/N re-establishes via step()→jump().
onScroll = { ( ) = > jumpRef . current ? . disarmSearch ( ) } / > : null }
onScroll = { ( ) = > jumpRef . current ? . disarmSearch ( ) } / > : null }
< CancelRequestHandler { ...cancelRequestProps } / >
< CancelRequestHandler { ...cancelRequestProps } / >
{ transcriptScrollRef ? < FullscreenLayout scrollRef = { scrollRef } scrollable = { < >
{ transcriptScrollRef ? < FullscreenLayout scrollRef = { scrollRef } scrollable = { < >
{ transcriptMessagesElement }
{ transcriptMessagesElement }
{ transcriptToolJSX }
{ transcriptToolJSX }
< SandboxViolationExpandedView / >
< SandboxViolationExpandedView / >
< / > } bottom = { searchOpen ? < TranscriptSearchBar jumpRef = { jumpRef }
< / > } bottom = { searchOpen ? < TranscriptSearchBar jumpRef = { jumpRef }
// Seed was tried (c01578c8) — broke /hello muscle
// Seed was tried (c01578c8) — broke /hello muscle
// memory (cursor lands after 'foo', /hello → foohello).
// memory (cursor lands after 'foo', /hello → foohello).
// Cancel-restore handles the 'don't lose prior search'
// Cancel-restore handles the 'don't lose prior search'
// concern differently (onCancel re-applies searchQuery).
// concern differently (onCancel re-applies searchQuery).
initialQuery = "" count = { searchCount } current = { searchCurrent } onClose = { q = > {
initialQuery = "" count = { searchCount } current = { searchCurrent } onClose = { q = > {
// Enter — commit. 0-match guard: junk query shouldn't
// Enter — commit. 0-match guard: junk query shouldn't
// persist (badge hidden, n/N dead anyway).
// persist (badge hidden, n/N dead anyway).
setSearchQuery ( searchCount > 0 ? q : '' ) ;
setSearchQuery ( searchCount > 0 ? q : '' ) ;
setSearchOpen ( false ) ;
setSearchOpen ( false ) ;
// onCancel path: bar unmounts before its useEffect([query])
// onCancel path: bar unmounts before its useEffect([query])
// can fire with ''. Without this, searchCount stays stale
// can fire with ''. Without this, searchCount stays stale
// (n guard at :4956 passes) and VML's matches[] too
// (n guard at :4956 passes) and VML's matches[] too
// (nextMatch walks the old array). Phantom nav, no
// (nextMatch walks the old array). Phantom nav, no
// highlight. onExit (Enter, q non-empty) still commits.
// highlight. onExit (Enter, q non-empty) still commits.
if ( ! q ) {
if ( ! q ) {
setSearchCount ( 0 ) ;
setSearchCount ( 0 ) ;
setSearchCurrent ( 0 ) ;
setSearchCurrent ( 0 ) ;
jumpRef . current ? . setSearchQuery ( '' ) ;
}
} } onCancel = { ( ) = > {
// Esc/ctrl+c/ctrl+g — undo. Bar's effect last fired
// with whatever was typed. searchQuery (REPL state)
// is unchanged since / (onClose = commit, didn't run).
// Two VML calls: '' restores anchor (0-match else-
// branch), then searchQuery re-scans from anchor's
// nearest. Both synchronous — one React batch.
// setHighlight explicit: REPL's sync-effect dep is
// searchQuery (unchanged), wouldn't re-fire.
setSearchOpen ( false ) ;
jumpRef . current ? . setSearchQuery ( '' ) ;
jumpRef . current ? . setSearchQuery ( '' ) ;
}
jumpRef . current ? . setSearchQuery ( searchQuery ) ;
} } onCancel = { ( ) = > {
setHighlight ( searchQuery ) ;
// Esc/ctrl+c/ctrl+g — undo. Bar's effect last fired
} } setHighlight = { setHighlight } / > : < TranscriptModeFooter showAllInTranscript = { showAllInTranscript } virtualScroll = { true } status = { editorStatus || undefined } searchBadge = { searchQuery && searchCount > 0 ? {
// with whatever was typed. searchQuery (REPL state)
current : searchCurrent ,
// is unchanged since / (onClose = commit, didn't run).
count : searchCount
// Two VML calls: '' restores anchor (0-match else-
} : undefined } / > } / > : < >
// branch), then searchQuery re-scans from anchor's
{ transcriptMessagesElement }
// nearest. Both synchronous — one React batch.
{ transcriptToolJSX }
// setHighlight explicit: REPL's sync-effect dep is
< SandboxViolationExpandedView / >
// searchQuery (unchanged), wouldn't re-fire.
< TranscriptModeFooter showAllInTranscript = { showAllInTranscript } virtualScroll = { false } suppressShowAll = { dumpMode } status = { editorStatus || undefined } / >
setSearchOpen ( false ) ;
< / > }
jumpRef . current ? . setSearchQuery ( '' ) ;
< / KeybindingSetup > ;
jumpRef . current ? . setSearchQuery ( searchQuery ) ;
setHighlight ( searchQuery ) ;
} } setHighlight = { setHighlight } / > : < TranscriptModeFooter showAllInTranscript = { showAllInTranscript } virtualScroll = { true } status = { editorStatus || undefined } searchBadge = { searchQuery && searchCount > 0 ? {
current : searchCurrent ,
count : searchCount
} : undefined } / > } / > : < >
{ transcriptMessagesElement }
{ transcriptToolJSX }
< SandboxViolationExpandedView / >
< TranscriptModeFooter showAllInTranscript = { showAllInTranscript } virtualScroll = { false } suppressShowAll = { dumpMode } status = { editorStatus || undefined } / >
< / > }
< / KeybindingSetup > ;
// The virtual-scroll branch (FullscreenLayout above) needs
// The virtual-scroll branch (FullscreenLayout above) needs
// <AlternateScreen>'s <Box height={rows}> constraint — without it,
// <AlternateScreen>'s <Box height={rows}> constraint — without it,
// ScrollBox's flexGrow has no ceiling, viewport = content height,
// ScrollBox's flexGrow has no ceiling, viewport = content height,
@@ -4478,8 +4478,8 @@ export function REPL({
// unwrapped — it wants native terminal scrollback.
// unwrapped — it wants native terminal scrollback.
if ( transcriptScrollRef ) {
if ( transcriptScrollRef ) {
return < AlternateScreen mouseTracking = { isMouseTrackingEnabled ( ) } >
return < AlternateScreen mouseTracking = { isMouseTrackingEnabled ( ) } >
{ transcriptReturn }
{ transcriptReturn }
< / AlternateScreen > ;
< / AlternateScreen > ;
}
}
return transcriptReturn ;
return transcriptReturn ;
}
}
@@ -4541,11 +4541,11 @@ export function REPL({
// early return above wraps its virtual-scroll branch the same way; only
// early return above wraps its virtual-scroll branch the same way; only
// the 30-cap dump branch stays unwrapped for native terminal scrollback.
// the 30-cap dump branch stays unwrapped for native terminal scrollback.
const mainReturn = < KeybindingSetup >
const mainReturn = < KeybindingSetup >
< AnimatedTerminalTitle isAnimating = { titleIsAnimating } title = { terminalTitle } disabled = { titleDisabled } noPrefix = { showStatusInTerminalTab } / >
< AnimatedTerminalTitle isAnimating = { titleIsAnimating } title = { terminalTitle } disabled = { titleDisabled } noPrefix = { showStatusInTerminalTab } / >
< GlobalKeybindingHandlers { ...globalKeybindingProps } / >
< GlobalKeybindingHandlers { ...globalKeybindingProps } / >
{ feature ( 'VOICE_MODE' ) ? < VoiceKeybindingHandler voiceHandleKeyEvent = { voice . handleKeyEvent } stripTrailing = { voice . stripTrailing } resetAnchor = { voice . resetAnchor } isActive = { ! toolJSX ? . isLocalJSXCommand } / > : null }
{ feature ( 'VOICE_MODE' ) ? < VoiceKeybindingHandler voiceHandleKeyEvent = { voice . handleKeyEvent } stripTrailing = { voice . stripTrailing } resetAnchor = { voice . resetAnchor } isActive = { ! toolJSX ? . isLocalJSXCommand } / > : null }
< CommandKeybindingHandlers onSubmit = { onSubmit } isActive = { ! toolJSX ? . isLocalJSXCommand } / >
< CommandKeybindingHandlers onSubmit = { onSubmit } isActive = { ! toolJSX ? . isLocalJSXCommand } / >
{ /* ScrollKeybindingHandler must mount before CancelRequestHandler so
{ /* ScrollKeybindingHandler must mount before CancelRequestHandler so
ctrl+c-with-selection copies instead of cancelling the active task.
ctrl+c-with-selection copies instead of cancelling the active task.
Its raw useInput handler only stops propagation when a selection
Its raw useInput handler only stops propagation when a selection
exists — without one, ctrl+c falls through to CancelRequestHandler.
exists — without one, ctrl+c falls through to CancelRequestHandler.
@@ -4553,40 +4553,40 @@ export function REPL({
the modal's inner ScrollBox is not keyboard-driven. onScroll
the modal's inner ScrollBox is not keyboard-driven. onScroll
stays suppressed while a modal is showing so scroll doesn't
stays suppressed while a modal is showing so scroll doesn't
stamp divider/pill state. */ }
stamp divider/pill state. */ }
< ScrollKeybindingHandler scrollRef = { scrollRef } isActive = { isFullscreenEnvEnabled ( ) && ( centeredModal != null || ! focusedInputDialog || focusedInputDialog === 'tool-permission' ) } onScroll = { centeredModal || toolPermissionOverlay || viewedAgentTask ? undefined : composedOnScroll } / >
< ScrollKeybindingHandler scrollRef = { scrollRef } isActive = { isFullscreenEnvEnabled ( ) && ( centeredModal != null || ! focusedInputDialog || focusedInputDialog === 'tool-permission' ) } onScroll = { centeredModal || toolPermissionOverlay || viewedAgentTask ? undefined : composedOnScroll } / >
{ feature ( 'MESSAGE_ACTIONS' ) && isFullscreenEnvEnabled ( ) && ! disableMessageActions ? < MessageActionsKeybindings handlers = { messageActionHandlers } isActive = { cursor !== null } / > : null }
{ feature ( 'MESSAGE_ACTIONS' ) && isFullscreenEnvEnabled ( ) && ! disableMessageActions ? < MessageActionsKeybindings handlers = { messageActionHandlers } isActive = { cursor !== null } / > : null }
< CancelRequestHandler { ...cancelRequestProps } / >
< CancelRequestHandler { ...cancelRequestProps } / >
< MCPConnectionManager key = { remountKey } dynamicMcpConfig = { dynamicMcpConfig } isStrictMcpConfig = { strictMcpConfig } >
< MCPConnectionManager key = { remountKey } dynamicMcpConfig = { dynamicMcpConfig } isStrictMcpConfig = { strictMcpConfig } >
< FullscreenLayout scrollRef = { scrollRef } overlay = { toolPermissionOverlay } bottomFloat = { feature ( 'BUDDY' ) && companionVisible && ! companionNarrow ? < CompanionFloatingBubble / > : undefined } modal = { centeredModal } modalScrollRef = { modalScrollRef } dividerYRef = { dividerYRef } hidePill = { ! ! viewedAgentTask } hideSticky = { ! ! viewedTeammateTask } newMessageCount = { unseenDivider ? . count ? ? 0 } onPillClick = { ( ) = > {
< FullscreenLayout scrollRef = { scrollRef } overlay = { toolPermissionOverlay } bottomFloat = { feature ( 'BUDDY' ) && companionVisible && ! companionNarrow ? < CompanionFloatingBubble / > : undefined } modal = { centeredModal } modalScrollRef = { modalScrollRef } dividerYRef = { dividerYRef } hidePill = { ! ! viewedAgentTask } hideSticky = { ! ! viewedTeammateTask } newMessageCount = { unseenDivider ? . count ? ? 0 } onPillClick = { ( ) = > {
setCursor ( null ) ;
setCursor ( null ) ;
jumpToNew ( scrollRef . current ) ;
jumpToNew ( scrollRef . current ) ;
} } scrollable = { < >
} } scrollable = { < >
< TeammateViewHeader / >
< TeammateViewHeader / >
< Messages messages = { displayedMessages } tools = { tools } commands = { commands } verbose = { verbose } toolJSX = { toolJSX } toolUseConfirmQueue = { toolUseConfirmQueue } inProgressToolUseIDs = { viewedTeammateTask ? viewedTeammateTask . inProgressToolUseIDs ? ? new Set ( ) : inProgressToolUseIDs } isMessageSelectorVisible = { isMessageSelectorVisible } conversationId = { conversationId } screen = { screen } streamingToolUses = { streamingToolUses } showAllInTranscript = { showAllInTranscript } agentDefinitions = { agentDefinitions } onOpenRateLimitOptions = { handleOpenRateLimitOptions } isLoading = { isLoading } streamingText = { isLoading && ! viewedAgentTask ? visibleStreamingText : null } isBriefOnly = { viewedAgentTask ? false : isBriefOnly } unseenDivider = { viewedAgentTask ? undefined : unseenDivider } scrollRef = { isFullscreenEnvEnabled ( ) ? scrollRef : undefined } trackStickyPrompt = { isFullscreenEnvEnabled ( ) ? true : undefined } cursor = { cursor } setCursor = { setCursor } cursorNavRef = { cursorNavRef } / >
< Messages messages = { displayedMessages } tools = { tools } commands = { commands } verbose = { verbose } toolJSX = { toolJSX } toolUseConfirmQueue = { toolUseConfirmQueue } inProgressToolUseIDs = { viewedTeammateTask ? viewedTeammateTask . inProgressToolUseIDs ? ? new Set ( ) : inProgressToolUseIDs } isMessageSelectorVisible = { isMessageSelectorVisible } conversationId = { conversationId } screen = { screen } streamingToolUses = { streamingToolUses } showAllInTranscript = { showAllInTranscript } agentDefinitions = { agentDefinitions } onOpenRateLimitOptions = { handleOpenRateLimitOptions } isLoading = { isLoading } streamingText = { isLoading && ! viewedAgentTask ? visibleStreamingText : null } isBriefOnly = { viewedAgentTask ? false : isBriefOnly } unseenDivider = { viewedAgentTask ? undefined : unseenDivider } scrollRef = { isFullscreenEnvEnabled ( ) ? scrollRef : undefined } trackStickyPrompt = { isFullscreenEnvEnabled ( ) ? true : undefined } cursor = { cursor } setCursor = { setCursor } cursorNavRef = { cursorNavRef } / >
< AwsAuthStatusBox / >
< AwsAuthStatusBox / >
{ /* Hide the processing placeholder while a modal is showing —
{ /* Hide the processing placeholder while a modal is showing —
it would sit at the last visible transcript row right above
it would sit at the last visible transcript row right above
the ▔ divider, showing "❯ /config" as redundant clutter
the ▔ divider, showing "❯ /config" as redundant clutter
(the modal IS the /config UI). Outside modals it stays so
(the modal IS the /config UI). Outside modals it stays so
the user sees their input echoed while Claude processes. */ }
the user sees their input echoed while Claude processes. */ }
{ ! disabled && placeholderText && ! centeredModal && < UserTextMessage param = { {
{ ! disabled && placeholderText && ! centeredModal && < UserTextMessage param = { {
text : placeholderText ,
text : placeholderText ,
type : 'text'
type : 'text'
} } addMargin = { true } verbose = { verbose } / > }
} } addMargin = { true } verbose = { verbose } / > }
{ toolJSX && ! ( toolJSX . isLocalJSXCommand && toolJSX . isImmediate ) && ! toolJsxCentered && < Box flexDirection = "column" width = "100%" >
{ toolJSX && ! ( toolJSX . isLocalJSXCommand && toolJSX . isImmediate ) && ! toolJsxCentered && < Box flexDirection = "column" width = "100%" >
{ toolJSX . jsx }
{ toolJSX . jsx }
< / Box > }
< / Box > }
{ "external" === 'ant' && < TungstenLiveMonitor / > }
{ "external" === 'ant' && < TungstenLiveMonitor / > }
{ feature ( 'WEB_BROWSER_TOOL' ) ? WebBrowserPanelModule && < WebBrowserPanelModule.WebBrowserPanel / > : null }
{ feature ( 'WEB_BROWSER_TOOL' ) ? WebBrowserPanelModule && < WebBrowserPanelModule.WebBrowserPanel / > : null }
< Box flexGrow = { 1 } / >
< Box flexGrow = { 1 } / >
{ showSpinner && < SpinnerWithVerb mode = { streamMode } spinnerTip = { spinnerTip } responseLengthRef = { responseLengthRef } apiMetricsRef = { apiMetricsRef } overrideMessage = { spinnerMessage } spinnerSuffix = { stopHookSpinnerSuffix } verbose = { verbose } loadingStartTimeRef = { loadingStartTimeRef } totalPausedMsRef = { totalPausedMsRef } pauseStartTimeRef = { pauseStartTimeRef } overrideColor = { spinnerColor } overrideShimmerColor = { spinnerShimmerColor } hasActiveTools = { inProgressToolUseIDs . size > 0 } leaderIsIdle = { ! isLoading } / > }
{ showSpinner && < SpinnerWithVerb mode = { streamMode } spinnerTip = { spinnerTip } responseLengthRef = { responseLengthRef } apiMetricsRef = { apiMetricsRef } overrideMessage = { spinnerMessage } spinnerSuffix = { stopHookSpinnerSuffix } verbose = { verbose } loadingStartTimeRef = { loadingStartTimeRef } totalPausedMsRef = { totalPausedMsRef } pauseStartTimeRef = { pauseStartTimeRef } overrideColor = { spinnerColor } overrideShimmerColor = { spinnerShimmerColor } hasActiveTools = { inProgressToolUseIDs . size > 0 } leaderIsIdle = { ! isLoading } / > }
{ ! showSpinner && ! isLoading && ! userInputOnProcessing && ! hasRunningTeammates && isBriefOnly && ! viewedAgentTask && < BriefIdleStatus / > }
{ ! showSpinner && ! isLoading && ! userInputOnProcessing && ! hasRunningTeammates && isBriefOnly && ! viewedAgentTask && < BriefIdleStatus / > }
{ isFullscreenEnvEnabled ( ) && < PromptInputQueuedCommands / > }
{ isFullscreenEnvEnabled ( ) && < PromptInputQueuedCommands / > }
< / > } bottom = { < Box flexDirection = { feature ( 'BUDDY' ) && companionNarrow ? 'column' : 'row' } width = "100%" alignItems = { feature ( 'BUDDY' ) && companionNarrow ? undefined : 'flex-end' } >
< / > } bottom = { < Box flexDirection = { feature ( 'BUDDY' ) && companionNarrow ? 'column' : 'row' } width = "100%" alignItems = { feature ( 'BUDDY' ) && companionNarrow ? undefined : 'flex-end' } >
{ feature ( 'BUDDY' ) && companionNarrow && isFullscreenEnvEnabled ( ) && companionVisible ? < CompanionSprite / > : null }
{ feature ( 'BUDDY' ) && companionNarrow && isFullscreenEnvEnabled ( ) && companionVisible ? < CompanionSprite / > : null }
< Box flexDirection = "column" flexGrow = { 1 } >
< Box flexDirection = "column" flexGrow = { 1 } >
{ permissionStickyFooter }
{ permissionStickyFooter }
{ /* Immediate local-jsx commands (/btw, /sandbox, /assistant,
{ /* Immediate local-jsx commands (/btw, /sandbox, /assistant,
/issue) render here, NOT inside scrollable. They stay mounted
/issue) render here, NOT inside scrollable. They stay mounted
while the main conversation streams behind them, so ScrollBox
while the main conversation streams behind them, so ScrollBox
relayouts on each new message would drag them around. bottom
relayouts on each new message would drag them around. bottom
@@ -4595,13 +4595,13 @@ export function REPL({
stays in scrollable: the main loop is paused so no jiggle,
stays in scrollable: the main loop is paused so no jiggle,
and their tall content (DiffDetailView renders up to 400
and their tall content (DiffDetailView renders up to 400
lines with no internal scroll) needs the outer ScrollBox. */ }
lines with no internal scroll) needs the outer ScrollBox. */ }
{ toolJSX ? . isLocalJSXCommand && toolJSX . isImmediate && ! toolJsxCentered && < Box flexDirection = "column" width = "100%" >
{ toolJSX ? . isLocalJSXCommand && toolJSX . isImmediate && ! toolJsxCentered && < Box flexDirection = "column" width = "100%" >
{ toolJSX . jsx }
{ toolJSX . jsx }
< / Box > }
< / Box > }
{ ! showSpinner && ! toolJSX ? . isLocalJSXCommand && showExpandedTodos && tasksV2 && tasksV2 . length > 0 && < Box width = "100%" flexDirection = "column" >
{ ! showSpinner && ! toolJSX ? . isLocalJSXCommand && showExpandedTodos && tasksV2 && tasksV2 . length > 0 && < Box width = "100%" flexDirection = "column" >
< TaskListV2 tasks = { tasksV2 } isStandalone = { true } / >
< TaskListV2 tasks = { tasksV2 } isStandalone = { true } / >
< / Box > }
< / Box > }
{ focusedInputDialog === 'sandbox-permission' && < SandboxPermissionRequest key = { sandboxPermissionRequestQueue [ 0 ] ! . hostPattern . host } hostPattern = { sandboxPermissionRequestQueue [ 0 ] ! . hostPattern } onUserResponse = { ( response : {
{ focusedInputDialog === 'sandbox-permission' && < SandboxPermissionRequest key = { sandboxPermissionRequestQueue [ 0 ] ! . hostPattern . host } hostPattern = { sandboxPermissionRequestQueue [ 0 ] ! . hostPattern } onUserResponse = { ( response : {
allow : boolean ;
allow : boolean ;
persistToSettings : boolean ;
persistToSettings : boolean ;
} ) = > {
} ) = > {
@@ -4650,7 +4650,7 @@ export function REPL({
sandboxBridgeCleanupRef . current . delete ( approvedHost ) ;
sandboxBridgeCleanupRef . current . delete ( approvedHost ) ;
}
}
} } / > }
} } / > }
{ focusedInputDialog === 'prompt' && < PromptDialog key = { promptQueue [ 0 ] ! . request . prompt } title = { promptQueue [ 0 ] ! . title } toolInputSummary = { promptQueue [ 0 ] ! . toolInputSummary } request = { promptQueue [ 0 ] ! . request } onRespond = { selectedKey = > {
{ focusedInputDialog === 'prompt' && < PromptDialog key = { promptQueue [ 0 ] ! . request . prompt } title = { promptQueue [ 0 ] ! . title } toolInputSummary = { promptQueue [ 0 ] ! . toolInputSummary } request = { promptQueue [ 0 ] ! . request } onRespond = { selectedKey = > {
const item = promptQueue [ 0 ] ;
const item = promptQueue [ 0 ] ;
if ( ! item ) return ;
if ( ! item ) return ;
item . resolve ( {
item . resolve ( {
@@ -4664,12 +4664,12 @@ export function REPL({
item . reject ( new Error ( 'Prompt cancelled by user' ) ) ;
item . reject ( new Error ( 'Prompt cancelled by user' ) ) ;
setPromptQueue ( ( [ , . . . tail ] ) = > tail ) ;
setPromptQueue ( ( [ , . . . tail ] ) = > tail ) ;
} } / > }
} } / > }
{ /* Show pending indicator on worker while waiting for leader approval */ }
{ /* Show pending indicator on worker while waiting for leader approval */ }
{ pendingWorkerRequest && < WorkerPendingPermission toolName = { pendingWorkerRequest . toolName } description = { pendingWorkerRequest . description } / > }
{ pendingWorkerRequest && < WorkerPendingPermission toolName = { pendingWorkerRequest . toolName } description = { pendingWorkerRequest . description } / > }
{ /* Show pending indicator for sandbox permission on worker side */ }
{ /* Show pending indicator for sandbox permission on worker side */ }
{ pendingSandboxRequest && < WorkerPendingPermission toolName = "Network Access" description = { ` Waiting for leader to approve network access to ${ pendingSandboxRequest . host } ` } / > }
{ pendingSandboxRequest && < WorkerPendingPermission toolName = "Network Access" description = { ` Waiting for leader to approve network access to ${ pendingSandboxRequest . host } ` } / > }
{ /* Worker sandbox permission requests from swarm workers */ }
{ /* Worker sandbox permission requests from swarm workers */ }
{ focusedInputDialog === 'worker-sandbox-permission' && < SandboxPermissionRequest key = { workerSandboxPermissions . queue [ 0 ] ! . requestId } hostPattern = { {
{ focusedInputDialog === 'worker-sandbox-permission' && < SandboxPermissionRequest key = { workerSandboxPermissions . queue [ 0 ] ! . requestId } hostPattern = { {
host : workerSandboxPermissions.queue [ 0 ] ! . host ,
host : workerSandboxPermissions.queue [ 0 ] ! . host ,
port : undefined
port : undefined
} as NetworkHostPattern } onUserResponse = { ( response : {
} as NetworkHostPattern } onUserResponse = { ( response : {
@@ -4713,7 +4713,7 @@ export function REPL({
}
}
} ) ) ;
} ) ) ;
} } / > }
} } / > }
{ focusedInputDialog === 'elicitation' && < ElicitationDialog key = { elicitation . queue [ 0 ] ! . serverName + ':' + String ( elicitation . queue [ 0 ] ! . requestId ) } event = { elicitation . queue [ 0 ] ! } onResponse = { ( action , content ) = > {
{ focusedInputDialog === 'elicitation' && < ElicitationDialog key = { elicitation . queue [ 0 ] ! . serverName + ':' + String ( elicitation . queue [ 0 ] ! . requestId ) } event = { elicitation . queue [ 0 ] ! } onResponse = { ( action , content ) = > {
const currentRequest = elicitation . queue [ 0 ] ;
const currentRequest = elicitation . queue [ 0 ] ;
if ( ! currentRequest ) return ;
if ( ! currentRequest ) return ;
// Call respond callback to resolve Promise
// Call respond callback to resolve Promise
@@ -4742,7 +4742,7 @@ export function REPL({
} ) ) ;
} ) ) ;
currentRequest ? . onWaitingDismiss ? . ( action ) ;
currentRequest ? . onWaitingDismiss ? . ( action ) ;
} } / > }
} } / > }
{ focusedInputDialog === 'cost' && < CostThresholdDialog onDone = { ( ) = > {
{ focusedInputDialog === 'cost' && < CostThresholdDialog onDone = { ( ) = > {
setShowCostDialog ( false ) ;
setShowCostDialog ( false ) ;
setHaveShownCostDialog ( true ) ;
setHaveShownCostDialog ( true ) ;
saveGlobalConfig ( current = > ( {
saveGlobalConfig ( current = > ( {
@@ -4751,7 +4751,7 @@ export function REPL({
} ) ) ;
} ) ) ;
logEvent ( 'tengu_cost_threshold_acknowledged' , { } ) ;
logEvent ( 'tengu_cost_threshold_acknowledged' , { } ) ;
} } / > }
} } / > }
{ focusedInputDialog === 'idle-return' && idleReturnPending && < IdleReturnDialog idleMinutes = { idleReturnPending . idleMinutes } totalInputTokens = { getTotalInputTokens ( ) } onDone = { async action = > {
{ focusedInputDialog === 'idle-return' && idleReturnPending && < IdleReturnDialog idleMinutes = { idleReturnPending . idleMinutes } totalInputTokens = { getTotalInputTokens ( ) } onDone = { async action = > {
const pending = idleReturnPending ;
const pending = idleReturnPending ;
setIdleReturnPending ( null ) ;
setIdleReturnPending ( null ) ;
logEvent ( 'tengu_idle_return_action' , {
logEvent ( 'tengu_idle_return_action' , {
@@ -4793,13 +4793,13 @@ export function REPL({
}
}
skipIdleCheckRef . current = true ;
skipIdleCheckRef . current = true ;
void onSubmitRef . current ( pending . input , {
void onSubmitRef . current ( pending . input , {
setCursorOffset : ( ) = > { } ,
setCursorOffset : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
clearBuffer : ( ) = > { } ,
resetHistory : ( ) = > { }
resetHistory : ( ) = > { }
} ) ;
} ) ;
} } / > }
} } / > }
{ focusedInputDialog === 'ide-onboarding' && < IdeOnboardingDialog onDone = { ( ) = > setShowIdeOnboarding ( false ) } installationStatus = { ideInstallationStatus } / > }
{ focusedInputDialog === 'ide-onboarding' && < IdeOnboardingDialog onDone = { ( ) = > setShowIdeOnboarding ( false ) } installationStatus = { ideInstallationStatus } / > }
{ "external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && < AntModelSwitchCallout onDone = { ( selection : string , modelAlias? : string ) = > {
{ "external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && < AntModelSwitchCallout onDone = { ( selection : string , modelAlias? : string ) = > {
setShowModelSwitchCallout ( false ) ;
setShowModelSwitchCallout ( false ) ;
if ( selection === 'switch' && modelAlias ) {
if ( selection === 'switch' && modelAlias ) {
setAppState ( prev = > ( {
setAppState ( prev = > ( {
@@ -4809,8 +4809,8 @@ export function REPL({
} ) ) ;
} ) ) ;
}
}
} } / > }
} } / > }
{ "external" === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && < UndercoverAutoCallout onDone = { ( ) = > setShowUndercoverCallout ( false ) } / > }
{ "external" === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && < UndercoverAutoCallout onDone = { ( ) = > setShowUndercoverCallout ( false ) } / > }
{ focusedInputDialog === 'effort-callout' && < EffortCallout model = { mainLoopModel } onDone = { selection = > {
{ focusedInputDialog === 'effort-callout' && < EffortCallout model = { mainLoopModel } onDone = { selection = > {
setShowEffortCallout ( false ) ;
setShowEffortCallout ( false ) ;
if ( selection !== 'dismiss' ) {
if ( selection !== 'dismiss' ) {
setAppState ( prev = > ( {
setAppState ( prev = > ( {
@@ -4819,7 +4819,7 @@ export function REPL({
} ) ) ;
} ) ) ;
}
}
} } / > }
} } / > }
{ focusedInputDialog === 'remote-callout' && < RemoteCallout onDone = { selection = > {
{ focusedInputDialog === 'remote-callout' && < RemoteCallout onDone = { selection = > {
setAppState ( prev = > {
setAppState ( prev = > {
if ( ! prev . showRemoteCallout ) return prev ;
if ( ! prev . showRemoteCallout ) return prev ;
return {
return {
@@ -4834,17 +4834,17 @@ export function REPL({
} ) ;
} ) ;
} } / > }
} } / > }
{ exitFlow }
{ exitFlow }
{ focusedInputDialog === 'plugin-hint' && hintRecommendation && < PluginHintMenu pluginName = { hintRecommendation . pluginName } pluginDescription = { hintRecommendation . pluginDescription } marketplaceName = { hintRecommendation . marketplaceName } sourceCommand = { hintRecommendation . sourceCommand } onResponse = { handleHintResponse } / > }
{ focusedInputDialog === 'plugin-hint' && hintRecommendation && < PluginHintMenu pluginName = { hintRecommendation . pluginName } pluginDescription = { hintRecommendation . pluginDescription } marketplaceName = { hintRecommendation . marketplaceName } sourceCommand = { hintRecommendation . sourceCommand } onResponse = { handleHintResponse } / > }
{ focusedInputDialog === 'lsp-recommendation' && lspRecommendation && < LspRecommendationMenu pluginName = { lspRecommendation . pluginName } pluginDescription = { lspRecommendation . pluginDescription } fileExtension = { lspRecommendation . fileExtension } onResponse = { handleLspResponse } / > }
{ focusedInputDialog === 'lsp-recommendation' && lspRecommendation && < LspRecommendationMenu pluginName = { lspRecommendation . pluginName } pluginDescription = { lspRecommendation . pluginDescription } fileExtension = { lspRecommendation . fileExtension } onResponse = { handleLspResponse } / > }
{ focusedInputDialog === 'desktop-upsell' && < DesktopUpsellStartup onDone = { ( ) = > setShowDesktopUpsellStartup ( false ) } / > }
{ focusedInputDialog === 'desktop-upsell' && < DesktopUpsellStartup onDone = { ( ) = > setShowDesktopUpsellStartup ( false ) } / > }
{ feature ( 'ULTRAPLAN' ) ? focusedInputDialog === 'ultraplan-choice' && ultraplanPendingChoice && < UltraplanChoiceDialog plan = { ultraplanPendingChoice . plan } sessionId = { ultraplanPendingChoice . sessionId } taskId = { ultraplanPendingChoice . taskId } setMessages = { setMessages } readFileState = { readFileState . current } getAppState = { ( ) = > store . getState ( ) } setConversationId = { setConversationId } / > : null }
{ feature ( 'ULTRAPLAN' ) ? focusedInputDialog === 'ultraplan-choice' && ultraplanPendingChoice && < UltraplanChoiceDialog plan = { ultraplanPendingChoice . plan } sessionId = { ultraplanPendingChoice . sessionId } taskId = { ultraplanPendingChoice . taskId } setMessages = { setMessages } readFileState = { readFileState . current } getAppState = { ( ) = > store . getState ( ) } setConversationId = { setConversationId } / > : null }
{ feature ( 'ULTRAPLAN' ) ? focusedInputDialog === 'ultraplan-launch' && ultraplanLaunchPending && < UltraplanLaunchDialog onChoice = { ( choice , opts ) = > {
{ feature ( 'ULTRAPLAN' ) ? focusedInputDialog === 'ultraplan-launch' && ultraplanLaunchPending && < UltraplanLaunchDialog onChoice = { ( choice , opts ) = > {
const blurb = ultraplanLaunchPending . blurb ;
const blurb = ultraplanLaunchPending . blurb ;
setAppState ( prev = > prev . ultraplanLaunchPending ? {
setAppState ( prev = > prev . ultraplanLaunchPending ? {
. . . prev ,
. . . prev ,
@@ -4884,26 +4884,26 @@ export function REPL({
} ) . then ( appendStdout ) . catch ( logError ) ;
} ) . then ( appendStdout ) . catch ( logError ) ;
} } / > : null }
} } / > : null }
{ mrRender ( ) }
{ mrRender ( ) }
{ ! toolJSX ? . shouldHidePromptInput && ! focusedInputDialog && ! isExiting && ! disabled && ! cursor && < >
{ ! toolJSX ? . shouldHidePromptInput && ! focusedInputDialog && ! isExiting && ! disabled && ! cursor && < >
{ autoRunIssueReason && < AutoRunIssueNotification onRun = { handleAutoRunIssue } onCancel = { handleCancelAutoRunIssue } reason = { getAutoRunIssueReasonText ( autoRunIssueReason ) } / > }
{ autoRunIssueReason && < AutoRunIssueNotification onRun = { handleAutoRunIssue } onCancel = { handleCancelAutoRunIssue } reason = { getAutoRunIssueReasonText ( autoRunIssueReason ) } / > }
{ postCompactSurvey . state !== 'closed' ? < FeedbackSurvey state = { postCompactSurvey . state } lastResponse = { postCompactSurvey . lastResponse } handleSelect = { postCompactSurvey . handleSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { handleSurveyRequestFeedback } / > : memorySurvey . state !== 'closed' ? < FeedbackSurvey state = { memorySurvey . state } lastResponse = { memorySurvey . lastResponse } handleSelect = { memorySurvey . handleSelect } handleTranscriptSelect = { memorySurvey . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { handleSurveyRequestFeedback } message = "How well did Claude use its memory? (optional)" / > : < FeedbackSurvey state = { feedbackSurvey . state } lastResponse = { feedbackSurvey . lastResponse } handleSelect = { feedbackSurvey . handleSelect } handleTranscriptSelect = { feedbackSurvey . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { didAutoRunIssueRef . current ? undefined : handleSurveyRequestFeedback } / > }
{ postCompactSurvey . state !== 'closed' ? < FeedbackSurvey state = { postCompactSurvey . state } lastResponse = { postCompactSurvey . lastResponse } handleSelect = { postCompactSurvey . handleSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { handleSurveyRequestFeedback } / > : memorySurvey . state !== 'closed' ? < FeedbackSurvey state = { memorySurvey . state } lastResponse = { memorySurvey . lastResponse } handleSelect = { memorySurvey . handleSelect } handleTranscriptSelect = { memorySurvey . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { handleSurveyRequestFeedback } message = "How well did Claude use its memory? (optional)" / > : < FeedbackSurvey state = { feedbackSurvey . state } lastResponse = { feedbackSurvey . lastResponse } handleSelect = { feedbackSurvey . handleSelect } handleTranscriptSelect = { feedbackSurvey . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } onRequestFeedback = { didAutoRunIssueRef . current ? undefined : handleSurveyRequestFeedback } / > }
{ /* Frustration-triggered transcript sharing prompt */ }
{ /* Frustration-triggered transcript sharing prompt */ }
{ frustrationDetection . state !== 'closed' && < FeedbackSurvey state = { frustrationDetection . state } lastResponse = { null } handleSelect = { ( ) = > { } } handleTranscriptSelect = { frustrationDetection . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } / > }
{ frustrationDetection . state !== 'closed' && < FeedbackSurvey state = { frustrationDetection . state } lastResponse = { null } handleSelect = { ( ) = > { } } handleTranscriptSelect = { frustrationDetection . handleTranscriptSelect } inputValue = { inputValue } setInputValue = { setInputValue } / > }
{ /* Skill improvement survey - appears when improvements detected (ant-only) */ }
{ /* Skill improvement survey - appears when improvements detected (ant-only) */ }
{ "external" === 'ant' && skillImprovementSurvey . suggestion && < SkillImprovementSurvey isOpen = { skillImprovementSurvey . isOpen } skillName = { skillImprovementSurvey . suggestion . skillName } updates = { skillImprovementSurvey . suggestion . updates } handleSelect = { skillImprovementSurvey . handleSelect } inputValue = { inputValue } setInputValue = { setInputValue } / > }
{ "external" === 'ant' && skillImprovementSurvey . suggestion && < SkillImprovementSurvey isOpen = { skillImprovementSurvey . isOpen } skillName = { skillImprovementSurvey . suggestion . skillName } updates = { skillImprovementSurvey . suggestion . updates } handleSelect = { skillImprovementSurvey . handleSelect } inputValue = { inputValue } setInputValue = { setInputValue } / > }
{ showIssueFlagBanner && < IssueFlagBanner / > }
{ showIssueFlagBanner && < IssueFlagBanner / > }
{ }
{ }
< PromptInput debug = { debug } ideSelection = { ideSelection } hasSuppressedDialogs = { ! ! hasSuppressedDialogs } isLocalJSXCommandActive = { isShowingLocalJSXCommand } getToolUseContext = { getToolUseContext } toolPermissionContext = { toolPermissionContext } setToolPermissionContext = { setToolPermissionContext } apiKeyStatus = { apiKeyStatus } commands = { commands } agents = { agentDefinitions . activeAgents } isLoading = { isLoading } onExit = { handleExit } verbose = { verbose } messages = { messages } onAutoUpdaterResult = { setAutoUpdaterResult } autoUpdaterResult = { autoUpdaterResult } input = { inputValue } onInputChange = { setInputValue } mode = { inputMode } onModeChange = { setInputMode } stashedPrompt = { stashedPrompt } setStashedPrompt = { setStashedPrompt } submitCount = { submitCount } onShowMessageSelector = { handleShowMessageSelector } onMessageActionsEnter = {
< PromptInput debug = { debug } ideSelection = { ideSelection } hasSuppressedDialogs = { ! ! hasSuppressedDialogs } isLocalJSXCommandActive = { isShowingLocalJSXCommand } getToolUseContext = { getToolUseContext } toolPermissionContext = { toolPermissionContext } setToolPermissionContext = { setToolPermissionContext } apiKeyStatus = { apiKeyStatus } commands = { commands } agents = { agentDefinitions . activeAgents } isLoading = { isLoading } onExit = { handleExit } verbose = { verbose } messages = { messages } onAutoUpdaterResult = { setAutoUpdaterResult } autoUpdaterResult = { autoUpdaterResult } input = { inputValue } onInputChange = { setInputValue } mode = { inputMode } onModeChange = { setInputMode } stashedPrompt = { stashedPrompt } setStashedPrompt = { setStashedPrompt } submitCount = { submitCount } onShowMessageSelector = { handleShowMessageSelector } onMessageActionsEnter = {
// Works during isLoading — edit cancels first; uuid selection survives appends.
// Works during isLoading — edit cancels first; uuid selection survives appends.
feature ( 'MESSAGE_ACTIONS' ) && isFullscreenEnvEnabled ( ) && ! disableMessageActions ? enterMessageActions : undefined } mcpClients = { mcpClients } pastedContents = { pastedContents } setPastedContents = { setPastedContents } vimMode = { vimMode } setVimMode = { setVimMode } showBashesDialog = { showBashesDialog } setShowBashesDialog = { setShowBashesDialog } onSubmit = { onSubmit } onAgentSubmit = { onAgentSubmit } isSearchingHistory = { isSearchingHistory } setIsSearchingHistory = { setIsSearchingHistory } helpOpen = { isHelpOpen } setHelpOpen = { setIsHelpOpen } insertTextRef = { feature ( 'VOICE_MODE' ) ? insertTextRef : undefined } voiceInterimRange = { voice . interimRange } / >
feature ( 'MESSAGE_ACTIONS' ) && isFullscreenEnvEnabled ( ) && ! disableMessageActions ? enterMessageActions : undefined } mcpClients = { mcpClients } pastedContents = { pastedContents } setPastedContents = { setPastedContents } vimMode = { vimMode } setVimMode = { setVimMode } showBashesDialog = { showBashesDialog } setShowBashesDialog = { setShowBashesDialog } onSubmit = { onSubmit } onAgentSubmit = { onAgentSubmit } isSearchingHistory = { isSearchingHistory } setIsSearchingHistory = { setIsSearchingHistory } helpOpen = { isHelpOpen } setHelpOpen = { setIsHelpOpen } insertTextRef = { feature ( 'VOICE_MODE' ) ? insertTextRef : undefined } voiceInterimRange = { voice . interimRange } / >
< SessionBackgroundHint onBackgroundSession = { handleBackgroundSession } isLoading = { isLoading } / >
< SessionBackgroundHint onBackgroundSession = { handleBackgroundSession } isLoading = { isLoading } / >
< / > }
< / > }
{ cursor &&
{ cursor &&
// inputValue is REPL state; typed text survives the round-trip.
// inputValue is REPL state; typed text survives the round-trip.
< MessageActionsBar cursor = { cursor } / > }
< MessageActionsBar cursor = { cursor } / > }
{ focusedInputDialog === 'message-selector' && < MessageSelector messages = { messages } preselectedMessage = { messageSelectorPreselect } onPreRestore = { onCancel } onRestoreCode = { async ( message : UserMessage ) = > {
{ focusedInputDialog === 'message-selector' && < MessageSelector messages = { messages } preselectedMessage = { messageSelectorPreselect } onPreRestore = { onCancel } onRestoreCode = { async ( message : UserMessage ) = > {
await fileHistoryRewind ( ( updater : ( prev : FileHistoryState ) = > FileHistoryState ) = > {
await fileHistoryRewind ( ( updater : ( prev : FileHistoryState ) = > FileHistoryState ) = > {
setAppState ( prev = > ( {
setAppState ( prev = > ( {
. . . prev ,
. . . prev ,
@@ -4985,16 +4985,16 @@ export function REPL({
setIsMessageSelectorVisible ( false ) ;
setIsMessageSelectorVisible ( false ) ;
setMessageSelectorPreselect ( undefined ) ;
setMessageSelectorPreselect ( undefined ) ;
} } / > }
} } / > }
{ "external" === 'ant' && < DevBar / > }
{ "external" === 'ant' && < DevBar / > }
< / Box >
< / Box >
{ feature ( 'BUDDY' ) && ! ( companionNarrow && isFullscreenEnvEnabled ( ) ) && companionVisible ? < CompanionSprite / > : null }
{ feature ( 'BUDDY' ) && ! ( companionNarrow && isFullscreenEnvEnabled ( ) ) && companionVisible ? < CompanionSprite / > : null }
< / Box > } / >
< / Box > } / >
< / MCPConnectionManager >
< / MCPConnectionManager >
< / KeybindingSetup > ;
< / KeybindingSetup > ;
if ( isFullscreenEnvEnabled ( ) ) {
if ( isFullscreenEnvEnabled ( ) ) {
return < AlternateScreen mouseTracking = { isMouseTrackingEnabled ( ) } >
return < AlternateScreen mouseTracking = { isMouseTrackingEnabled ( ) } >
{ mainReturn }
{ mainReturn }
< / AlternateScreen > ;
< / AlternateScreen > ;
}
}
return mainReturn ;
return mainReturn ;
}
}