@vybestack/llxprt-code 0.10.0 → 0.11.0-nightly.260728.ffaa8d5d7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/commands/mcp/add.ts +8 -4
- package/src/config/cliArgParser.ts +2 -0
- package/src/config/config.ts +2 -0
- package/src/config/configBuilder.ts +3 -0
- package/src/config/configError.ts +23 -0
- package/src/config/extensions/extensionEnablement.ts +62 -1
- package/src/config/interactiveContext.ts +2 -1
- package/src/config/intermediateConfig.ts +4 -0
- package/src/config/keyBindings.ts +29 -3
- package/src/config/policy.ts +7 -1
- package/src/config/postConfigRuntime.ts +20 -14
- package/src/config/settings-schema/schema-ui.ts +9 -0
- package/src/config/settingsLoader.ts +21 -17
- package/src/config/settingsSchema.ts +2 -2
- package/src/config/trustedFolders.ts +275 -58
- package/src/config/yargsOptions.ts +7 -0
- package/src/generated/git-commit.json +1 -1
- package/src/nonInteractiveCli.ts +1 -0
- package/src/nonInteractiveCliSupport.ts +171 -51
- package/src/runtime/interactiveToolScheduler.ts +6 -6
- package/src/services/BuiltinCommandLoader.ts +33 -25
- package/src/services/FileCommandLoader.ts +34 -7
- package/src/test-utils/async.ts +20 -0
- package/src/test-utils/render.tsx +10 -1
- package/src/ui/App.tsx +17 -10
- package/src/ui/AppContainerRuntime.tsx +7 -9
- package/src/ui/cliUiRuntime.ts +28 -0
- package/src/ui/commands/authCommand.ts +240 -3
- package/src/ui/commands/compressCommand.ts +9 -1
- package/src/ui/commands/directoryCommand.tsx +1 -1
- package/src/ui/commands/logoutCommand.ts +2 -2
- package/src/ui/commands/permissionsCommand.ts +77 -20
- package/src/ui/commands/policiesCommand.ts +6 -1
- package/src/ui/commands/quotaCommand.ts +501 -0
- package/src/ui/commands/statsCommand.ts +2 -2
- package/src/ui/commands/statsQuota.ts +1 -1
- package/src/ui/commands/subagentCommand.ts +59 -27
- package/src/ui/components/AuthDialog.tsx +3 -3
- package/src/ui/components/Composer.tsx +1 -0
- package/src/ui/components/DialogManager.tsx +9 -2
- package/src/ui/components/FolderTrustDialog.tsx +71 -60
- package/src/ui/components/PermissionsModifyTrustDialog.tsx +211 -146
- package/src/ui/components/ProfileCreateWizard/constants.ts +13 -1
- package/src/ui/components/QueuedMessagesPanel.tsx +388 -0
- package/src/ui/components/inputPromptHooks.ts +2 -0
- package/src/ui/components/inputPromptKeyHandlers.ts +14 -0
- package/src/ui/components/inputPromptTypes.ts +6 -0
- package/src/ui/components/messages/AiMessage.tsx +24 -12
- package/src/ui/components/messages/ToolMessage.tsx +26 -40
- package/src/ui/containers/AppContainer/builders/buildUIActions.ts +3 -1
- package/src/ui/containers/AppContainer/builders/buildUIState.ts +5 -4
- package/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts +3 -33
- package/src/ui/containers/AppContainer/hooks/useAppDialogs.ts +2 -14
- package/src/ui/containers/AppContainer/hooks/useAppInput.ts +31 -25
- package/src/ui/containers/AppContainer/hooks/useAppLayout.ts +90 -60
- package/src/ui/containers/AppContainer/hooks/useDisplayPreferences.ts +10 -0
- package/src/ui/containers/AppContainer/hooks/useInputHandling.ts +3 -17
- package/src/ui/containers/AppContainer/hooks/useKeybindings.ts +51 -38
- package/src/ui/containers/AppContainer/hooks/useSteer.ts +51 -0
- package/src/ui/contexts/KeypressContext.tsx +13 -2
- package/src/ui/contexts/ShellCommandDisplayContext.tsx +103 -0
- package/src/ui/contexts/TodoContext.tsx +0 -13
- package/src/ui/contexts/TodoProvider.tsx +1 -34
- package/src/ui/contexts/UIActionsContext.tsx +2 -1
- package/src/ui/contexts/UIStateContext.tsx +3 -2
- package/src/ui/hooks/agentStream/streamUtils.ts +0 -24
- package/src/ui/hooks/agentStream/thoughtState.ts +110 -30
- package/src/ui/hooks/agentStream/types.ts +1 -0
- package/src/ui/hooks/agentStream/useAgentEventStream.ts +8 -17
- package/src/ui/hooks/agentStream/useAgentStream.ts +2 -2
- package/src/ui/hooks/agentStream/useAgentStreamLifecycle.ts +4 -5
- package/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts +12 -5
- package/src/ui/hooks/agentStream/useQueuedSubmissions.ts +99 -0
- package/src/ui/hooks/agentStream/useStreamEventHandlers.ts +4 -13
- package/src/ui/hooks/agentStream/useStreamState.ts +60 -41
- package/src/ui/hooks/agentStream/useSubmitQuery.ts +284 -59
- package/src/ui/hooks/slashCommandProcessorSupport.ts +4 -0
- package/src/ui/hooks/useAgentStream-test-helpers.ts +1 -0
- package/src/ui/hooks/useAutoAcceptIndicator.ts +13 -0
- package/src/ui/hooks/useFolderTrust.ts +118 -81
- package/src/ui/hooks/useGitBranchName.ts +79 -33
- package/src/ui/hooks/useIdeTrustListener.ts +5 -19
- package/src/ui/hooks/usePermissionsModifyTrust.ts +223 -85
- package/src/ui/hooks/useReactToolScheduler.ts +13 -17
- package/src/ui/hooks/useTodoPausePreserver.ts +0 -55
- package/src/ui/layouts/DefaultAppLayout.tsx +2 -0
- package/src/ui/layouts/DefaultAppLayoutHelpers.tsx +37 -162
- package/src/ui/layouts/InlineContent.tsx +198 -0
- package/src/ui/trustDialogHelpers.ts +190 -0
- package/src/utils/cleanup-state.ts +115 -0
- package/src/utils/cleanup.ts +32 -34
- package/src/utils/sandbox-containers.ts +183 -53
- package/src/utils/sandbox-exec.ts +9 -3
- package/src/zed-integration/acp-terminal-shell-host.ts +112 -0
- package/src/zed-integration/acp-types.ts +35 -0
- package/src/zed-integration/fileSystemService.ts +1 -1
- package/src/zed-integration/runZedIntegration.ts +142 -0
- package/src/zed-integration/zed-agent-event-handler.ts +179 -0
- package/src/zed-integration/zed-agent-setup.ts +43 -0
- package/src/zed-integration/zed-command-registry.ts +167 -0
- package/src/zed-integration/zed-config-options.ts +280 -0
- package/src/zed-integration/zed-content-utils.ts +2 -2
- package/src/zed-integration/zed-helpers.ts +132 -0
- package/src/zed-integration/zed-initialize.ts +64 -0
- package/src/zed-integration/zed-plan-update.ts +20 -0
- package/src/zed-integration/zed-prompt-command.ts +77 -0
- package/src/zed-integration/zed-session-config.ts +119 -0
- package/src/zed-integration/zed-session-errors.ts +337 -0
- package/src/zed-integration/zed-session-events.ts +216 -0
- package/src/zed-integration/zed-session-info.ts +255 -0
- package/src/zed-integration/zed-session-lifecycle.ts +186 -0
- package/src/zed-integration/zed-session-listing.ts +153 -0
- package/src/zed-integration/zed-session-loader.ts +193 -0
- package/src/zed-integration/zed-session-pagination.ts +240 -0
- package/src/zed-integration/zed-session-replay.ts +564 -0
- package/src/zed-integration/zed-stream-batcher.ts +236 -0
- package/src/zed-integration/zed-terminal-manager.ts +416 -0
- package/src/zed-integration/zed-terminal-setup.ts +72 -0
- package/src/zed-integration/zed-test-helpers.ts +349 -4
- package/src/zed-integration/zed-tool-handler.ts +47 -5
- package/src/zed-integration/zedIntegration.ts +538 -587
- package/src/services/todo-continuation/todoContinuationService.ts +0 -610
- package/src/ui/containers/AppContainer/hooks/useIdeRestartHotkey.ts +0 -33
- package/src/ui/containers/AppContainer/hooks/useTodoContinuationFlow.ts +0 -160
- package/src/ui/hooks/useTodoContinuation.ts +0 -299
|
@@ -5,22 +5,25 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from 'react';
|
|
8
|
-
import { Box
|
|
8
|
+
import { Box } from 'ink';
|
|
9
9
|
import type {
|
|
10
10
|
MessageBus,
|
|
11
11
|
IdeContext,
|
|
12
12
|
ThoughtSummary,
|
|
13
|
+
ApprovalMode,
|
|
13
14
|
} from '@vybestack/llxprt-code-core';
|
|
14
|
-
import {
|
|
15
|
-
|
|
15
|
+
import type {
|
|
16
|
+
StreamingState,
|
|
17
|
+
HistoryItem,
|
|
18
|
+
ConsoleMessageItem,
|
|
19
|
+
} from '../types.js';
|
|
16
20
|
import type { SlashCommandRuntime, UiRuntime } from '../cliUiRuntime.js';
|
|
17
|
-
import type {
|
|
21
|
+
import type { QueuedSubmission } from '../hooks/agentStream/types.js';
|
|
18
22
|
import type { LoadedSettings } from '../../config/settings.js';
|
|
19
23
|
import type { UpdateObject } from '../utils/updateCheck.js';
|
|
20
24
|
import type { UIState } from '../contexts/UIStateContext.js';
|
|
21
25
|
import type { UIActions } from '../contexts/UIActionsContext.js';
|
|
22
26
|
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
|
23
|
-
import { Colors } from '../colors.js';
|
|
24
27
|
import { getCliRuntimeContext } from '@vybestack/llxprt-code-providers/runtime.js';
|
|
25
28
|
import { themeManager } from '../themes/theme-manager.js';
|
|
26
29
|
import type { SlashCommand } from '../commands/types.js';
|
|
@@ -30,15 +33,11 @@ import { HistoryItemDisplay } from '../components/HistoryItemDisplay.js';
|
|
|
30
33
|
import { ShowMoreLines } from '../components/ShowMoreLines.js';
|
|
31
34
|
import { Notifications } from '../components/Notifications.js';
|
|
32
35
|
import { TodoPanel } from '../components/TodoPanel.js';
|
|
36
|
+
import { QueuedMessagesPanel } from '../components/QueuedMessagesPanel.js';
|
|
33
37
|
import { Footer } from '../components/Footer.js';
|
|
34
38
|
import { DialogManager } from '../components/DialogManager.js';
|
|
35
39
|
import { BucketAuthConfirmation } from '../components/BucketAuthConfirmation.js';
|
|
36
|
-
import {
|
|
37
|
-
import { LoadingIndicator } from '../components/LoadingIndicator.js';
|
|
38
|
-
import { AutoAcceptIndicator } from '../components/AutoAcceptIndicator.js';
|
|
39
|
-
import { ShellModeIndicator } from '../components/ShellModeIndicator.js';
|
|
40
|
-
import { ContextSummaryDisplay } from '../components/ContextSummaryDisplay.js';
|
|
41
|
-
import { DetailedMessagesDisplay } from '../components/DetailedMessagesDisplay.js';
|
|
40
|
+
import { InlineContent } from './InlineContent.js';
|
|
42
41
|
|
|
43
42
|
export type { ScrollableMainContentItem } from './scrollableMainContent.js';
|
|
44
43
|
export {
|
|
@@ -52,7 +51,6 @@ export function hasActiveDialog(uiState: UIState): boolean {
|
|
|
52
51
|
const dialogFlags = [
|
|
53
52
|
uiState.showWorkspaceMigrationDialog,
|
|
54
53
|
uiState.shouldShowIdePrompt,
|
|
55
|
-
uiState.showIdeRestartPrompt,
|
|
56
54
|
uiState.isFolderTrustDialogOpen,
|
|
57
55
|
uiState.isWelcomeDialogOpen,
|
|
58
56
|
uiState.isPermissionsDialogOpen,
|
|
@@ -516,6 +514,8 @@ export interface MainControlsProps {
|
|
|
516
514
|
history: HistoryItem[];
|
|
517
515
|
inputWidth: number;
|
|
518
516
|
isTodoPanelCollapsed: boolean;
|
|
517
|
+
isQueuedMessagesPanelCollapsed: boolean;
|
|
518
|
+
queuedSubmissions: readonly QueuedSubmission[];
|
|
519
519
|
showTodoPanelSetting: boolean;
|
|
520
520
|
dialogsVisible: boolean;
|
|
521
521
|
hideContextSummary: boolean;
|
|
@@ -575,6 +575,11 @@ export function MainControls(props: MainControlsProps) {
|
|
|
575
575
|
inputWidth={props.inputWidth}
|
|
576
576
|
isTodoPanelCollapsed={props.isTodoPanelCollapsed}
|
|
577
577
|
/>
|
|
578
|
+
<QueuedMessagesPanelSection
|
|
579
|
+
inputWidth={props.inputWidth}
|
|
580
|
+
isQueuedMessagesPanelCollapsed={props.isQueuedMessagesPanelCollapsed}
|
|
581
|
+
queuedSubmissions={props.queuedSubmissions}
|
|
582
|
+
/>
|
|
578
583
|
<BucketAuthSection dialogsVisible={dialogsVisible} />
|
|
579
584
|
{dialogsVisible ? (
|
|
580
585
|
<DialogManager
|
|
@@ -634,164 +639,34 @@ function TodoPanelSection({
|
|
|
634
639
|
return <TodoPanel width={inputWidth} collapsed={isTodoPanelCollapsed} />;
|
|
635
640
|
}
|
|
636
641
|
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
isFocused={!dialogsVisible}
|
|
644
|
-
/>
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
export interface InlineContentProps {
|
|
649
|
-
streamingState: StreamingState;
|
|
650
|
-
disableLoadingPhrases: boolean;
|
|
651
|
-
thought: ThoughtSummary | null;
|
|
652
|
-
currentLoadingPhrase: string | undefined;
|
|
653
|
-
elapsedTime: number;
|
|
654
|
-
hideContextSummary: boolean;
|
|
655
|
-
isNarrow: boolean;
|
|
656
|
-
ctrlCPressedOnce: boolean;
|
|
657
|
-
ctrlDPressedOnce: boolean;
|
|
658
|
-
showEscapePrompt: boolean;
|
|
659
|
-
ideContextState: IdeContext | undefined;
|
|
660
|
-
llxprtMdFileCount: number;
|
|
661
|
-
coreMemoryFileCount: number;
|
|
662
|
-
contextFileNames: string[];
|
|
663
|
-
config: SlashCommandRuntime;
|
|
664
|
-
showToolDescriptions: boolean;
|
|
665
|
-
showAutoAcceptIndicator: ApprovalMode;
|
|
666
|
-
shellModeActive: boolean;
|
|
667
|
-
showErrorDetails: boolean;
|
|
668
|
-
consoleMessages: ConsoleMessageItem[];
|
|
669
|
-
constrainHeight: boolean;
|
|
670
|
-
debugConsoleMaxHeight: number;
|
|
642
|
+
function QueuedMessagesPanelSection({
|
|
643
|
+
inputWidth,
|
|
644
|
+
isQueuedMessagesPanelCollapsed,
|
|
645
|
+
queuedSubmissions,
|
|
646
|
+
}: {
|
|
671
647
|
inputWidth: number;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
export function InlineContent(props: InlineContentProps) {
|
|
678
|
-
return (
|
|
679
|
-
<>
|
|
680
|
-
<LoadingIndicator
|
|
681
|
-
thought={
|
|
682
|
-
props.streamingState === StreamingState.WaitingForConfirmation ||
|
|
683
|
-
props.disableLoadingPhrases
|
|
684
|
-
? undefined
|
|
685
|
-
: props.thought
|
|
686
|
-
}
|
|
687
|
-
currentLoadingPhrase={
|
|
688
|
-
props.disableLoadingPhrases ? undefined : props.currentLoadingPhrase
|
|
689
|
-
}
|
|
690
|
-
elapsedTime={props.elapsedTime}
|
|
691
|
-
/>
|
|
692
|
-
<StatusBar {...props} />
|
|
693
|
-
<ErrorConsoleSection {...props} />
|
|
694
|
-
<ComposerSection {...props} />
|
|
695
|
-
</>
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function StatusBar(props: InlineContentProps) {
|
|
700
|
-
return (
|
|
701
|
-
<Box
|
|
702
|
-
marginTop={1}
|
|
703
|
-
display="flex"
|
|
704
|
-
justifyContent={props.hideContextSummary ? 'flex-start' : 'space-between'}
|
|
705
|
-
width="100%"
|
|
706
|
-
>
|
|
707
|
-
<StatusBarLeft {...props} />
|
|
708
|
-
<StatusBarRight {...props} />
|
|
709
|
-
</Box>
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
function StatusBarLeftPrompt(props: InlineContentProps) {
|
|
714
|
-
const transientPrompt = [
|
|
715
|
-
{ active: props.ctrlCPressedOnce, text: 'Press Ctrl+C again to exit.' },
|
|
716
|
-
{ active: props.ctrlDPressedOnce, text: 'Press Ctrl+D again to exit.' },
|
|
717
|
-
].find((entry) => entry.active);
|
|
718
|
-
if (transientPrompt) {
|
|
719
|
-
return <Text color={Colors.AccentYellow}>{transientPrompt.text}</Text>;
|
|
720
|
-
}
|
|
721
|
-
if (props.showEscapePrompt) {
|
|
722
|
-
return <Text color={Colors.Gray}>Press Esc again to clear.</Text>;
|
|
723
|
-
}
|
|
724
|
-
if (!props.hideContextSummary) {
|
|
725
|
-
return (
|
|
726
|
-
<ContextSummaryDisplay
|
|
727
|
-
ideContext={props.ideContextState}
|
|
728
|
-
llxprtMdFileCount={props.llxprtMdFileCount}
|
|
729
|
-
coreMemoryFileCount={props.coreMemoryFileCount}
|
|
730
|
-
contextFileNames={props.contextFileNames}
|
|
731
|
-
mcpServers={props.config.getMcpServers()}
|
|
732
|
-
blockedMcpServers={props.config.getBlockedMcpServers()}
|
|
733
|
-
showToolDescriptions={props.showToolDescriptions}
|
|
734
|
-
/>
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
return null;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
function StatusBarLeft(props: InlineContentProps) {
|
|
741
|
-
return (
|
|
742
|
-
<Box>
|
|
743
|
-
{process.env.GEMINI_SYSTEM_MD && (
|
|
744
|
-
<Text color={Colors.AccentRed}>|⌐■_■| </Text>
|
|
745
|
-
)}
|
|
746
|
-
<StatusBarLeftPrompt {...props} />
|
|
747
|
-
</Box>
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function StatusBarRight(props: InlineContentProps) {
|
|
752
|
-
return (
|
|
753
|
-
<Box
|
|
754
|
-
paddingTop={props.isNarrow ? 1 : 0}
|
|
755
|
-
marginLeft={props.hideContextSummary ? 1 : 2}
|
|
756
|
-
>
|
|
757
|
-
{props.showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
|
|
758
|
-
!props.shellModeActive && (
|
|
759
|
-
<AutoAcceptIndicator approvalMode={props.showAutoAcceptIndicator} />
|
|
760
|
-
)}
|
|
761
|
-
{props.shellModeActive && <ShellModeIndicator />}
|
|
762
|
-
</Box>
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function ErrorConsoleSection(props: InlineContentProps) {
|
|
767
|
-
if (!props.showErrorDetails) {
|
|
648
|
+
isQueuedMessagesPanelCollapsed: boolean;
|
|
649
|
+
queuedSubmissions: readonly QueuedSubmission[];
|
|
650
|
+
}) {
|
|
651
|
+
if (queuedSubmissions.length === 0) {
|
|
768
652
|
return null;
|
|
769
653
|
}
|
|
770
654
|
return (
|
|
771
|
-
<
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
props.constrainHeight ? props.debugConsoleMaxHeight : undefined
|
|
777
|
-
}
|
|
778
|
-
width={props.inputWidth}
|
|
779
|
-
/>
|
|
780
|
-
<ShowMoreLines constrainHeight={props.constrainHeight} />
|
|
781
|
-
</Box>
|
|
782
|
-
</OverflowProvider>
|
|
655
|
+
<QueuedMessagesPanel
|
|
656
|
+
width={inputWidth}
|
|
657
|
+
collapsed={isQueuedMessagesPanelCollapsed}
|
|
658
|
+
messages={queuedSubmissions}
|
|
659
|
+
/>
|
|
783
660
|
);
|
|
784
661
|
}
|
|
785
662
|
|
|
786
|
-
function
|
|
787
|
-
if (!props.isInputActive) {
|
|
788
|
-
return null;
|
|
789
|
-
}
|
|
663
|
+
function BucketAuthSection({ dialogsVisible }: { dialogsVisible: boolean }) {
|
|
790
664
|
return (
|
|
791
|
-
<
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
665
|
+
<BucketAuthConfirmation
|
|
666
|
+
messageBus={
|
|
667
|
+
(getCliRuntimeContext() as { messageBus?: MessageBus }).messageBus
|
|
668
|
+
}
|
|
669
|
+
isFocused={!dialogsVisible}
|
|
795
670
|
/>
|
|
796
671
|
);
|
|
797
672
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Box, Text } from 'ink';
|
|
8
|
+
import type { IdeContext, ThoughtSummary } from '@vybestack/llxprt-code-core';
|
|
9
|
+
import { ApprovalMode } from '@vybestack/llxprt-code-core';
|
|
10
|
+
import { StreamingState } from '../types.js';
|
|
11
|
+
import type { ConsoleMessageItem } from '../types.js';
|
|
12
|
+
import type { LoadedSettings } from '../../config/settings.js';
|
|
13
|
+
import type { SlashCommandRuntime } from '../cliUiRuntime.js';
|
|
14
|
+
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
|
15
|
+
import { Colors } from '../colors.js';
|
|
16
|
+
import { ShowMoreLines } from '../components/ShowMoreLines.js';
|
|
17
|
+
import { LoadingIndicator } from '../components/LoadingIndicator.js';
|
|
18
|
+
import { AutoAcceptIndicator } from '../components/AutoAcceptIndicator.js';
|
|
19
|
+
import { ShellModeIndicator } from '../components/ShellModeIndicator.js';
|
|
20
|
+
import { ContextSummaryDisplay } from '../components/ContextSummaryDisplay.js';
|
|
21
|
+
import { DetailedMessagesDisplay } from '../components/DetailedMessagesDisplay.js';
|
|
22
|
+
import { Composer } from '../components/Composer.js';
|
|
23
|
+
|
|
24
|
+
const CTRL_C_EXIT_PROMPT = 'Press Ctrl+C again to exit.';
|
|
25
|
+
const CTRL_D_EXIT_PROMPT = 'Press Ctrl+D again to exit.';
|
|
26
|
+
const ESCAPE_CLEAR_PROMPT = 'Press Esc again to clear.';
|
|
27
|
+
const SYSTEM_MD_ENVIRONMENT_VARIABLE = 'GEMINI_SYSTEM_MD';
|
|
28
|
+
|
|
29
|
+
function isSystemMdEnabled(): boolean {
|
|
30
|
+
return Boolean(process.env[SYSTEM_MD_ENVIRONMENT_VARIABLE]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface InlineContentProps {
|
|
34
|
+
streamingState: StreamingState;
|
|
35
|
+
disableLoadingPhrases: boolean;
|
|
36
|
+
thought: ThoughtSummary | null;
|
|
37
|
+
currentLoadingPhrase: string | undefined;
|
|
38
|
+
elapsedTime: number;
|
|
39
|
+
hideContextSummary: boolean;
|
|
40
|
+
isNarrow: boolean;
|
|
41
|
+
ctrlCPressedOnce: boolean;
|
|
42
|
+
ctrlDPressedOnce: boolean;
|
|
43
|
+
showEscapePrompt: boolean;
|
|
44
|
+
ideContextState: IdeContext | undefined;
|
|
45
|
+
llxprtMdFileCount: number;
|
|
46
|
+
coreMemoryFileCount: number;
|
|
47
|
+
contextFileNames: string[];
|
|
48
|
+
config: SlashCommandRuntime;
|
|
49
|
+
showToolDescriptions: boolean;
|
|
50
|
+
showAutoAcceptIndicator: ApprovalMode;
|
|
51
|
+
shellModeActive: boolean;
|
|
52
|
+
showErrorDetails: boolean;
|
|
53
|
+
consoleMessages: ConsoleMessageItem[];
|
|
54
|
+
constrainHeight: boolean;
|
|
55
|
+
debugConsoleMaxHeight: number;
|
|
56
|
+
inputWidth: number;
|
|
57
|
+
isInputActive: boolean;
|
|
58
|
+
settings: LoadedSettings;
|
|
59
|
+
onSuggestionsVisibilityChange: (visible: boolean) => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function InlineContent(props: InlineContentProps) {
|
|
63
|
+
const shouldHideThought =
|
|
64
|
+
props.streamingState === StreamingState.WaitingForConfirmation ||
|
|
65
|
+
props.disableLoadingPhrases;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<LoadingIndicator
|
|
70
|
+
thought={shouldHideThought ? undefined : props.thought}
|
|
71
|
+
currentLoadingPhrase={
|
|
72
|
+
props.disableLoadingPhrases ? undefined : props.currentLoadingPhrase
|
|
73
|
+
}
|
|
74
|
+
elapsedTime={props.elapsedTime}
|
|
75
|
+
/>
|
|
76
|
+
<StatusBar {...props} />
|
|
77
|
+
<ErrorConsoleSection {...props} />
|
|
78
|
+
<ComposerSection {...props} />
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function StatusBar(props: InlineContentProps) {
|
|
84
|
+
return (
|
|
85
|
+
<Box
|
|
86
|
+
marginTop={1}
|
|
87
|
+
display="flex"
|
|
88
|
+
justifyContent={props.hideContextSummary ? 'flex-start' : 'space-between'}
|
|
89
|
+
width="100%"
|
|
90
|
+
>
|
|
91
|
+
<StatusBarLeft {...props} />
|
|
92
|
+
<StatusBarRight {...props} />
|
|
93
|
+
</Box>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function StatusBarLeftPrompt(props: InlineContentProps) {
|
|
98
|
+
// Exit confirmations take precedence over clearing feedback and context status.
|
|
99
|
+
if (props.ctrlCPressedOnce) {
|
|
100
|
+
return <Text color={Colors.AccentYellow}>{CTRL_C_EXIT_PROMPT}</Text>;
|
|
101
|
+
}
|
|
102
|
+
if (props.ctrlDPressedOnce) {
|
|
103
|
+
return <Text color={Colors.AccentYellow}>{CTRL_D_EXIT_PROMPT}</Text>;
|
|
104
|
+
}
|
|
105
|
+
if (props.showEscapePrompt) {
|
|
106
|
+
return <Text color={Colors.Gray}>{ESCAPE_CLEAR_PROMPT}</Text>;
|
|
107
|
+
}
|
|
108
|
+
if (!props.hideContextSummary) {
|
|
109
|
+
return (
|
|
110
|
+
<ContextSummaryDisplay
|
|
111
|
+
ideContext={props.ideContextState}
|
|
112
|
+
llxprtMdFileCount={props.llxprtMdFileCount}
|
|
113
|
+
coreMemoryFileCount={props.coreMemoryFileCount}
|
|
114
|
+
contextFileNames={props.contextFileNames}
|
|
115
|
+
mcpServers={props.config.getMcpServers()}
|
|
116
|
+
blockedMcpServers={props.config.getBlockedMcpServers()}
|
|
117
|
+
showToolDescriptions={props.showToolDescriptions}
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function StatusBarLeft(props: InlineContentProps) {
|
|
125
|
+
const showSystemMdIndicator = isSystemMdEnabled();
|
|
126
|
+
const showPrompt =
|
|
127
|
+
props.ctrlCPressedOnce ||
|
|
128
|
+
props.ctrlDPressedOnce ||
|
|
129
|
+
props.showEscapePrompt ||
|
|
130
|
+
!props.hideContextSummary;
|
|
131
|
+
if (!showSystemMdIndicator && !showPrompt) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<Box>
|
|
137
|
+
{showSystemMdIndicator && (
|
|
138
|
+
<Text color={Colors.AccentRed}>|⌐■_■| </Text>
|
|
139
|
+
)}
|
|
140
|
+
<StatusBarLeftPrompt {...props} />
|
|
141
|
+
</Box>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function StatusBarRight(props: InlineContentProps) {
|
|
146
|
+
if (
|
|
147
|
+
props.showAutoAcceptIndicator === ApprovalMode.DEFAULT &&
|
|
148
|
+
!props.shellModeActive
|
|
149
|
+
) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const shouldShowAutoAcceptIndicator = !props.shellModeActive;
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Box
|
|
156
|
+
paddingTop={props.isNarrow ? 1 : 0}
|
|
157
|
+
marginLeft={props.hideContextSummary ? 1 : 2}
|
|
158
|
+
>
|
|
159
|
+
{shouldShowAutoAcceptIndicator && (
|
|
160
|
+
<AutoAcceptIndicator approvalMode={props.showAutoAcceptIndicator} />
|
|
161
|
+
)}
|
|
162
|
+
{props.shellModeActive && <ShellModeIndicator />}
|
|
163
|
+
</Box>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function ErrorConsoleSection(props: InlineContentProps) {
|
|
168
|
+
if (!props.showErrorDetails) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return (
|
|
172
|
+
<OverflowProvider>
|
|
173
|
+
<Box flexDirection="column">
|
|
174
|
+
<DetailedMessagesDisplay
|
|
175
|
+
messages={props.consoleMessages}
|
|
176
|
+
maxHeight={
|
|
177
|
+
props.constrainHeight ? props.debugConsoleMaxHeight : undefined
|
|
178
|
+
}
|
|
179
|
+
width={props.inputWidth}
|
|
180
|
+
/>
|
|
181
|
+
<ShowMoreLines constrainHeight={props.constrainHeight} />
|
|
182
|
+
</Box>
|
|
183
|
+
</OverflowProvider>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function ComposerSection(props: InlineContentProps) {
|
|
188
|
+
if (!props.isInputActive) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return (
|
|
192
|
+
<Composer
|
|
193
|
+
config={props.config}
|
|
194
|
+
settings={props.settings}
|
|
195
|
+
onSuggestionsVisibilityChange={props.onSuggestionsVisibilityChange}
|
|
196
|
+
/>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { TrustLevel } from '../config/trustedFolders.js';
|
|
8
|
+
import type { RadioSelectItem } from './components/shared/RadioButtonSelect.js';
|
|
9
|
+
|
|
10
|
+
export enum FolderTrustChoice {
|
|
11
|
+
TRUST_FOLDER = 'trust_folder',
|
|
12
|
+
TRUST_PARENT = 'trust_parent',
|
|
13
|
+
DO_NOT_TRUST = 'do_not_trust',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function buildTrustRadioOptions<T extends string>(
|
|
17
|
+
currentFolder: string,
|
|
18
|
+
parentFolder: string,
|
|
19
|
+
values: {
|
|
20
|
+
readonly trustFolder: T;
|
|
21
|
+
readonly trustParent: T;
|
|
22
|
+
readonly doNotTrust: T;
|
|
23
|
+
},
|
|
24
|
+
): Array<RadioSelectItem<T>> {
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
label: `Trust folder (${currentFolder})`,
|
|
28
|
+
value: values.trustFolder,
|
|
29
|
+
key: values.trustFolder,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: `Trust parent folder (${parentFolder})`,
|
|
33
|
+
value: values.trustParent,
|
|
34
|
+
key: values.trustParent,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "Don't trust",
|
|
38
|
+
value: values.doNotTrust,
|
|
39
|
+
key: values.doNotTrust,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildTrustOptions(
|
|
45
|
+
currentFolder: string,
|
|
46
|
+
parentFolder: string,
|
|
47
|
+
): Array<RadioSelectItem<FolderTrustChoice>> {
|
|
48
|
+
return buildTrustRadioOptions(currentFolder, parentFolder, {
|
|
49
|
+
trustFolder: FolderTrustChoice.TRUST_FOLDER,
|
|
50
|
+
trustParent: FolderTrustChoice.TRUST_PARENT,
|
|
51
|
+
doNotTrust: FolderTrustChoice.DO_NOT_TRUST,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getLocalTrustLevelDisplay(
|
|
56
|
+
level: TrustLevel | undefined,
|
|
57
|
+
): string {
|
|
58
|
+
switch (level) {
|
|
59
|
+
case TrustLevel.TRUST_FOLDER:
|
|
60
|
+
return 'Trusted';
|
|
61
|
+
case TrustLevel.TRUST_PARENT:
|
|
62
|
+
return 'Trust parent';
|
|
63
|
+
case TrustLevel.DO_NOT_TRUST:
|
|
64
|
+
return 'Not trusted';
|
|
65
|
+
default:
|
|
66
|
+
return 'Not set';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getTrustLevelDisplay(
|
|
71
|
+
level: TrustLevel | undefined,
|
|
72
|
+
isIdeTrusted: boolean | undefined,
|
|
73
|
+
isParentTrusted: boolean | undefined,
|
|
74
|
+
): string {
|
|
75
|
+
if (isIdeTrusted !== undefined) {
|
|
76
|
+
return isIdeTrusted ? 'Trusted (via IDE)' : 'Not trusted (via IDE)';
|
|
77
|
+
}
|
|
78
|
+
const localDisplay = getLocalTrustLevelDisplay(level);
|
|
79
|
+
if (isParentTrusted === true) {
|
|
80
|
+
return level === undefined
|
|
81
|
+
? 'Trusted (via parent folder)'
|
|
82
|
+
: `${localDisplay} (via parent folder)`;
|
|
83
|
+
}
|
|
84
|
+
return localDisplay;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function getWarningMessage(
|
|
88
|
+
isIdeTrusted: boolean | undefined,
|
|
89
|
+
isParentTrusted: boolean | undefined,
|
|
90
|
+
currentTrustLevel: TrustLevel | undefined,
|
|
91
|
+
): string | null {
|
|
92
|
+
if (isIdeTrusted !== undefined) {
|
|
93
|
+
const status = isIdeTrusted ? 'trusted' : 'not trusted';
|
|
94
|
+
return `This folder is ${status} via your IDE settings. Changes here save a local fallback for use without the IDE.`;
|
|
95
|
+
}
|
|
96
|
+
if (isParentTrusted === true) {
|
|
97
|
+
if (currentTrustLevel === TrustLevel.DO_NOT_TRUST) {
|
|
98
|
+
return 'This folder is not trusted because a parent folder rule denies trust.';
|
|
99
|
+
}
|
|
100
|
+
return 'This folder is trusted via a parent folder setting. You can override it with a more specific rule.';
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getTrustUpdateDisplay(
|
|
106
|
+
committedTrustLevel: TrustLevel | undefined,
|
|
107
|
+
effectiveTrust: boolean | undefined,
|
|
108
|
+
isIdeTrusted: boolean | undefined,
|
|
109
|
+
isParentTrusted: boolean | undefined,
|
|
110
|
+
): { savedLocalFallback: string; effectiveNow: string } {
|
|
111
|
+
let effectiveTrustLevel: TrustLevel | undefined;
|
|
112
|
+
if (effectiveTrust !== undefined) {
|
|
113
|
+
effectiveTrustLevel = effectiveTrust
|
|
114
|
+
? TrustLevel.TRUST_FOLDER
|
|
115
|
+
: TrustLevel.DO_NOT_TRUST;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
savedLocalFallback: getLocalTrustLevelDisplay(committedTrustLevel),
|
|
119
|
+
effectiveNow: getTrustLevelDisplay(
|
|
120
|
+
effectiveTrustLevel,
|
|
121
|
+
isIdeTrusted,
|
|
122
|
+
isParentTrusted,
|
|
123
|
+
),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function flattenErrorDetails(error: unknown): string[] {
|
|
128
|
+
if (error instanceof AggregateError) {
|
|
129
|
+
return error.errors.flatMap(flattenErrorDetails);
|
|
130
|
+
}
|
|
131
|
+
return [error instanceof Error ? error.message : String(error)];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function combineTrustUpdateFailure(
|
|
135
|
+
error: unknown,
|
|
136
|
+
rollbackFailures: readonly unknown[],
|
|
137
|
+
aggregateMessage: string,
|
|
138
|
+
): { error: unknown; rollbackSucceeded: boolean } {
|
|
139
|
+
if (rollbackFailures.length === 0) {
|
|
140
|
+
return { error, rollbackSucceeded: true };
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
error: new AggregateError([error, ...rollbackFailures], aggregateMessage),
|
|
144
|
+
rollbackSucceeded: false,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function getTrustCommitErrorMessage(
|
|
149
|
+
phase: 'persistence' | 'live',
|
|
150
|
+
error: unknown,
|
|
151
|
+
rollbackSucceeded = true,
|
|
152
|
+
): string {
|
|
153
|
+
const detail = flattenErrorDetails(error).join('; ');
|
|
154
|
+
if (phase === 'persistence') {
|
|
155
|
+
return rollbackSucceeded
|
|
156
|
+
? `Failed to save trust settings: ${detail}`
|
|
157
|
+
: `Failed to save trust settings and rollback was incomplete: ${detail}`;
|
|
158
|
+
}
|
|
159
|
+
return rollbackSucceeded
|
|
160
|
+
? `Trust settings could not be applied live, so the saved setting was restored: ${detail}`
|
|
161
|
+
: `Trust settings could not be applied live and rollback was incomplete: ${detail}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function shouldDismissTrustDialog(
|
|
165
|
+
showUpdatedPrompt: boolean,
|
|
166
|
+
keyName: string,
|
|
167
|
+
): boolean {
|
|
168
|
+
return keyName === 'escape' || (showUpdatedPrompt && keyName === 'return');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function buildTrustLevelOptions(
|
|
172
|
+
folderName: string,
|
|
173
|
+
parentFolderName: string,
|
|
174
|
+
): Array<RadioSelectItem<TrustLevel>> {
|
|
175
|
+
return buildTrustRadioOptions(folderName, parentFolderName, {
|
|
176
|
+
trustFolder: TrustLevel.TRUST_FOLDER,
|
|
177
|
+
trustParent: TrustLevel.TRUST_PARENT,
|
|
178
|
+
doNotTrust: TrustLevel.DO_NOT_TRUST,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function findInitialTrustOptionIndex(
|
|
183
|
+
options: Array<RadioSelectItem<TrustLevel>>,
|
|
184
|
+
currentTrustLevel: TrustLevel | undefined,
|
|
185
|
+
): number {
|
|
186
|
+
const index = options.findIndex(
|
|
187
|
+
(option) => option.value === currentTrustLevel,
|
|
188
|
+
);
|
|
189
|
+
return index >= 0 ? index : 0;
|
|
190
|
+
}
|