@iaforged/context-code 1.1.9 → 1.2.1
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/README.md +24 -0
- package/dist/src/commands/model/model.js +9 -5
- package/dist/src/commands/timeline/index.js +8 -0
- package/dist/src/commands/timeline/timeline.js +194 -0
- package/dist/src/commands.js +2 -0
- package/dist/src/components/AgentActivitySidebar.js +50 -0
- package/dist/src/components/AgentProgressLine.js +5 -5
- package/dist/src/components/ModelPicker.js +252 -441
- package/dist/src/components/PromptInput/PromptInputFooter.js +10 -29
- package/dist/src/components/Spinner/TeammateSpinnerLine.js +20 -62
- package/dist/src/components/Spinner/TeammateSpinnerTree.js +16 -258
- package/dist/src/components/Spinner/teammateSelectHint.js +1 -1
- package/dist/src/components/Spinner/utils.js +3 -6
- package/dist/src/components/ThemeBrowser.js +120 -0
- package/dist/src/components/ThemePicker.js +113 -321
- package/dist/src/components/design-system/ThemeProvider.js +3 -0
- package/dist/src/components/mcp/MCPListPanel.js +138 -444
- package/dist/src/components/permissions/SandboxPermissionRequest.js +5 -5
- package/dist/src/components/teams/TeamStatus.js +7 -71
- package/dist/src/constants/spinnerVerbs.js +80 -180
- package/dist/src/context/modalStackContext.js +12 -0
- package/dist/src/hooks/useTextInput.js +28 -18
- package/dist/src/main.js +12 -0
- package/dist/src/screens/REPL.js +386 -320
- package/dist/src/skills/loadSkillsDir.js +1 -0
- package/dist/src/tools/AgentTool/UI.js +8 -8
- package/dist/src/tools/BashTool/bashSecurity.js +1 -1
- package/dist/src/utils/handlePromptSubmit.js +12 -2
- package/dist/src/utils/processUserInput/processSlashCommand.js +9 -5
- package/dist/src/utils/sembleMcp/common.js +5 -0
- package/dist/src/utils/sembleMcp/setup.js +119 -0
- package/dist/src/utils/theme.js +24 -3
- package/dist/src/utils/themes/bootstrap.js +109 -0
- package/dist/src/utils/themes/builtin/opencode/_index.json +41 -0
- package/dist/src/utils/themes/builtin/opencode/amoled.json +49 -0
- package/dist/src/utils/themes/builtin/opencode/aura.json +51 -0
- package/dist/src/utils/themes/builtin/opencode/ayu.json +51 -0
- package/dist/src/utils/themes/builtin/opencode/carbonfox.json +53 -0
- package/dist/src/utils/themes/builtin/opencode/catppuccin-frappe.json +85 -0
- package/dist/src/utils/themes/builtin/opencode/catppuccin-macchiato.json +85 -0
- package/dist/src/utils/themes/builtin/opencode/catppuccin.json +45 -0
- package/dist/src/utils/themes/builtin/opencode/cobalt2.json +87 -0
- package/dist/src/utils/themes/builtin/opencode/cursor.json +91 -0
- package/dist/src/utils/themes/builtin/opencode/dracula.json +49 -0
- package/dist/src/utils/themes/builtin/opencode/everforest.json +89 -0
- package/dist/src/utils/themes/builtin/opencode/flexoki.json +86 -0
- package/dist/src/utils/themes/builtin/opencode/github.json +85 -0
- package/dist/src/utils/themes/builtin/opencode/gruvbox.json +45 -0
- package/dist/src/utils/themes/builtin/opencode/kanagawa.json +89 -0
- package/dist/src/utils/themes/builtin/opencode/lucent-orng.json +87 -0
- package/dist/src/utils/themes/builtin/opencode/material.json +87 -0
- package/dist/src/utils/themes/builtin/opencode/matrix.json +91 -0
- package/dist/src/utils/themes/builtin/opencode/mercury.json +86 -0
- package/dist/src/utils/themes/builtin/opencode/monokai.json +49 -0
- package/dist/src/utils/themes/builtin/opencode/nightowl.json +46 -0
- package/dist/src/utils/themes/builtin/opencode/nord.json +46 -0
- package/dist/src/utils/themes/builtin/opencode/oc-2.json +88 -0
- package/dist/src/utils/themes/builtin/opencode/one-dark.json +89 -0
- package/dist/src/utils/themes/builtin/opencode/onedarkpro.json +45 -0
- package/dist/src/utils/themes/builtin/opencode/opencode.json +89 -0
- package/dist/src/utils/themes/builtin/opencode/orng.json +87 -0
- package/dist/src/utils/themes/builtin/opencode/osaka-jade.json +88 -0
- package/dist/src/utils/themes/builtin/opencode/palenight.json +85 -0
- package/dist/src/utils/themes/builtin/opencode/rosepine.json +85 -0
- package/dist/src/utils/themes/builtin/opencode/shadesofpurple.json +51 -0
- package/dist/src/utils/themes/builtin/opencode/solarized.json +49 -0
- package/dist/src/utils/themes/builtin/opencode/synthwave84.json +87 -0
- package/dist/src/utils/themes/builtin/opencode/tokyonight.json +47 -0
- package/dist/src/utils/themes/builtin/opencode/vercel.json +90 -0
- package/dist/src/utils/themes/builtin/opencode/vesper.json +51 -0
- package/dist/src/utils/themes/builtin/opencode/zenburn.json +87 -0
- package/dist/src/utils/themes/index.js +4 -0
- package/dist/src/utils/themes/loader.js +147 -0
- package/dist/src/utils/themes/opencodeMapper.js +124 -0
- package/dist/src/utils/themes/resolver.js +66 -0
- package/dist/src/utils/themes/types.js +1 -0
- package/docs/MCP_SERVERS.md +27 -1
- package/package.json +1 -1
package/dist/src/screens/REPL.js
CHANGED
|
@@ -25,6 +25,7 @@ import { IdleReturnDialog } from '../components/IdleReturnDialog.js';
|
|
|
25
25
|
import * as React from 'react';
|
|
26
26
|
import { useEffect, useMemo, useRef, useState, useCallback, useDeferredValue, useLayoutEffect } from 'react';
|
|
27
27
|
import { useNotifications } from '../context/notifications.js';
|
|
28
|
+
import { ModalStackContext } from '../context/modalStackContext.js';
|
|
28
29
|
import { sendNotification } from '../services/notifier.js';
|
|
29
30
|
import { startPreventSleep, stopPreventSleep } from '../services/preventSleep.js';
|
|
30
31
|
import { useTerminalNotification } from '../ink/useTerminalNotification.js';
|
|
@@ -828,6 +829,24 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
828
829
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
829
830
|
}, []);
|
|
830
831
|
const [toolJSX, setToolJSXInternal] = useState(null);
|
|
832
|
+
const [modalStack, setModalStack] = useState([]);
|
|
833
|
+
const pushModal = useCallback((item) => {
|
|
834
|
+
setModalStack(prev => [...prev, item]);
|
|
835
|
+
}, []);
|
|
836
|
+
const replaceModal = useCallback((item) => {
|
|
837
|
+
setModalStack(prev => {
|
|
838
|
+
if (prev.length === 0) {
|
|
839
|
+
return [item];
|
|
840
|
+
}
|
|
841
|
+
return [...prev.slice(0, -1), item];
|
|
842
|
+
});
|
|
843
|
+
}, []);
|
|
844
|
+
const popModal = useCallback(() => {
|
|
845
|
+
setModalStack(prev => prev.slice(0, -1));
|
|
846
|
+
}, []);
|
|
847
|
+
const clearModals = useCallback(() => {
|
|
848
|
+
setModalStack([]);
|
|
849
|
+
}, []);
|
|
831
850
|
// Track local JSX commands separately so tools can't overwrite them.
|
|
832
851
|
// This enables "immediate" commands (like /btw) to persist while Claude is processing.
|
|
833
852
|
const localJSXCommandRef = useRef(null);
|
|
@@ -1001,6 +1020,26 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
1001
1020
|
}
|
|
1002
1021
|
const [cursor, setCursor] = useState(null);
|
|
1003
1022
|
const cursorNavRef = useRef(null);
|
|
1023
|
+
const jumpToMessage = useCallback((messageId) => {
|
|
1024
|
+
const exact = messagesRef.current.find(m => m.uuid === messageId)?.uuid;
|
|
1025
|
+
const prefix = messagesRef.current.find(m => m.uuid.slice(0, 24) === messageId.slice(0, 24))?.uuid;
|
|
1026
|
+
const resolved = exact ?? prefix;
|
|
1027
|
+
if (!resolved) {
|
|
1028
|
+
addNotification({
|
|
1029
|
+
key: 'timeline-jump-not-found',
|
|
1030
|
+
text: 'Message no longer available in active transcript',
|
|
1031
|
+
color: 'warning',
|
|
1032
|
+
priority: 'medium',
|
|
1033
|
+
timeoutMs: 3000
|
|
1034
|
+
});
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
setCursor({
|
|
1038
|
+
uuid: resolved,
|
|
1039
|
+
msgType: 'user',
|
|
1040
|
+
expanded: false
|
|
1041
|
+
});
|
|
1042
|
+
}, [addNotification]);
|
|
1004
1043
|
// Memoized so Messages' React.memo holds.
|
|
1005
1044
|
const unseenDivider = useMemo(() => computeUnseenDivider(messages, dividerIndex),
|
|
1006
1045
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- length change covers appends; useUnseenDivider's count-drop guard clears dividerIndex on replace/rewind
|
|
@@ -1783,6 +1822,12 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
1783
1822
|
prevDialogRef.current = focusedInputDialog;
|
|
1784
1823
|
}, [focusedInputDialog, repinScroll]);
|
|
1785
1824
|
function onCancel() {
|
|
1825
|
+
if (modalStack.length > 0) {
|
|
1826
|
+
const topModal = modalStack[modalStack.length - 1];
|
|
1827
|
+
topModal?.onClose?.();
|
|
1828
|
+
popModal();
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1786
1831
|
if (focusedInputDialog === 'elicitation') {
|
|
1787
1832
|
// Elicitation dialog handles its own Escape, and closing it shouldn't affect any loading state.
|
|
1788
1833
|
return;
|
|
@@ -2131,6 +2176,18 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
2131
2176
|
setIsMessageSelectorVisible(true);
|
|
2132
2177
|
}
|
|
2133
2178
|
},
|
|
2179
|
+
openMessageSelectorAtMessage: (messageId) => {
|
|
2180
|
+
if (disabled)
|
|
2181
|
+
return;
|
|
2182
|
+
const exact = messages.find(m => m.uuid === messageId);
|
|
2183
|
+
const prefix = messages.find(m => m.uuid.slice(0, 24) === messageId.slice(0, 24));
|
|
2184
|
+
const raw = exact ?? prefix;
|
|
2185
|
+
if (raw && selectableUserMessagesFilter(raw)) {
|
|
2186
|
+
setMessageSelectorPreselect(raw);
|
|
2187
|
+
}
|
|
2188
|
+
setIsMessageSelectorVisible(true);
|
|
2189
|
+
},
|
|
2190
|
+
jumpToMessage,
|
|
2134
2191
|
onChangeAPIKey: reverify,
|
|
2135
2192
|
readFileState: readFileState.current,
|
|
2136
2193
|
setToolJSX,
|
|
@@ -2184,7 +2241,7 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
2184
2241
|
requestPrompt: feature('HOOK_PROMPTS') ? requestPrompt : undefined,
|
|
2185
2242
|
contentReplacementState: contentReplacementStateRef.current
|
|
2186
2243
|
};
|
|
2187
|
-
}, [commands, combinedInitialTools, mainThreadAgentDefinition, debug, initialMcpClients, ideInstallationStatus, dynamicMcpConfig, theme, allowedAgentTypes, store, setAppState, reverify, addNotification, setMessages, onChangeDynamicMcpConfig, resume, requestPrompt, disabled, customSystemPrompt, appendSystemPrompt, setConversationId]);
|
|
2244
|
+
}, [commands, combinedInitialTools, mainThreadAgentDefinition, debug, initialMcpClients, ideInstallationStatus, dynamicMcpConfig, theme, allowedAgentTypes, store, setAppState, reverify, addNotification, setMessages, onChangeDynamicMcpConfig, resume, requestPrompt, disabled, customSystemPrompt, appendSystemPrompt, setConversationId, jumpToMessage, messages]);
|
|
2188
2245
|
// Session backgrounding (Ctrl+B to background/foreground)
|
|
2189
2246
|
const handleBackgroundQuery = useCallback(() => {
|
|
2190
2247
|
// Stop the foreground query so the background one takes over
|
|
@@ -3125,7 +3182,8 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
3125
3182
|
// Read via ref so streamMode can be dropped from onSubmit deps —
|
|
3126
3183
|
// handlePromptSubmit only uses it for debug log + telemetry event.
|
|
3127
3184
|
streamMode: streamModeRef.current,
|
|
3128
|
-
hasInterruptibleToolInProgress: hasInterruptibleToolInProgressRef.current
|
|
3185
|
+
hasInterruptibleToolInProgress: hasInterruptibleToolInProgressRef.current,
|
|
3186
|
+
onJumpToMessage: jumpToMessage
|
|
3129
3187
|
});
|
|
3130
3188
|
// Restore stash that was deferred above. Two cases:
|
|
3131
3189
|
// - Slash command: handlePromptSubmit awaited the full command execution
|
|
@@ -3481,9 +3539,10 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
3481
3539
|
canUseTool,
|
|
3482
3540
|
addNotification,
|
|
3483
3541
|
setMessages,
|
|
3484
|
-
queuedCommands
|
|
3542
|
+
queuedCommands,
|
|
3543
|
+
onJumpToMessage: jumpToMessage
|
|
3485
3544
|
});
|
|
3486
|
-
}, [queryGuard, commands, setToolJSX, getToolUseContext, messages, mainLoopModel, ideSelection, setUserInputOnProcessing, canUseTool, setAbortController, onQuery, addNotification, setAppState, onBeforeQuery]);
|
|
3545
|
+
}, [queryGuard, commands, setToolJSX, getToolUseContext, messages, mainLoopModel, ideSelection, setUserInputOnProcessing, canUseTool, setAbortController, onQuery, addNotification, setAppState, onBeforeQuery, jumpToMessage]);
|
|
3487
3546
|
useQueueProcessor({
|
|
3488
3547
|
executeQueuedInput,
|
|
3489
3548
|
hasActiveLocalJsxUI: isShowingLocalJSXCommand,
|
|
@@ -4073,348 +4132,355 @@ export function REPL({ commands: initialCommands, debug, initialTools, initialMe
|
|
|
4073
4132
|
// (immediate: /model, /mcp, /btw, ...) and scrollable (non-immediate:
|
|
4074
4133
|
// /config, /theme, /diff, ...) both go here now.
|
|
4075
4134
|
const toolJsxCentered = isFullscreenEnvEnabled() && toolJSX?.isLocalJSXCommand === true;
|
|
4076
|
-
const
|
|
4135
|
+
const stackTopModal = modalStack.length > 0 ? modalStack[modalStack.length - 1]?.element : null;
|
|
4136
|
+
const centeredModal = stackTopModal ?? (toolJsxCentered ? toolJSX.jsx : null);
|
|
4077
4137
|
// <AlternateScreen> at the root: everything below is inside its
|
|
4078
4138
|
// <Box height={rows}>. Handlers/contexts are zero-height so ScrollBox's
|
|
4079
4139
|
// flexGrow in FullscreenLayout resolves against this Box. The transcript
|
|
4080
4140
|
// early return above wraps its virtual-scroll branch the same way; only
|
|
4081
4141
|
// the 30-cap dump branch stays unwrapped for native terminal scrollback.
|
|
4082
|
-
const mainReturn =
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
const
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
});
|
|
4119
|
-
// Clean up bridge subscriptions and cancel remote prompts
|
|
4120
|
-
// for this host since the local user already responded.
|
|
4121
|
-
const cleanups = sandboxBridgeCleanupRef.current.get(approvedHost);
|
|
4122
|
-
if (cleanups) {
|
|
4123
|
-
for (const fn of cleanups) {
|
|
4124
|
-
fn();
|
|
4142
|
+
const mainReturn = _jsx(ModalStackContext.Provider, { value: {
|
|
4143
|
+
stack: modalStack,
|
|
4144
|
+
pushModal,
|
|
4145
|
+
replaceModal,
|
|
4146
|
+
popModal,
|
|
4147
|
+
clearModals
|
|
4148
|
+
}, children: _jsxs(KeybindingSetup, { children: [_jsx(AnimatedTerminalTitle, { isAnimating: titleIsAnimating, title: terminalTitle, disabled: titleDisabled, noPrefix: showStatusInTerminalTab }), _jsx(GlobalKeybindingHandlers, { ...globalKeybindingProps }), _jsx(VoiceKeybindingHandler, { voiceHandleKeyEvent: voice.handleKeyEvent, stripTrailing: voice.stripTrailing, resetAnchor: voice.resetAnchor, isActive: !toolJSX?.isLocalJSXCommand }), _jsx(CommandKeybindingHandlers, { onSubmit: onSubmit, isActive: !toolJSX?.isLocalJSXCommand }), _jsx(ScrollKeybindingHandler, { scrollRef: scrollRef, isActive: isFullscreenEnvEnabled() && (centeredModal != null || !focusedInputDialog || focusedInputDialog === 'tool-permission'), onScroll: centeredModal || toolPermissionOverlay || viewedAgentTask ? undefined : composedOnScroll }), feature('MESSAGE_ACTIONS') && isFullscreenEnvEnabled() && !disableMessageActions ? _jsx(MessageActionsKeybindings, { handlers: messageActionHandlers, isActive: cursor !== null }) : null, _jsx(CancelRequestHandler, { ...cancelRequestProps }), _jsx(MCPConnectionManager, { dynamicMcpConfig: dynamicMcpConfig, isStrictMcpConfig: strictMcpConfig, children: _jsx(FullscreenLayout, { scrollRef: scrollRef, overlay: toolPermissionOverlay, bottomFloat: feature('BUDDY') && companionVisible && !companionNarrow ? _jsx(CompanionFloatingBubble, {}) : undefined, modal: centeredModal, modalScrollRef: modalScrollRef, dividerYRef: dividerYRef, hidePill: !!viewedAgentTask, hideSticky: !!viewedTeammateTask, newMessageCount: unseenDivider?.count ?? 0, onPillClick: () => {
|
|
4149
|
+
setCursor(null);
|
|
4150
|
+
jumpToNew(scrollRef.current);
|
|
4151
|
+
}, scrollable: _jsxs(_Fragment, { children: [_jsx(TeammateViewHeader, {}), _jsx(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 }), _jsx(AwsAuthStatusBox, {}), !disabled && placeholderText && !centeredModal && _jsx(UserTextMessage, { param: {
|
|
4152
|
+
text: placeholderText,
|
|
4153
|
+
type: 'text'
|
|
4154
|
+
}, addMargin: true, verbose: verbose }), toolJSX && !(toolJSX.isLocalJSXCommand && toolJSX.isImmediate) && !toolJsxCentered && _jsx(Box, { flexDirection: "column", width: "100%", children: toolJSX.jsx }), "external" === 'ant' && _jsx(TungstenLiveMonitor, {}), feature('WEB_BROWSER_TOOL') ? WebBrowserPanelModule && _jsx(WebBrowserPanelModule.WebBrowserPanel, {}) : null, _jsx(Box, { flexGrow: 1 }), showSpinner && _jsx(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 && _jsx(BriefIdleStatus, {}), isFullscreenEnvEnabled() && _jsx(PromptInputQueuedCommands, {})] }), bottom: _jsxs(Box, { flexDirection: feature('BUDDY') && companionNarrow ? 'column' : 'row', width: "100%", alignItems: feature('BUDDY') && companionNarrow ? undefined : 'flex-end', children: [feature('BUDDY') && companionNarrow && isFullscreenEnvEnabled() && companionVisible ? _jsx(CompanionSprite, {}) : null, _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [permissionStickyFooter, toolJSX?.isLocalJSXCommand && toolJSX.isImmediate && !toolJsxCentered && _jsx(Box, { flexDirection: "column", width: "100%", children: toolJSX.jsx }), !showSpinner && !toolJSX?.isLocalJSXCommand && showExpandedTodos && tasksV2 && tasksV2.length > 0 && _jsx(Box, { width: "100%", flexDirection: "column", children: _jsx(TaskListV2, { tasks: tasksV2, isStandalone: true }) }), focusedInputDialog === 'sandbox-permission' && _jsx(SandboxPermissionRequest, { hostPattern: sandboxPermissionRequestQueue[0].hostPattern, onUserResponse: (response) => {
|
|
4155
|
+
const { allow, persistToSettings } = response;
|
|
4156
|
+
const currentRequest = sandboxPermissionRequestQueue[0];
|
|
4157
|
+
if (!currentRequest)
|
|
4158
|
+
return;
|
|
4159
|
+
const approvedHost = currentRequest.hostPattern.host;
|
|
4160
|
+
if (persistToSettings) {
|
|
4161
|
+
const update = {
|
|
4162
|
+
type: 'addRules',
|
|
4163
|
+
rules: [{
|
|
4164
|
+
toolName: WEB_FETCH_TOOL_NAME,
|
|
4165
|
+
ruleContent: `domain:${approvedHost}`
|
|
4166
|
+
}],
|
|
4167
|
+
behavior: (allow ? 'allow' : 'deny'),
|
|
4168
|
+
destination: 'localSettings'
|
|
4169
|
+
};
|
|
4170
|
+
setAppState(prev => ({
|
|
4171
|
+
...prev,
|
|
4172
|
+
toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, update)
|
|
4173
|
+
}));
|
|
4174
|
+
persistPermissionUpdate(update);
|
|
4175
|
+
// Immediately update sandbox in-memory config to prevent race conditions
|
|
4176
|
+
// where pending requests slip through before settings change is detected
|
|
4177
|
+
SandboxManager.refreshConfig();
|
|
4125
4178
|
}
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4179
|
+
// Resolve ALL pending requests for the same host (not just the first one)
|
|
4180
|
+
// This handles the case where multiple parallel requests came in for the same domain
|
|
4181
|
+
setSandboxPermissionRequestQueue(queue => {
|
|
4182
|
+
queue.filter(item => item.hostPattern.host === approvedHost).forEach(item => item.resolvePromise(allow));
|
|
4183
|
+
return queue.filter(item => item.hostPattern.host !== approvedHost);
|
|
4184
|
+
});
|
|
4185
|
+
// Clean up bridge subscriptions and cancel remote prompts
|
|
4186
|
+
// for this host since the local user already responded.
|
|
4187
|
+
const cleanups = sandboxBridgeCleanupRef.current.get(approvedHost);
|
|
4188
|
+
if (cleanups) {
|
|
4189
|
+
for (const fn of cleanups) {
|
|
4190
|
+
fn();
|
|
4191
|
+
}
|
|
4192
|
+
sandboxBridgeCleanupRef.current.delete(approvedHost);
|
|
4193
|
+
}
|
|
4194
|
+
} }, sandboxPermissionRequestQueue[0].hostPattern.host), focusedInputDialog === 'prompt' && _jsx(PromptDialog, { title: promptQueue[0].title, toolInputSummary: promptQueue[0].toolInputSummary, request: promptQueue[0].request, onRespond: selectedKey => {
|
|
4195
|
+
const item = promptQueue[0];
|
|
4196
|
+
if (!item)
|
|
4197
|
+
return;
|
|
4198
|
+
item.resolve({
|
|
4199
|
+
prompt_response: item.request.prompt,
|
|
4200
|
+
selected: selectedKey
|
|
4201
|
+
});
|
|
4202
|
+
setPromptQueue(([, ...tail]) => tail);
|
|
4203
|
+
}, onAbort: () => {
|
|
4204
|
+
const item = promptQueue[0];
|
|
4205
|
+
if (!item)
|
|
4206
|
+
return;
|
|
4207
|
+
item.reject(new Error('Prompt cancelled by user'));
|
|
4208
|
+
setPromptQueue(([, ...tail]) => tail);
|
|
4209
|
+
} }, promptQueue[0].request.prompt), pendingWorkerRequest && _jsx(WorkerPendingPermission, { toolName: pendingWorkerRequest.toolName, description: pendingWorkerRequest.description }), pendingSandboxRequest && _jsx(WorkerPendingPermission, { toolName: "Network Access", description: `Waiting for leader to approve network access to ${pendingSandboxRequest.host}` }), focusedInputDialog === 'worker-sandbox-permission' && _jsx(SandboxPermissionRequest, { hostPattern: {
|
|
4210
|
+
host: workerSandboxPermissions.queue[0].host,
|
|
4211
|
+
port: undefined
|
|
4212
|
+
}, onUserResponse: (response) => {
|
|
4213
|
+
const { allow, persistToSettings } = response;
|
|
4214
|
+
const currentRequest = workerSandboxPermissions.queue[0];
|
|
4215
|
+
if (!currentRequest)
|
|
4216
|
+
return;
|
|
4217
|
+
const approvedHost = currentRequest.host;
|
|
4218
|
+
// Send response via mailbox to the worker
|
|
4219
|
+
void sendSandboxPermissionResponseViaMailbox(currentRequest.workerName, currentRequest.requestId, approvedHost, allow, teamContext?.teamName);
|
|
4220
|
+
if (persistToSettings && allow) {
|
|
4221
|
+
const update = {
|
|
4222
|
+
type: 'addRules',
|
|
4223
|
+
rules: [{
|
|
4224
|
+
toolName: WEB_FETCH_TOOL_NAME,
|
|
4225
|
+
ruleContent: `domain:${approvedHost}`
|
|
4226
|
+
}],
|
|
4227
|
+
behavior: 'allow',
|
|
4228
|
+
destination: 'localSettings'
|
|
4229
|
+
};
|
|
4230
|
+
setAppState(prev => ({
|
|
4231
|
+
...prev,
|
|
4232
|
+
toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, update)
|
|
4233
|
+
}));
|
|
4234
|
+
persistPermissionUpdate(update);
|
|
4235
|
+
SandboxManager.refreshConfig();
|
|
4236
|
+
}
|
|
4237
|
+
// Remove from queue
|
|
4164
4238
|
setAppState(prev => ({
|
|
4165
4239
|
...prev,
|
|
4166
|
-
|
|
4240
|
+
workerSandboxPermissions: {
|
|
4241
|
+
...prev.workerSandboxPermissions,
|
|
4242
|
+
queue: prev.workerSandboxPermissions.queue.slice(1)
|
|
4243
|
+
}
|
|
4167
4244
|
}));
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4245
|
+
} }, workerSandboxPermissions.queue[0].requestId), focusedInputDialog === 'elicitation' && _jsx(ElicitationDialog, { event: elicitation.queue[0], onResponse: (action, content) => {
|
|
4246
|
+
const currentRequest = elicitation.queue[0];
|
|
4247
|
+
if (!currentRequest)
|
|
4248
|
+
return;
|
|
4249
|
+
// Call respond callback to resolve Promise
|
|
4250
|
+
currentRequest.respond({
|
|
4251
|
+
action,
|
|
4252
|
+
content
|
|
4253
|
+
});
|
|
4254
|
+
// For URL accept, keep in queue for phase 2
|
|
4255
|
+
const isUrlAccept = currentRequest.params.mode === 'url' && action === 'accept';
|
|
4256
|
+
if (!isUrlAccept) {
|
|
4257
|
+
setAppState(prev => ({
|
|
4258
|
+
...prev,
|
|
4259
|
+
elicitation: {
|
|
4260
|
+
queue: prev.elicitation.queue.slice(1)
|
|
4261
|
+
}
|
|
4262
|
+
}));
|
|
4177
4263
|
}
|
|
4178
|
-
}
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
if (!currentRequest)
|
|
4182
|
-
return;
|
|
4183
|
-
// Call respond callback to resolve Promise
|
|
4184
|
-
currentRequest.respond({
|
|
4185
|
-
action,
|
|
4186
|
-
content
|
|
4187
|
-
});
|
|
4188
|
-
// For URL accept, keep in queue for phase 2
|
|
4189
|
-
const isUrlAccept = currentRequest.params.mode === 'url' && action === 'accept';
|
|
4190
|
-
if (!isUrlAccept) {
|
|
4264
|
+
}, onWaitingDismiss: action => {
|
|
4265
|
+
const currentRequest = elicitation.queue[0];
|
|
4266
|
+
// Remove from queue
|
|
4191
4267
|
setAppState(prev => ({
|
|
4192
4268
|
...prev,
|
|
4193
4269
|
elicitation: {
|
|
4194
4270
|
queue: prev.elicitation.queue.slice(1)
|
|
4195
4271
|
}
|
|
4196
4272
|
}));
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4273
|
+
currentRequest?.onWaitingDismiss?.(action);
|
|
4274
|
+
} }, elicitation.queue[0].serverName + ':' + String(elicitation.queue[0].requestId)), focusedInputDialog === 'cost' && _jsx(CostThresholdDialog, { onDone: () => {
|
|
4275
|
+
setShowCostDialog(false);
|
|
4276
|
+
setHaveShownCostDialog(true);
|
|
4277
|
+
saveGlobalConfig(current => ({
|
|
4278
|
+
...current,
|
|
4279
|
+
hasAcknowledgedCostThreshold: true
|
|
4280
|
+
}));
|
|
4281
|
+
logEvent('tengu_cost_threshold_acknowledged', {});
|
|
4282
|
+
} }), focusedInputDialog === 'idle-return' && idleReturnPending && _jsx(IdleReturnDialog, { idleMinutes: idleReturnPending.idleMinutes, totalInputTokens: getTotalInputTokens(), onDone: async (action) => {
|
|
4283
|
+
const pending = idleReturnPending;
|
|
4284
|
+
setIdleReturnPending(null);
|
|
4285
|
+
logEvent('tengu_idle_return_action', {
|
|
4286
|
+
action: action,
|
|
4287
|
+
idleMinutes: Math.round(pending.idleMinutes),
|
|
4288
|
+
messageCount: messagesRef.current.length,
|
|
4289
|
+
totalInputTokens: getTotalInputTokens()
|
|
4290
|
+
});
|
|
4291
|
+
if (action === 'dismiss') {
|
|
4292
|
+
setInputValue(pending.input);
|
|
4293
|
+
return;
|
|
4294
|
+
}
|
|
4295
|
+
if (action === 'never') {
|
|
4296
|
+
saveGlobalConfig(current => {
|
|
4297
|
+
if (current.idleReturnDismissed)
|
|
4298
|
+
return current;
|
|
4299
|
+
return {
|
|
4300
|
+
...current,
|
|
4301
|
+
idleReturnDismissed: true
|
|
4302
|
+
};
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
if (action === 'clear') {
|
|
4306
|
+
const { clearConversation } = await import('../commands/clear/conversation.js');
|
|
4307
|
+
await clearConversation({
|
|
4308
|
+
setMessages,
|
|
4309
|
+
readFileState: readFileState.current,
|
|
4310
|
+
discoveredSkillNames: discoveredSkillNamesRef.current,
|
|
4311
|
+
loadedNestedMemoryPaths: loadedNestedMemoryPathsRef.current,
|
|
4312
|
+
getAppState: () => store.getState(),
|
|
4313
|
+
setAppState,
|
|
4314
|
+
setConversationId
|
|
4315
|
+
});
|
|
4316
|
+
haikuTitleAttemptedRef.current = false;
|
|
4317
|
+
setHaikuTitle(undefined);
|
|
4318
|
+
bashTools.current.clear();
|
|
4319
|
+
bashToolsProcessedIdx.current = 0;
|
|
4320
|
+
}
|
|
4321
|
+
skipIdleCheckRef.current = true;
|
|
4322
|
+
void onSubmitRef.current(pending.input, {
|
|
4323
|
+
setCursorOffset: () => { },
|
|
4324
|
+
clearBuffer: () => { },
|
|
4325
|
+
resetHistory: () => { }
|
|
4326
|
+
});
|
|
4327
|
+
} }), focusedInputDialog === 'ide-onboarding' && _jsx(IdeOnboardingDialog, { onDone: () => setShowIdeOnboarding(false), installationStatus: ideInstallationStatus }), "external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && _jsx(AntModelSwitchCallout, { onDone: (selection, modelAlias) => {
|
|
4328
|
+
setShowModelSwitchCallout(false);
|
|
4329
|
+
if (selection === 'switch' && modelAlias) {
|
|
4330
|
+
setAppState(prev => ({
|
|
4331
|
+
...prev,
|
|
4332
|
+
mainLoopModel: modelAlias,
|
|
4333
|
+
mainLoopModelForSession: null
|
|
4334
|
+
}));
|
|
4205
4335
|
}
|
|
4206
|
-
}))
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
})
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
setIdleReturnPending(null);
|
|
4219
|
-
logEvent('tengu_idle_return_action', {
|
|
4220
|
-
action: action,
|
|
4221
|
-
idleMinutes: Math.round(pending.idleMinutes),
|
|
4222
|
-
messageCount: messagesRef.current.length,
|
|
4223
|
-
totalInputTokens: getTotalInputTokens()
|
|
4224
|
-
});
|
|
4225
|
-
if (action === 'dismiss') {
|
|
4226
|
-
setInputValue(pending.input);
|
|
4227
|
-
return;
|
|
4228
|
-
}
|
|
4229
|
-
if (action === 'never') {
|
|
4230
|
-
saveGlobalConfig(current => {
|
|
4231
|
-
if (current.idleReturnDismissed)
|
|
4232
|
-
return current;
|
|
4336
|
+
} }), "external" === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && _jsx(UndercoverAutoCallout, { onDone: () => setShowUndercoverCallout(false) }), focusedInputDialog === 'effort-callout' && _jsx(EffortCallout, { model: mainLoopModel, onDone: selection => {
|
|
4337
|
+
setShowEffortCallout(false);
|
|
4338
|
+
if (selection !== 'dismiss') {
|
|
4339
|
+
setAppState(prev => ({
|
|
4340
|
+
...prev,
|
|
4341
|
+
effortValue: selection
|
|
4342
|
+
}));
|
|
4343
|
+
}
|
|
4344
|
+
} }), focusedInputDialog === 'remote-callout' && _jsx(RemoteCallout, { onDone: selection => {
|
|
4345
|
+
setAppState(prev => {
|
|
4346
|
+
if (!prev.showRemoteCallout)
|
|
4347
|
+
return prev;
|
|
4233
4348
|
return {
|
|
4234
|
-
...
|
|
4235
|
-
|
|
4349
|
+
...prev,
|
|
4350
|
+
showRemoteCallout: false,
|
|
4351
|
+
...(selection === 'enable' && {
|
|
4352
|
+
replBridgeEnabled: true,
|
|
4353
|
+
replBridgeExplicit: true,
|
|
4354
|
+
replBridgeOutboundOnly: false
|
|
4355
|
+
})
|
|
4236
4356
|
};
|
|
4237
4357
|
});
|
|
4238
|
-
}
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
await clearConversation({
|
|
4242
|
-
setMessages,
|
|
4243
|
-
readFileState: readFileState.current,
|
|
4244
|
-
discoveredSkillNames: discoveredSkillNamesRef.current,
|
|
4245
|
-
loadedNestedMemoryPaths: loadedNestedMemoryPathsRef.current,
|
|
4246
|
-
getAppState: () => store.getState(),
|
|
4247
|
-
setAppState,
|
|
4248
|
-
setConversationId
|
|
4249
|
-
});
|
|
4250
|
-
haikuTitleAttemptedRef.current = false;
|
|
4251
|
-
setHaikuTitle(undefined);
|
|
4252
|
-
bashTools.current.clear();
|
|
4253
|
-
bashToolsProcessedIdx.current = 0;
|
|
4254
|
-
}
|
|
4255
|
-
skipIdleCheckRef.current = true;
|
|
4256
|
-
void onSubmitRef.current(pending.input, {
|
|
4257
|
-
setCursorOffset: () => { },
|
|
4258
|
-
clearBuffer: () => { },
|
|
4259
|
-
resetHistory: () => { }
|
|
4260
|
-
});
|
|
4261
|
-
} }), focusedInputDialog === 'ide-onboarding' && _jsx(IdeOnboardingDialog, { onDone: () => setShowIdeOnboarding(false), installationStatus: ideInstallationStatus }), "external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && _jsx(AntModelSwitchCallout, { onDone: (selection, modelAlias) => {
|
|
4262
|
-
setShowModelSwitchCallout(false);
|
|
4263
|
-
if (selection === 'switch' && modelAlias) {
|
|
4264
|
-
setAppState(prev => ({
|
|
4358
|
+
} }), exitFlow, focusedInputDialog === 'plugin-hint' && hintRecommendation && _jsx(PluginHintMenu, { pluginName: hintRecommendation.pluginName, pluginDescription: hintRecommendation.pluginDescription, marketplaceName: hintRecommendation.marketplaceName, sourceCommand: hintRecommendation.sourceCommand, onResponse: handleHintResponse }), focusedInputDialog === 'lsp-recommendation' && lspRecommendation && _jsx(LspRecommendationMenu, { pluginName: lspRecommendation.pluginName, pluginDescription: lspRecommendation.pluginDescription, fileExtension: lspRecommendation.fileExtension, onResponse: handleLspResponse }), focusedInputDialog === 'desktop-upsell' && _jsx(DesktopUpsellStartup, { onDone: () => setShowDesktopUpsellStartup(false) }), feature('ULTRAPLAN') ? focusedInputDialog === 'ultraplan-choice' && ultraplanPendingChoice && _jsx(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 && _jsx(UltraplanLaunchDialog, { onChoice: (choice, opts) => {
|
|
4359
|
+
const blurb = ultraplanLaunchPending.blurb;
|
|
4360
|
+
setAppState(prev => prev.ultraplanLaunchPending ? {
|
|
4265
4361
|
...prev,
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4362
|
+
ultraplanLaunchPending: undefined
|
|
4363
|
+
} : prev);
|
|
4364
|
+
if (choice === 'cancel')
|
|
4365
|
+
return;
|
|
4366
|
+
// Command's onDone used display:'skip', so add the
|
|
4367
|
+
// echo here — gives immediate feedback before the
|
|
4368
|
+
// ~5s teleportToRemote resolves.
|
|
4369
|
+
setMessages(prev => [...prev, createCommandInputMessage(formatCommandInputTags('ultraplan', blurb))]);
|
|
4370
|
+
const appendStdout = (msg) => setMessages(prev => [...prev, createCommandInputMessage(`<${LOCAL_COMMAND_STDOUT_TAG}>${escapeXml(msg)}</${LOCAL_COMMAND_STDOUT_TAG}>`)]);
|
|
4371
|
+
// Defer the second message if a query is mid-turn
|
|
4372
|
+
// so it lands after the assistant reply, not
|
|
4373
|
+
// between the user's prompt and the reply.
|
|
4374
|
+
const appendWhenIdle = (msg) => {
|
|
4375
|
+
if (!queryGuard.isActive) {
|
|
4376
|
+
appendStdout(msg);
|
|
4377
|
+
return;
|
|
4378
|
+
}
|
|
4379
|
+
const unsub = queryGuard.subscribe(() => {
|
|
4380
|
+
if (queryGuard.isActive)
|
|
4381
|
+
return;
|
|
4382
|
+
unsub();
|
|
4383
|
+
// Skip if the user stopped ultraplan while we
|
|
4384
|
+
// were waiting — avoids a stale "Monitoring
|
|
4385
|
+
// <url>" message for a session that's gone.
|
|
4386
|
+
if (!store.getState().ultraplanSessionUrl)
|
|
4387
|
+
return;
|
|
4388
|
+
appendStdout(msg);
|
|
4389
|
+
});
|
|
4290
4390
|
};
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4391
|
+
void launchUltraplan({
|
|
4392
|
+
blurb,
|
|
4393
|
+
getAppState: () => store.getState(),
|
|
4394
|
+
setAppState,
|
|
4395
|
+
signal: createAbortController().signal,
|
|
4396
|
+
disconnectedBridge: opts?.disconnectedBridge,
|
|
4397
|
+
onSessionReady: appendWhenIdle
|
|
4398
|
+
}).then(appendStdout).catch(logError);
|
|
4399
|
+
} }) : null, mrRender(), !toolJSX?.shouldHidePromptInput && !focusedInputDialog && !isExiting && !disabled && !cursor && _jsxs(_Fragment, { children: [autoRunIssueReason && _jsx(AutoRunIssueNotification, { onRun: handleAutoRunIssue, onCancel: handleCancelAutoRunIssue, reason: getAutoRunIssueReasonText(autoRunIssueReason) }), postCompactSurvey.state !== 'closed' ? _jsx(FeedbackSurvey, { state: postCompactSurvey.state, lastResponse: postCompactSurvey.lastResponse, handleSelect: postCompactSurvey.handleSelect, inputValue: inputValue, setInputValue: setInputValue, onRequestFeedback: handleSurveyRequestFeedback }) : memorySurvey.state !== 'closed' ? _jsx(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)" }) : _jsx(FeedbackSurvey, { state: feedbackSurvey.state, lastResponse: feedbackSurvey.lastResponse, handleSelect: feedbackSurvey.handleSelect, handleTranscriptSelect: feedbackSurvey.handleTranscriptSelect, inputValue: inputValue, setInputValue: setInputValue, onRequestFeedback: didAutoRunIssueRef.current ? undefined : handleSurveyRequestFeedback }), frustrationDetection.state !== 'closed' && _jsx(FeedbackSurvey, { state: frustrationDetection.state, lastResponse: null, handleSelect: () => { }, handleTranscriptSelect: frustrationDetection.handleTranscriptSelect, inputValue: inputValue, setInputValue: setInputValue }), "external" === 'ant' && skillImprovementSurvey.suggestion && _jsx(SkillImprovementSurvey, { isOpen: skillImprovementSurvey.isOpen, skillName: skillImprovementSurvey.suggestion.skillName, updates: skillImprovementSurvey.suggestion.updates, handleSelect: skillImprovementSurvey.handleSelect, inputValue: inputValue, setInputValue: setInputValue }), showIssueFlagBanner && _jsx(IssueFlagBanner, {}), _jsx(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:
|
|
4400
|
+
// Works during isLoading — edit cancels first; uuid selection survives appends.
|
|
4401
|
+
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: insertTextRef, voiceInterimRange: voice.interimRange }), _jsx(SessionBackgroundHint, { onBackgroundSession: handleBackgroundSession, isLoading: isLoading })] }), cursor &&
|
|
4402
|
+
_jsx(MessageActionsBar, { cursor: cursor }), focusedInputDialog === 'message-selector' && _jsx(MessageSelector, { messages: messages, preselectedMessage: messageSelectorPreselect, onPreRestore: onCancel, onRestoreCode: async (message) => {
|
|
4403
|
+
await fileHistoryRewind((updater) => {
|
|
4404
|
+
setAppState(prev => ({
|
|
4405
|
+
...prev,
|
|
4406
|
+
fileHistory: updater(prev.fileHistory)
|
|
4407
|
+
}));
|
|
4408
|
+
}, message.uuid);
|
|
4409
|
+
}, onSummarize: async (message, feedback, direction = 'from') => {
|
|
4410
|
+
// Project snipped messages so the compact model
|
|
4411
|
+
// doesn't summarize content that was intentionally removed.
|
|
4412
|
+
const compactMessages = getMessagesAfterCompactBoundary(messages);
|
|
4413
|
+
const messageIndex = compactMessages.indexOf(message);
|
|
4414
|
+
if (messageIndex === -1) {
|
|
4415
|
+
// Selected a snipped or pre-compact message that the
|
|
4416
|
+
// selector still shows (REPL keeps full history for
|
|
4417
|
+
// scrollback). Surface why nothing happened instead
|
|
4418
|
+
// of silently no-oping.
|
|
4419
|
+
setMessages(prev => [...prev, createSystemMessage('That message is no longer in the active context (snipped or pre-compact). Choose a more recent message.', 'warning')]);
|
|
4311
4420
|
return;
|
|
4312
4421
|
}
|
|
4313
|
-
const
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4422
|
+
const newAbortController = createAbortController();
|
|
4423
|
+
const context = getToolUseContext(compactMessages, [], newAbortController, mainLoopModel);
|
|
4424
|
+
const appState = context.getAppState();
|
|
4425
|
+
const defaultSysPrompt = await getSystemPrompt(context.options.tools, context.options.mainLoopModel, Array.from(appState.toolPermissionContext.additionalWorkingDirectories.keys()), context.options.mcpClients);
|
|
4426
|
+
const systemPrompt = buildEffectiveSystemPrompt({
|
|
4427
|
+
mainThreadAgentDefinition: undefined,
|
|
4428
|
+
toolUseContext: context,
|
|
4429
|
+
customSystemPrompt: context.options.customSystemPrompt,
|
|
4430
|
+
defaultSystemPrompt: defaultSysPrompt,
|
|
4431
|
+
appendSystemPrompt: context.options.appendSystemPrompt
|
|
4323
4432
|
});
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
if (messageIndex === -1) {
|
|
4349
|
-
// Selected a snipped or pre-compact message that the
|
|
4350
|
-
// selector still shows (REPL keeps full history for
|
|
4351
|
-
// scrollback). Surface why nothing happened instead
|
|
4352
|
-
// of silently no-oping.
|
|
4353
|
-
setMessages(prev => [...prev, createSystemMessage('That message is no longer in the active context (snipped or pre-compact). Choose a more recent message.', 'warning')]);
|
|
4354
|
-
return;
|
|
4355
|
-
}
|
|
4356
|
-
const newAbortController = createAbortController();
|
|
4357
|
-
const context = getToolUseContext(compactMessages, [], newAbortController, mainLoopModel);
|
|
4358
|
-
const appState = context.getAppState();
|
|
4359
|
-
const defaultSysPrompt = await getSystemPrompt(context.options.tools, context.options.mainLoopModel, Array.from(appState.toolPermissionContext.additionalWorkingDirectories.keys()), context.options.mcpClients);
|
|
4360
|
-
const systemPrompt = buildEffectiveSystemPrompt({
|
|
4361
|
-
mainThreadAgentDefinition: undefined,
|
|
4362
|
-
toolUseContext: context,
|
|
4363
|
-
customSystemPrompt: context.options.customSystemPrompt,
|
|
4364
|
-
defaultSystemPrompt: defaultSysPrompt,
|
|
4365
|
-
appendSystemPrompt: context.options.appendSystemPrompt
|
|
4366
|
-
});
|
|
4367
|
-
const [userContext, systemContext] = await Promise.all([getUserContext(), getSystemContext()]);
|
|
4368
|
-
const result = await partialCompactConversation(compactMessages, messageIndex, context, {
|
|
4369
|
-
systemPrompt,
|
|
4370
|
-
userContext,
|
|
4371
|
-
systemContext,
|
|
4372
|
-
toolUseContext: context,
|
|
4373
|
-
forkContextMessages: compactMessages
|
|
4374
|
-
}, feedback, direction);
|
|
4375
|
-
const kept = result.messagesToKeep ?? [];
|
|
4376
|
-
const ordered = direction === 'up_to' ? [...result.summaryMessages, ...kept] : [...kept, ...result.summaryMessages];
|
|
4377
|
-
const postCompact = [result.boundaryMarker, ...ordered, ...result.attachments, ...result.hookResults];
|
|
4378
|
-
// Fullscreen 'from' keeps scrollback; 'up_to' must not
|
|
4379
|
-
// (old[0] unchanged + grown array means incremental
|
|
4380
|
-
// useLogMessages path, so boundary never persisted).
|
|
4381
|
-
// Find by uuid since old is raw REPL history and snipped
|
|
4382
|
-
// entries can shift the projected messageIndex.
|
|
4383
|
-
if (isFullscreenEnvEnabled() && direction === 'from') {
|
|
4384
|
-
setMessages(old => {
|
|
4385
|
-
const rawIdx = old.findIndex(m => m.uuid === message.uuid);
|
|
4386
|
-
return [...old.slice(0, rawIdx === -1 ? 0 : rawIdx), ...postCompact];
|
|
4387
|
-
});
|
|
4388
|
-
}
|
|
4389
|
-
else {
|
|
4390
|
-
setMessages(postCompact);
|
|
4391
|
-
}
|
|
4392
|
-
// Partial compact bypasses handleMessageFromStream — clear
|
|
4393
|
-
// the context-blocked flag so proactive ticks resume.
|
|
4394
|
-
if (feature('PROACTIVE') || feature('KAIROS')) {
|
|
4395
|
-
proactiveModule?.setContextBlocked(false);
|
|
4396
|
-
}
|
|
4397
|
-
setConversationId(randomUUID());
|
|
4398
|
-
runPostCompactCleanup(context.options.querySource);
|
|
4399
|
-
if (direction === 'from') {
|
|
4400
|
-
const r = textForResubmit(message);
|
|
4401
|
-
if (r) {
|
|
4402
|
-
setInputValue(r.text);
|
|
4403
|
-
setInputMode(r.mode);
|
|
4433
|
+
const [userContext, systemContext] = await Promise.all([getUserContext(), getSystemContext()]);
|
|
4434
|
+
const result = await partialCompactConversation(compactMessages, messageIndex, context, {
|
|
4435
|
+
systemPrompt,
|
|
4436
|
+
userContext,
|
|
4437
|
+
systemContext,
|
|
4438
|
+
toolUseContext: context,
|
|
4439
|
+
forkContextMessages: compactMessages
|
|
4440
|
+
}, feedback, direction);
|
|
4441
|
+
const kept = result.messagesToKeep ?? [];
|
|
4442
|
+
const ordered = direction === 'up_to' ? [...result.summaryMessages, ...kept] : [...kept, ...result.summaryMessages];
|
|
4443
|
+
const postCompact = [result.boundaryMarker, ...ordered, ...result.attachments, ...result.hookResults];
|
|
4444
|
+
// Fullscreen 'from' keeps scrollback; 'up_to' must not
|
|
4445
|
+
// (old[0] unchanged + grown array means incremental
|
|
4446
|
+
// useLogMessages path, so boundary never persisted).
|
|
4447
|
+
// Find by uuid since old is raw REPL history and snipped
|
|
4448
|
+
// entries can shift the projected messageIndex.
|
|
4449
|
+
if (isFullscreenEnvEnabled() && direction === 'from') {
|
|
4450
|
+
setMessages(old => {
|
|
4451
|
+
const rawIdx = old.findIndex(m => m.uuid === message.uuid);
|
|
4452
|
+
return [...old.slice(0, rawIdx === -1 ? 0 : rawIdx), ...postCompact];
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
else {
|
|
4456
|
+
setMessages(postCompact);
|
|
4404
4457
|
}
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4458
|
+
// Partial compact bypasses handleMessageFromStream — clear
|
|
4459
|
+
// the context-blocked flag so proactive ticks resume.
|
|
4460
|
+
if (feature('PROACTIVE') || feature('KAIROS')) {
|
|
4461
|
+
proactiveModule?.setContextBlocked(false);
|
|
4462
|
+
}
|
|
4463
|
+
setConversationId(randomUUID());
|
|
4464
|
+
runPostCompactCleanup(context.options.querySource);
|
|
4465
|
+
if (direction === 'from') {
|
|
4466
|
+
const r = textForResubmit(message);
|
|
4467
|
+
if (r) {
|
|
4468
|
+
setInputValue(r.text);
|
|
4469
|
+
setInputMode(r.mode);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
// Show notification with ctrl+o hint
|
|
4473
|
+
const historyShortcut = getShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o');
|
|
4474
|
+
addNotification({
|
|
4475
|
+
key: 'summarize-ctrl-o-hint',
|
|
4476
|
+
text: `Conversation summarized (${historyShortcut} for history)`,
|
|
4477
|
+
priority: 'medium',
|
|
4478
|
+
timeoutMs: 8000
|
|
4479
|
+
});
|
|
4480
|
+
}, onRestoreMessage: handleRestoreMessage, onClose: () => {
|
|
4481
|
+
setIsMessageSelectorVisible(false);
|
|
4482
|
+
setMessageSelectorPreselect(undefined);
|
|
4483
|
+
} }), "external" === 'ant' && _jsx(DevBar, {})] }), feature('BUDDY') && !(companionNarrow && isFullscreenEnvEnabled()) && companionVisible ? _jsx(CompanionSprite, {}) : null] }) }) }, remountKey)] }) });
|
|
4418
4484
|
if (isFullscreenEnvEnabled()) {
|
|
4419
4485
|
return _jsx(AlternateScreen, { mouseTracking: isMouseTrackingEnabled(), children: mainReturn });
|
|
4420
4486
|
}
|