2025-09-06 01:39:02 -04:00
|
|
|
/**
|
|
|
|
|
* @license
|
2026-02-09 21:53:10 -05:00
|
|
|
* Copyright 2026 Google LLC
|
2025-09-06 01:39:02 -04:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { Box, Text } from 'ink';
|
|
|
|
|
import { IdeIntegrationNudge } from '../IdeIntegrationNudge.js';
|
2025-09-10 22:20:13 -07:00
|
|
|
import { LoopDetectionConfirmation } from './LoopDetectionConfirmation.js';
|
2025-09-06 01:39:02 -04:00
|
|
|
import { FolderTrustDialog } from './FolderTrustDialog.js';
|
2025-09-29 14:19:19 -07:00
|
|
|
import { ConsentPrompt } from './ConsentPrompt.js';
|
2025-09-06 01:39:02 -04:00
|
|
|
import { ThemeDialog } from './ThemeDialog.js';
|
|
|
|
|
import { SettingsDialog } from './SettingsDialog.js';
|
|
|
|
|
import { AuthInProgress } from '../auth/AuthInProgress.js';
|
|
|
|
|
import { AuthDialog } from '../auth/AuthDialog.js';
|
2026-02-27 10:18:16 -08:00
|
|
|
import { BannedAccountDialog } from '../auth/BannedAccountDialog.js';
|
2025-10-29 18:58:08 -07:00
|
|
|
import { ApiAuthDialog } from '../auth/ApiAuthDialog.js';
|
2025-09-06 01:39:02 -04:00
|
|
|
import { EditorSettingsDialog } from './EditorSettingsDialog.js';
|
|
|
|
|
import { PrivacyNotice } from '../privacy/PrivacyNotice.js';
|
|
|
|
|
import { ProQuotaDialog } from './ProQuotaDialog.js';
|
2026-01-20 16:23:01 -08:00
|
|
|
import { ValidationDialog } from './ValidationDialog.js';
|
2026-02-27 10:15:06 -08:00
|
|
|
import { OverageMenuDialog } from './OverageMenuDialog.js';
|
|
|
|
|
import { EmptyWalletDialog } from './EmptyWalletDialog.js';
|
2025-11-21 08:31:47 -08:00
|
|
|
import { runExitCleanup } from '../../utils/cleanup.js';
|
|
|
|
|
import { RELAUNCH_EXIT_CODE } from '../../utils/processUtils.js';
|
2025-11-25 11:54:09 -07:00
|
|
|
import { SessionBrowser } from './SessionBrowser.js';
|
2025-09-22 11:45:02 -07:00
|
|
|
import { PermissionsModifyTrustDialog } from './PermissionsModifyTrustDialog.js';
|
2025-09-23 12:50:09 -04:00
|
|
|
import { ModelDialog } from './ModelDialog.js';
|
2025-09-10 10:57:07 -07:00
|
|
|
import { theme } from '../semantic-colors.js';
|
2025-09-06 01:39:02 -04:00
|
|
|
import { useUIState } from '../contexts/UIStateContext.js';
|
|
|
|
|
import { useUIActions } from '../contexts/UIActionsContext.js';
|
|
|
|
|
import { useConfig } from '../contexts/ConfigContext.js';
|
|
|
|
|
import { useSettings } from '../contexts/SettingsContext.js';
|
|
|
|
|
import process from 'node:process';
|
2025-09-22 11:45:02 -07:00
|
|
|
import { type UseHistoryManagerReturn } from '../hooks/useHistoryManager.js';
|
2026-01-16 15:24:53 -05:00
|
|
|
import { AdminSettingsChangedDialog } from './AdminSettingsChangedDialog.js';
|
2025-09-29 13:54:12 -07:00
|
|
|
import { IdeTrustChangeDialog } from './IdeTrustChangeDialog.js';
|
2026-01-26 19:49:32 +00:00
|
|
|
import { NewAgentsNotification } from './NewAgentsNotification.js';
|
2026-01-23 16:10:51 -08:00
|
|
|
import { AgentConfigDialog } from './AgentConfigDialog.js';
|
2026-02-19 16:16:03 -08:00
|
|
|
import { PolicyUpdateDialog } from './PolicyUpdateDialog.js';
|
2025-09-22 11:45:02 -07:00
|
|
|
|
|
|
|
|
interface DialogManagerProps {
|
|
|
|
|
addItem: UseHistoryManagerReturn['addItem'];
|
2025-09-29 14:19:19 -07:00
|
|
|
terminalWidth: number;
|
2025-09-22 11:45:02 -07:00
|
|
|
}
|
2025-09-06 01:39:02 -04:00
|
|
|
|
|
|
|
|
// Props for DialogManager
|
2025-09-29 14:19:19 -07:00
|
|
|
export const DialogManager = ({
|
|
|
|
|
addItem,
|
|
|
|
|
terminalWidth,
|
|
|
|
|
}: DialogManagerProps) => {
|
2025-09-06 01:39:02 -04:00
|
|
|
const config = useConfig();
|
|
|
|
|
const settings = useSettings();
|
|
|
|
|
|
|
|
|
|
const uiState = useUIState();
|
|
|
|
|
const uiActions = useUIActions();
|
2026-01-26 15:23:54 -08:00
|
|
|
const {
|
|
|
|
|
constrainHeight,
|
|
|
|
|
terminalHeight,
|
|
|
|
|
staticExtraHeight,
|
|
|
|
|
terminalWidth: uiTerminalWidth,
|
|
|
|
|
} = uiState;
|
2025-09-06 01:39:02 -04:00
|
|
|
|
2026-01-16 15:24:53 -05:00
|
|
|
if (uiState.adminSettingsChanged) {
|
|
|
|
|
return <AdminSettingsChangedDialog />;
|
|
|
|
|
}
|
2025-09-06 01:39:02 -04:00
|
|
|
if (uiState.showIdeRestartPrompt) {
|
2025-09-29 13:54:12 -07:00
|
|
|
return <IdeTrustChangeDialog reason={uiState.ideTrustRestartReason} />;
|
2025-09-06 01:39:02 -04:00
|
|
|
}
|
2026-01-26 19:49:32 +00:00
|
|
|
if (uiState.newAgents) {
|
|
|
|
|
return (
|
|
|
|
|
<NewAgentsNotification
|
|
|
|
|
agents={uiState.newAgents}
|
|
|
|
|
onSelect={uiActions.handleNewAgentsSelect}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-09 21:53:10 -05:00
|
|
|
if (uiState.quota.proQuotaRequest) {
|
2025-09-06 01:39:02 -04:00
|
|
|
return (
|
|
|
|
|
<ProQuotaDialog
|
2026-02-09 21:53:10 -05:00
|
|
|
failedModel={uiState.quota.proQuotaRequest.failedModel}
|
|
|
|
|
fallbackModel={uiState.quota.proQuotaRequest.fallbackModel}
|
|
|
|
|
message={uiState.quota.proQuotaRequest.message}
|
|
|
|
|
isTerminalQuotaError={
|
|
|
|
|
uiState.quota.proQuotaRequest.isTerminalQuotaError
|
|
|
|
|
}
|
|
|
|
|
isModelNotFoundError={
|
|
|
|
|
!!uiState.quota.proQuotaRequest.isModelNotFoundError
|
|
|
|
|
}
|
2026-02-26 17:39:25 -05:00
|
|
|
authType={uiState.quota.proQuotaRequest.authType}
|
2025-09-06 01:39:02 -04:00
|
|
|
onChoice={uiActions.handleProQuotaChoice}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-09 21:53:10 -05:00
|
|
|
if (uiState.quota.validationRequest) {
|
2026-01-20 16:23:01 -08:00
|
|
|
return (
|
|
|
|
|
<ValidationDialog
|
2026-02-09 21:53:10 -05:00
|
|
|
validationLink={uiState.quota.validationRequest.validationLink}
|
|
|
|
|
validationDescription={
|
|
|
|
|
uiState.quota.validationRequest.validationDescription
|
|
|
|
|
}
|
|
|
|
|
learnMoreUrl={uiState.quota.validationRequest.learnMoreUrl}
|
2026-01-20 16:23:01 -08:00
|
|
|
onChoice={uiActions.handleValidationChoice}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-27 10:15:06 -08:00
|
|
|
if (uiState.quota.overageMenuRequest) {
|
|
|
|
|
return (
|
|
|
|
|
<OverageMenuDialog
|
|
|
|
|
failedModel={uiState.quota.overageMenuRequest.failedModel}
|
|
|
|
|
fallbackModel={uiState.quota.overageMenuRequest.fallbackModel}
|
|
|
|
|
resetTime={uiState.quota.overageMenuRequest.resetTime}
|
|
|
|
|
creditBalance={uiState.quota.overageMenuRequest.creditBalance}
|
|
|
|
|
onChoice={uiActions.handleOverageMenuChoice}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.quota.emptyWalletRequest) {
|
|
|
|
|
return (
|
|
|
|
|
<EmptyWalletDialog
|
|
|
|
|
failedModel={uiState.quota.emptyWalletRequest.failedModel}
|
|
|
|
|
fallbackModel={uiState.quota.emptyWalletRequest.fallbackModel}
|
|
|
|
|
resetTime={uiState.quota.emptyWalletRequest.resetTime}
|
|
|
|
|
onGetCredits={uiState.quota.emptyWalletRequest.onGetCredits}
|
|
|
|
|
onChoice={uiActions.handleEmptyWalletChoice}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-06 01:39:02 -04:00
|
|
|
if (uiState.shouldShowIdePrompt) {
|
|
|
|
|
return (
|
|
|
|
|
<IdeIntegrationNudge
|
|
|
|
|
ide={uiState.currentIDE!}
|
|
|
|
|
onComplete={uiActions.handleIdePromptComplete}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.isFolderTrustDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<FolderTrustDialog
|
|
|
|
|
onSelect={uiActions.handleFolderTrustSelect}
|
|
|
|
|
isRestarting={uiState.isRestarting}
|
2026-02-20 10:21:03 -08:00
|
|
|
discoveryResults={uiState.folderDiscoveryResults}
|
2025-09-06 01:39:02 -04:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-19 16:16:03 -08:00
|
|
|
if (uiState.isPolicyUpdateDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<PolicyUpdateDialog
|
|
|
|
|
config={config}
|
|
|
|
|
request={uiState.policyUpdateConfirmationRequest!}
|
|
|
|
|
onClose={() => uiActions.setIsPolicyUpdateDialogOpen(false)}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-10 22:20:13 -07:00
|
|
|
if (uiState.loopDetectionConfirmationRequest) {
|
|
|
|
|
return (
|
|
|
|
|
<LoopDetectionConfirmation
|
|
|
|
|
onComplete={uiState.loopDetectionConfirmationRequest.onComplete}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-01-30 09:57:34 -05:00
|
|
|
|
2026-02-09 12:24:28 -08:00
|
|
|
if (uiState.permissionConfirmationRequest) {
|
|
|
|
|
const files = uiState.permissionConfirmationRequest.files;
|
|
|
|
|
const filesList = files.map((f) => `- ${f}`).join('\n');
|
|
|
|
|
return (
|
|
|
|
|
<ConsentPrompt
|
|
|
|
|
prompt={`The following files are outside your workspace:\n\n${filesList}\n\nDo you want to allow this read?`}
|
|
|
|
|
onConfirm={(allowed) => {
|
|
|
|
|
uiState.permissionConfirmationRequest?.onComplete({ allowed });
|
|
|
|
|
}}
|
|
|
|
|
terminalWidth={terminalWidth}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 09:57:34 -05:00
|
|
|
// commandConfirmationRequest and authConsentRequest are kept separate
|
|
|
|
|
// to avoid focus deadlocks and state race conditions between the
|
|
|
|
|
// synchronous command loop and the asynchronous auth flow.
|
|
|
|
|
if (uiState.commandConfirmationRequest) {
|
|
|
|
|
return (
|
|
|
|
|
<ConsentPrompt
|
|
|
|
|
prompt={uiState.commandConfirmationRequest.prompt}
|
|
|
|
|
onConfirm={uiState.commandConfirmationRequest.onConfirm}
|
|
|
|
|
terminalWidth={terminalWidth}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.authConsentRequest) {
|
2025-09-06 01:39:02 -04:00
|
|
|
return (
|
2025-09-29 14:19:19 -07:00
|
|
|
<ConsentPrompt
|
2026-01-30 09:57:34 -05:00
|
|
|
prompt={uiState.authConsentRequest.prompt}
|
|
|
|
|
onConfirm={uiState.authConsentRequest.onConfirm}
|
2025-09-29 14:19:19 -07:00
|
|
|
terminalWidth={terminalWidth}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.confirmUpdateExtensionRequests.length > 0) {
|
|
|
|
|
const request = uiState.confirmUpdateExtensionRequests[0];
|
|
|
|
|
return (
|
|
|
|
|
<ConsentPrompt
|
|
|
|
|
prompt={request.prompt}
|
|
|
|
|
onConfirm={request.onConfirm}
|
|
|
|
|
terminalWidth={terminalWidth}
|
|
|
|
|
/>
|
2025-09-06 01:39:02 -04:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.isThemeDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
{uiState.themeError && (
|
|
|
|
|
<Box marginBottom={1}>
|
2025-09-10 10:57:07 -07:00
|
|
|
<Text color={theme.status.error}>{uiState.themeError}</Text>
|
2025-09-06 01:39:02 -04:00
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
<ThemeDialog
|
|
|
|
|
onSelect={uiActions.handleThemeSelect}
|
2025-10-20 10:50:09 -07:00
|
|
|
onCancel={uiActions.closeThemeDialog}
|
2025-09-06 01:39:02 -04:00
|
|
|
onHighlight={uiActions.handleThemeHighlight}
|
|
|
|
|
settings={settings}
|
|
|
|
|
availableTerminalHeight={
|
|
|
|
|
constrainHeight ? terminalHeight - staticExtraHeight : undefined
|
|
|
|
|
}
|
2026-01-26 15:23:54 -08:00
|
|
|
terminalWidth={uiTerminalWidth}
|
2025-09-06 01:39:02 -04:00
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.isSettingsDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<SettingsDialog
|
|
|
|
|
onSelect={() => uiActions.closeSettingsDialog()}
|
2025-11-21 08:31:47 -08:00
|
|
|
onRestartRequest={async () => {
|
|
|
|
|
await runExitCleanup();
|
|
|
|
|
process.exit(RELAUNCH_EXIT_CODE);
|
|
|
|
|
}}
|
2025-09-18 02:51:46 +09:00
|
|
|
availableTerminalHeight={terminalHeight - staticExtraHeight}
|
2025-09-06 01:39:02 -04:00
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-23 12:50:09 -04:00
|
|
|
if (uiState.isModelDialogOpen) {
|
|
|
|
|
return <ModelDialog onClose={uiActions.closeModelDialog} />;
|
|
|
|
|
}
|
2026-01-23 16:10:51 -08:00
|
|
|
if (
|
|
|
|
|
uiState.isAgentConfigDialogOpen &&
|
|
|
|
|
uiState.selectedAgentName &&
|
|
|
|
|
uiState.selectedAgentDisplayName &&
|
|
|
|
|
uiState.selectedAgentDefinition
|
|
|
|
|
) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<AgentConfigDialog
|
|
|
|
|
agentName={uiState.selectedAgentName}
|
|
|
|
|
displayName={uiState.selectedAgentDisplayName}
|
|
|
|
|
definition={uiState.selectedAgentDefinition}
|
|
|
|
|
settings={settings}
|
|
|
|
|
onClose={uiActions.closeAgentConfigDialog}
|
|
|
|
|
onSave={async () => {
|
|
|
|
|
// Reload agent registry to pick up changes
|
|
|
|
|
const agentRegistry = config?.getAgentRegistry();
|
|
|
|
|
if (agentRegistry) {
|
|
|
|
|
await agentRegistry.reload();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-27 10:18:16 -08:00
|
|
|
if (uiState.accountSuspensionInfo) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<BannedAccountDialog
|
|
|
|
|
accountSuspensionInfo={uiState.accountSuspensionInfo}
|
|
|
|
|
onExit={() => {
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}}
|
|
|
|
|
onChangeAuth={() => {
|
|
|
|
|
uiActions.clearAccountSuspension();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-06 01:39:02 -04:00
|
|
|
if (uiState.isAuthenticating) {
|
|
|
|
|
return (
|
|
|
|
|
<AuthInProgress
|
|
|
|
|
onTimeout={() => {
|
2025-09-06 10:34:00 -07:00
|
|
|
uiActions.onAuthError('Authentication cancelled.');
|
2025-09-06 01:39:02 -04:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-29 18:58:08 -07:00
|
|
|
if (uiState.isAwaitingApiKeyInput) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<ApiAuthDialog
|
2025-12-12 17:50:21 -08:00
|
|
|
key={uiState.apiKeyDefaultValue}
|
2025-10-29 18:58:08 -07:00
|
|
|
onSubmit={uiActions.handleApiKeySubmit}
|
|
|
|
|
onCancel={uiActions.handleApiKeyCancel}
|
|
|
|
|
error={uiState.authError}
|
|
|
|
|
defaultValue={uiState.apiKeyDefaultValue}
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-27 10:18:16 -08:00
|
|
|
|
2025-09-06 01:39:02 -04:00
|
|
|
if (uiState.isAuthDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<AuthDialog
|
|
|
|
|
config={config}
|
|
|
|
|
settings={settings}
|
|
|
|
|
setAuthState={uiActions.setAuthState}
|
|
|
|
|
authError={uiState.authError}
|
|
|
|
|
onAuthError={uiActions.onAuthError}
|
2026-01-12 15:39:08 -05:00
|
|
|
setAuthContext={uiActions.setAuthContext}
|
2025-09-06 01:39:02 -04:00
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.isEditorDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
{uiState.editorError && (
|
|
|
|
|
<Box marginBottom={1}>
|
2025-09-10 10:57:07 -07:00
|
|
|
<Text color={theme.status.error}>{uiState.editorError}</Text>
|
2025-09-06 01:39:02 -04:00
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
<EditorSettingsDialog
|
|
|
|
|
onSelect={uiActions.handleEditorSelect}
|
|
|
|
|
settings={settings}
|
|
|
|
|
onExit={uiActions.exitEditorDialog}
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (uiState.showPrivacyNotice) {
|
|
|
|
|
return (
|
|
|
|
|
<PrivacyNotice
|
|
|
|
|
onExit={() => uiActions.exitPrivacyNotice()}
|
|
|
|
|
config={config}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-11-25 11:54:09 -07:00
|
|
|
if (uiState.isSessionBrowserOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<SessionBrowser
|
|
|
|
|
config={config}
|
|
|
|
|
onResumeSession={uiActions.handleResumeSession}
|
|
|
|
|
onDeleteSession={uiActions.handleDeleteSession}
|
|
|
|
|
onExit={uiActions.closeSessionBrowser}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-06 01:39:02 -04:00
|
|
|
|
2025-09-22 11:45:02 -07:00
|
|
|
if (uiState.isPermissionsDialogOpen) {
|
|
|
|
|
return (
|
|
|
|
|
<PermissionsModifyTrustDialog
|
|
|
|
|
onExit={uiActions.closePermissionsDialog}
|
|
|
|
|
addItem={addItem}
|
2025-11-14 14:41:53 -08:00
|
|
|
targetDirectory={uiState.permissionsDialogProps?.targetDirectory}
|
2025-09-22 11:45:02 -07:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-06 01:39:02 -04:00
|
|
|
return null;
|
|
|
|
|
};
|