wave-code 0.11.5 → 0.11.7

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.
Files changed (58) hide show
  1. package/dist/acp/agent.d.ts.map +1 -1
  2. package/dist/acp/agent.js +9 -0
  3. package/dist/commands/update.d.ts +2 -0
  4. package/dist/commands/update.d.ts.map +1 -0
  5. package/dist/commands/update.js +97 -0
  6. package/dist/components/App.js +1 -1
  7. package/dist/components/BtwDisplay.d.ts +8 -0
  8. package/dist/components/BtwDisplay.d.ts.map +1 -0
  9. package/dist/components/BtwDisplay.js +9 -0
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +3 -4
  12. package/dist/components/ConfirmationDetails.d.ts.map +1 -1
  13. package/dist/components/ConfirmationDetails.js +7 -1
  14. package/dist/components/InputBox.d.ts.map +1 -1
  15. package/dist/components/InputBox.js +13 -3
  16. package/dist/components/MessageList.d.ts.map +1 -1
  17. package/dist/components/MessageList.js +5 -3
  18. package/dist/components/RewindCommand.js +2 -2
  19. package/dist/components/StatusLine.d.ts +8 -0
  20. package/dist/components/StatusLine.d.ts.map +1 -0
  21. package/dist/components/StatusLine.js +5 -0
  22. package/dist/constants/commands.d.ts.map +1 -1
  23. package/dist/constants/commands.js +6 -0
  24. package/dist/contexts/useChat.d.ts +4 -0
  25. package/dist/contexts/useChat.d.ts.map +1 -1
  26. package/dist/contexts/useChat.js +35 -1
  27. package/dist/hooks/useInputManager.d.ts +3 -0
  28. package/dist/hooks/useInputManager.d.ts.map +1 -1
  29. package/dist/hooks/useInputManager.js +50 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +4 -0
  32. package/dist/managers/inputHandlers.d.ts +1 -1
  33. package/dist/managers/inputHandlers.d.ts.map +1 -1
  34. package/dist/managers/inputHandlers.js +68 -5
  35. package/dist/managers/inputReducer.d.ts +17 -0
  36. package/dist/managers/inputReducer.d.ts.map +1 -1
  37. package/dist/managers/inputReducer.js +16 -0
  38. package/dist/utils/version.d.ts +2 -0
  39. package/dist/utils/version.d.ts.map +1 -0
  40. package/dist/utils/version.js +4 -0
  41. package/package.json +4 -2
  42. package/src/acp/agent.ts +10 -0
  43. package/src/commands/update.ts +116 -0
  44. package/src/components/App.tsx +1 -1
  45. package/src/components/BtwDisplay.tsx +33 -0
  46. package/src/components/ChatInterface.tsx +25 -25
  47. package/src/components/ConfirmationDetails.tsx +14 -0
  48. package/src/components/InputBox.tsx +30 -17
  49. package/src/components/MessageList.tsx +6 -3
  50. package/src/components/RewindCommand.tsx +2 -2
  51. package/src/components/StatusLine.tsx +36 -0
  52. package/src/constants/commands.ts +6 -0
  53. package/src/contexts/useChat.tsx +49 -1
  54. package/src/hooks/useInputManager.ts +59 -0
  55. package/src/index.ts +9 -0
  56. package/src/managers/inputHandlers.ts +87 -4
  57. package/src/managers/inputReducer.ts +31 -1
  58. package/src/utils/version.ts +8 -0
@@ -10,6 +10,7 @@ import { RewindCommand } from "./RewindCommand.js";
10
10
  import { HelpView } from "./HelpView.js";
11
11
  import { StatusCommand } from "./StatusCommand.js";
12
12
  import { PluginManagerShell } from "./PluginManagerShell.js";
13
+ import { StatusLine } from "./StatusLine.js";
13
14
  import { useInputManager } from "../hooks/useInputManager.js";
14
15
  import { useChat } from "../contexts/useChat.js";
15
16
 
@@ -54,12 +55,16 @@ export const InputBox: React.FC<InputBoxProps> = ({
54
55
  const {
55
56
  permissionMode: chatPermissionMode,
56
57
  setPermissionMode: setChatPermissionMode,
58
+ allowBypassInCycle: chatAllowBypassInCycle,
57
59
  handleRewindSelect,
58
60
  backgroundCurrentTask,
59
61
  messages,
60
62
  getFullMessageThread,
61
63
  sessionId,
62
64
  workingDirectory,
65
+ askBtw,
66
+ btwState: chatBtwState,
67
+ setBtwState: setChatBtwState,
63
68
  } = useChat();
64
69
 
65
70
  // Input manager with all input state and functionality (including images)
@@ -103,12 +108,18 @@ export const InputBox: React.FC<InputBoxProps> = ({
103
108
  // Permission mode
104
109
  permissionMode,
105
110
  setPermissionMode,
111
+ setAllowBypassInCycle,
112
+ // BTW state
113
+ btwState,
106
114
  // Main handler
107
115
  handleInput,
108
116
  // Manager ready state
109
117
  isManagerReady,
110
118
  } = useInputManager({
111
119
  onSendMessage: sendMessage,
120
+ onAskBtw: askBtw,
121
+ btwState: chatBtwState,
122
+ onBtwStateChange: setChatBtwState,
112
123
  onHasSlashCommand: hasSlashCommand,
113
124
  onAbortMessage: abortMessage,
114
125
  onBackgroundCurrentTask: backgroundCurrentTask,
@@ -123,6 +134,11 @@ export const InputBox: React.FC<InputBoxProps> = ({
123
134
  setPermissionMode(chatPermissionMode);
124
135
  }, [chatPermissionMode, setPermissionMode]);
125
136
 
137
+ // Sync allowBypassInCycle from useChat to InputManager
138
+ useEffect(() => {
139
+ setAllowBypassInCycle(chatAllowBypassInCycle);
140
+ }, [chatAllowBypassInCycle, setAllowBypassInCycle]);
141
+
126
142
  // Use the InputManager's unified input handler
127
143
  useInput(async (input, key) => {
128
144
  await handleInput(input, key, attachedImages, clearImages);
@@ -243,12 +259,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
243
259
  <Box flexDirection="column">
244
260
  <Box
245
261
  borderStyle="single"
246
- borderColor="gray"
262
+ borderColor={btwState.isActive ? "cyan" : "gray"}
247
263
  borderLeft={false}
248
264
  borderRight={false}
249
265
  >
250
266
  <Text color={isPlaceholder ? "gray" : "white"}>
251
- {shouldShowCursor ? (
267
+ {btwState.isActive && isPlaceholder ? (
268
+ <>
269
+ <Text backgroundColor="white" color="black">
270
+ {" "}
271
+ </Text>
272
+ <Text color="cyan">Type your side question...</Text>
273
+ </>
274
+ ) : shouldShowCursor ? (
252
275
  <>
253
276
  {beforeCursor}
254
277
  <Text backgroundColor="white" color="black">
@@ -261,21 +284,11 @@ export const InputBox: React.FC<InputBoxProps> = ({
261
284
  )}
262
285
  </Text>
263
286
  </Box>
264
- <Box paddingRight={1} justifyContent="space-between" width="100%">
265
- {isShellCommand ? (
266
- <Text color="gray">
267
- Shell: <Text color="yellow">Run shell command</Text>
268
- </Text>
269
- ) : (
270
- <Text color="gray">
271
- Mode:{" "}
272
- <Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
273
- {permissionMode}
274
- </Text>{" "}
275
- (Shift+Tab to cycle)
276
- </Text>
277
- )}
278
- </Box>
287
+ <StatusLine
288
+ permissionMode={permissionMode}
289
+ isShellCommand={isShellCommand}
290
+ isBtwActive={btwState.isActive}
291
+ />
279
292
  </Box>
280
293
  )}
281
294
  </Box>
@@ -41,8 +41,11 @@ export const MessageList = React.memo(
41
41
  // Limit messages to prevent long rendering times
42
42
  const maxMessages = 10;
43
43
 
44
+ // Filter out meta messages
45
+ const visibleMessages = messages.filter((m) => !m.isMeta);
46
+
44
47
  // Flatten messages into blocks with metadata
45
- const allBlocks = messages.flatMap((message, messageIndex) => {
48
+ const allBlocks = visibleMessages.flatMap((message, messageIndex) => {
46
49
  return message.blocks.map((block, blockIndex) => ({
47
50
  block,
48
51
  message,
@@ -101,8 +104,8 @@ export const MessageList = React.memo(
101
104
  );
102
105
  }
103
106
  if (
104
- messages.length > maxMessages &&
105
- item.messageIndex < messages.length - maxMessages
107
+ visibleMessages.length > maxMessages &&
108
+ item.messageIndex < visibleMessages.length - maxMessages
106
109
  ) {
107
110
  return null;
108
111
  }
@@ -30,10 +30,10 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
30
30
  }
31
31
  }, [getFullMessageThread]);
32
32
 
33
- // Filter user messages as checkpoints
33
+ // Filter user messages as checkpoints, excluding meta messages
34
34
  const checkpoints = messages
35
35
  .map((msg, index) => ({ msg, index }))
36
- .filter(({ msg }) => msg.role === "user");
36
+ .filter(({ msg }) => msg.role === "user" && !msg.isMeta);
37
37
 
38
38
  const MAX_VISIBLE_ITEMS = 3;
39
39
  const [selectedIndex, setSelectedIndex] = useState(checkpoints.length - 1);
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+
4
+ export interface StatusLineProps {
5
+ permissionMode: string;
6
+ isShellCommand: boolean;
7
+ isBtwActive: boolean;
8
+ }
9
+
10
+ export const StatusLine: React.FC<StatusLineProps> = ({
11
+ permissionMode,
12
+ isShellCommand,
13
+ isBtwActive,
14
+ }) => {
15
+ return (
16
+ <Box paddingRight={1} justifyContent="space-between" width="100%">
17
+ {isBtwActive ? (
18
+ <Text color="gray">
19
+ Mode: <Text color="cyan">BTW</Text> (ESC to dismiss)
20
+ </Text>
21
+ ) : isShellCommand ? (
22
+ <Text color="gray">
23
+ Shell: <Text color="yellow">Run shell command</Text>
24
+ </Text>
25
+ ) : (
26
+ <Text color="gray">
27
+ Mode:{" "}
28
+ <Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
29
+ {permissionMode}
30
+ </Text>{" "}
31
+ (Shift+Tab to cycle)
32
+ </Text>
33
+ )}
34
+ </Box>
35
+ );
36
+ };
@@ -38,4 +38,10 @@ export const AVAILABLE_COMMANDS: SlashCommand[] = [
38
38
  description: "View and manage plugins",
39
39
  handler: () => {}, // Handler here won't be used, actual processing is in the hook
40
40
  },
41
+ {
42
+ id: "btw",
43
+ name: "btw",
44
+ description: "Ask a side question without tool use",
45
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
46
+ },
41
47
  ];
@@ -51,6 +51,7 @@ export interface ChatContextType {
51
51
  images?: Array<{ path: string; mimeType: string }>,
52
52
  longTextMap?: Record<string, string>,
53
53
  ) => Promise<void>;
54
+ askBtw: (question: string) => Promise<string>;
54
55
  abortMessage: () => void;
55
56
  latestTotalTokens: number;
56
57
  // MCP functionality
@@ -77,6 +78,7 @@ export interface ChatContextType {
77
78
  // Permission functionality
78
79
  permissionMode: PermissionMode;
79
80
  setPermissionMode: (mode: PermissionMode) => void;
81
+ allowBypassInCycle: boolean;
80
82
  // Permission confirmation state
81
83
  isConfirmationVisible: boolean;
82
84
  confirmingTool?: {
@@ -113,6 +115,10 @@ export interface ChatContextType {
113
115
  workingDirectory: string;
114
116
  version?: string;
115
117
  workdir?: string;
118
+ btwState: import("../managers/inputReducer.js").BtwState;
119
+ setBtwState: (
120
+ payload: Partial<import("../managers/inputReducer.js").BtwState>,
121
+ ) => void;
116
122
  }
117
123
 
118
124
  const ChatContext = createContext<ChatContextType | null>(null);
@@ -235,8 +241,32 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
235
241
  // Status metadata state
236
242
  const [workingDirectory, setWorkingDirectory] = useState("");
237
243
 
244
+ // /btw state
245
+ const [btwState, setBtwStateInternal] = useState<
246
+ import("../managers/inputReducer.js").BtwState
247
+ >({
248
+ isActive: false,
249
+ question: "",
250
+ isLoading: false,
251
+ });
252
+
253
+ const setBtwState = useCallback(
254
+ (payload: Partial<import("../managers/inputReducer.js").BtwState>) => {
255
+ setBtwStateInternal((prev) => {
256
+ const newState = { ...prev, ...payload };
257
+ if (process.env.NODE_ENV === "test") {
258
+ // console.log("setBtwState", newState);
259
+ }
260
+ return newState;
261
+ });
262
+ },
263
+ [],
264
+ );
265
+
238
266
  // Confirmation too tall state
239
267
  const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
268
+ const allowBypassInCycle =
269
+ !!bypassPermissions || initialPermissionMode === "bypassPermissions";
240
270
 
241
271
  const agentRef = useRef<Agent | null>(null);
242
272
  const taskUpdateTimerRef = useRef<NodeJS.Timeout | null>(null);
@@ -497,6 +527,13 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
497
527
  [isLoading, isCommandRunning],
498
528
  );
499
529
 
530
+ const askBtw = useCallback(async (question: string) => {
531
+ if (!agentRef.current) {
532
+ throw new Error("Agent not initialized");
533
+ }
534
+ return await agentRef.current.askBtw(question);
535
+ }, []);
536
+
500
537
  // Process queued messages when idle
501
538
  useEffect(() => {
502
539
  if (!isLoading && !isCommandRunning && queuedMessages.length > 0) {
@@ -664,10 +701,17 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
664
701
  setIsTaskListVisible((prev) => !prev);
665
702
  }
666
703
 
667
- // Handle ESC key to cancel confirmation
704
+ // Handle ESC key to cancel confirmation or dismiss BTW
668
705
  if (key.escape) {
669
706
  if (isConfirmationVisible) {
670
707
  handleConfirmationCancel();
708
+ } else if (btwState.isActive) {
709
+ setBtwState({
710
+ isActive: false,
711
+ question: "",
712
+ answer: undefined,
713
+ isLoading: false,
714
+ });
671
715
  }
672
716
  }
673
717
  });
@@ -682,6 +726,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
682
726
  queuedMessages,
683
727
  sessionId,
684
728
  sendMessage,
729
+ askBtw,
685
730
  abortMessage,
686
731
  latestTotalTokens,
687
732
  isCompressing,
@@ -698,6 +743,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
698
743
  subagentLatestTokens,
699
744
  permissionMode,
700
745
  setPermissionMode,
746
+ allowBypassInCycle,
701
747
  isConfirmationVisible,
702
748
  confirmingTool,
703
749
  showConfirmation,
@@ -715,6 +761,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
715
761
  workingDirectory,
716
762
  version,
717
763
  workdir,
764
+ btwState,
765
+ setBtwState,
718
766
  };
719
767
 
720
768
  return (
@@ -153,6 +153,47 @@ export const useInputManager = (
153
153
  callbacksRef.current.onImagesStateChange?.(state.attachedImages);
154
154
  }, [state.attachedImages]);
155
155
 
156
+ useEffect(() => {
157
+ if (callbacksRef.current.onBtwStateChange) {
158
+ callbacksRef.current.onBtwStateChange(state.btwState);
159
+ }
160
+ }, [state.btwState]);
161
+
162
+ // Handle /btw side question
163
+ useEffect(() => {
164
+ if (
165
+ state.btwState.isActive &&
166
+ state.btwState.isLoading &&
167
+ state.btwState.question
168
+ ) {
169
+ const askBtw = async () => {
170
+ try {
171
+ const answer = await callbacksRef.current.onAskBtw?.(
172
+ state.btwState.question,
173
+ );
174
+ dispatch({
175
+ type: "SET_BTW_STATE",
176
+ payload: { answer, isLoading: false },
177
+ });
178
+ } catch (error) {
179
+ console.error("Failed to ask side question:", error);
180
+ dispatch({
181
+ type: "SET_BTW_STATE",
182
+ payload: {
183
+ answer: "Error: Failed to get an answer for your side question.",
184
+ isLoading: false,
185
+ },
186
+ });
187
+ }
188
+ };
189
+ askBtw();
190
+ }
191
+ }, [
192
+ state.btwState.isActive,
193
+ state.btwState.isLoading,
194
+ state.btwState.question,
195
+ ]);
196
+
156
197
  // Methods
157
198
  const insertTextAtCursor = useCallback((text: string) => {
158
199
  dispatch({ type: "INSERT_TEXT", payload: text });
@@ -307,6 +348,21 @@ export const useInputManager = (
307
348
  callbacksRef.current.onPermissionModeChange?.(mode);
308
349
  }, []);
309
350
 
351
+ const setAllowBypassInCycle = useCallback((allow: boolean) => {
352
+ dispatch({ type: "SET_ALLOW_BYPASS_IN_CYCLE", payload: allow });
353
+ }, []);
354
+
355
+ const setBtwState = useCallback(
356
+ (payload: Partial<import("../managers/inputReducer.js").BtwState>) => {
357
+ if (callbacksRef.current.onBtwStateChange) {
358
+ callbacksRef.current.onBtwStateChange(payload);
359
+ } else {
360
+ dispatch({ type: "SET_BTW_STATE", payload });
361
+ }
362
+ },
363
+ [],
364
+ );
365
+
310
366
  const addImage = useCallback((imagePath: string, mimeType: string) => {
311
367
  dispatch({ type: "ADD_IMAGE", payload: { path: imagePath, mimeType } });
312
368
  }, []);
@@ -398,6 +454,7 @@ export const useInputManager = (
398
454
  showPluginManager: state.showPluginManager,
399
455
  permissionMode: state.permissionMode,
400
456
  attachedImages: state.attachedImages,
457
+ btwState: state.btwState,
401
458
  isManagerReady: true,
402
459
 
403
460
  // Methods
@@ -437,6 +494,8 @@ export const useInputManager = (
437
494
  setShowStatusCommand,
438
495
  setShowPluginManager,
439
496
  setPermissionMode,
497
+ setAllowBypassInCycle,
498
+ setBtwState,
440
499
 
441
500
  // Image management
442
501
  addImage,
package/src/index.ts CHANGED
@@ -228,6 +228,15 @@ export async function main() {
228
228
  },
229
229
  );
230
230
  })
231
+ .command(
232
+ "update",
233
+ "Update WAVE Code to the latest version",
234
+ {},
235
+ async () => {
236
+ const { updateCommand } = await import("./commands/update.js");
237
+ await updateCommand();
238
+ },
239
+ )
231
240
  .version()
232
241
  .alias("v", "version")
233
242
  .example("$0", "Start CLI with default settings")
@@ -55,6 +55,30 @@ export const handleSubmit = async (
55
55
  .replace(imageRegex, "")
56
56
  .trim();
57
57
 
58
+ if (
59
+ contentWithPlaceholders === "/btw" ||
60
+ contentWithPlaceholders.startsWith("/btw ")
61
+ ) {
62
+ const question = contentWithPlaceholders.startsWith("/btw ")
63
+ ? contentWithPlaceholders.substring(5).trim()
64
+ : "";
65
+
66
+ const payload = {
67
+ isActive: true,
68
+ question,
69
+ isLoading: question !== "",
70
+ };
71
+
72
+ dispatch({
73
+ type: "SET_BTW_STATE",
74
+ payload,
75
+ });
76
+ dispatch({ type: "CLEAR_INPUT" });
77
+ dispatch({ type: "RESET_HISTORY_NAVIGATION" });
78
+ dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
79
+ return;
80
+ }
81
+
58
82
  PromptHistoryManager.addEntry(
59
83
  contentWithPlaceholders,
60
84
  callbacks.sessionId,
@@ -100,8 +124,12 @@ export const cyclePermissionMode = (
100
124
  currentMode: PermissionMode,
101
125
  dispatch: React.Dispatch<InputAction>,
102
126
  callbacks: Partial<InputManagerCallbacks>,
127
+ allowBypassInCycle: boolean = false,
103
128
  ) => {
104
129
  const modes: PermissionMode[] = ["default", "acceptEdits", "plan"];
130
+ if (allowBypassInCycle) {
131
+ modes.push("bypassPermissions");
132
+ }
105
133
  const currentIndex = modes.indexOf(currentMode);
106
134
  const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
107
135
  const nextMode = modes[nextIndex];
@@ -342,6 +370,11 @@ export const handleCommandSelect = (
342
370
  dispatch({ type: "SET_SHOW_STATUS_COMMAND", payload: true });
343
371
  } else if (command === "plugin") {
344
372
  dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
373
+ } else if (command === "btw") {
374
+ dispatch({
375
+ type: "SET_BTW_STATE",
376
+ payload: { isActive: true, question: "", isLoading: false },
377
+ });
345
378
  }
346
379
  }
347
380
  })();
@@ -461,8 +494,10 @@ export const handleSelectorInput = (
461
494
  if (input === " ") {
462
495
  if (state.showFileSelector) {
463
496
  dispatch({ type: "CANCEL_FILE_SELECTOR" });
497
+ return false;
464
498
  } else if (state.showCommandSelector) {
465
499
  dispatch({ type: "CANCEL_COMMAND_SELECTOR" });
500
+ return false;
466
501
  }
467
502
  }
468
503
 
@@ -667,6 +702,46 @@ export const handleInput = async (
667
702
  return true;
668
703
  }
669
704
 
705
+ if (state.btwState.isActive) {
706
+ if (key.escape) {
707
+ dispatch({
708
+ type: "SET_BTW_STATE",
709
+ payload: {
710
+ isActive: false,
711
+ question: "",
712
+ answer: undefined,
713
+ isLoading: false,
714
+ },
715
+ });
716
+ return true;
717
+ }
718
+
719
+ if (key.return) {
720
+ if (state.inputText.trim() && !state.btwState.isLoading) {
721
+ dispatch({
722
+ type: "SET_BTW_STATE",
723
+ payload: {
724
+ question: state.inputText.trim(),
725
+ isLoading: true,
726
+ answer: undefined,
727
+ },
728
+ });
729
+ dispatch({ type: "CLEAR_INPUT" });
730
+ }
731
+ return true;
732
+ }
733
+
734
+ // Handle normal input for the question
735
+ return await handleNormalInput(
736
+ state,
737
+ dispatch,
738
+ callbacks,
739
+ input,
740
+ key,
741
+ clearImages,
742
+ );
743
+ }
744
+
670
745
  if (key.escape) {
671
746
  if (
672
747
  !(
@@ -678,7 +753,8 @@ export const handleInput = async (
678
753
  state.showRewindManager ||
679
754
  state.showHelp ||
680
755
  state.showStatusCommand ||
681
- state.showPluginManager
756
+ state.showPluginManager ||
757
+ state.btwState.isActive
682
758
  )
683
759
  ) {
684
760
  callbacks.onAbortMessage?.();
@@ -687,7 +763,12 @@ export const handleInput = async (
687
763
  }
688
764
 
689
765
  if (key.tab && key.shift) {
690
- cyclePermissionMode(state.permissionMode, dispatch, callbacks);
766
+ cyclePermissionMode(
767
+ state.permissionMode,
768
+ dispatch,
769
+ callbacks,
770
+ state.allowBypassInCycle,
771
+ );
691
772
  return true;
692
773
  }
693
774
 
@@ -700,7 +781,8 @@ export const handleInput = async (
700
781
  state.showRewindManager ||
701
782
  state.showHelp ||
702
783
  state.showStatusCommand ||
703
- state.showPluginManager
784
+ state.showPluginManager ||
785
+ state.btwState.isActive
704
786
  ) {
705
787
  if (
706
788
  state.showBackgroundTaskManager ||
@@ -708,7 +790,8 @@ export const handleInput = async (
708
790
  state.showRewindManager ||
709
791
  state.showHelp ||
710
792
  state.showStatusCommand ||
711
- state.showPluginManager
793
+ state.showPluginManager ||
794
+ state.btwState.isActive
712
795
  ) {
713
796
  return true;
714
797
  }
@@ -12,6 +12,13 @@ export interface AttachedImage {
12
12
  mimeType: string;
13
13
  }
14
14
 
15
+ export interface BtwState {
16
+ isActive: boolean;
17
+ question: string;
18
+ answer?: string;
19
+ isLoading: boolean;
20
+ }
21
+
15
22
  export interface InputManagerCallbacks {
16
23
  onInputTextChange?: (text: string) => void;
17
24
  onCursorPositionChange?: (position: number) => void;
@@ -43,6 +50,9 @@ export interface InputManagerCallbacks {
43
50
  onAbortMessage?: () => void;
44
51
  onBackgroundCurrentTask?: () => void;
45
52
  onPermissionModeChange?: (mode: PermissionMode) => void;
53
+ onAskBtw?: (question: string) => Promise<string>;
54
+ btwState?: BtwState;
55
+ onBtwStateChange?: (payload: Partial<BtwState>) => void;
46
56
  sessionId?: string;
47
57
  workdir?: string;
48
58
  getFullMessageThread?: () => Promise<{
@@ -75,6 +85,7 @@ export interface InputState {
75
85
  showStatusCommand: boolean;
76
86
  showPluginManager: boolean;
77
87
  permissionMode: PermissionMode;
88
+ allowBypassInCycle: boolean;
78
89
  selectorJustUsed: boolean;
79
90
  isPasting: boolean;
80
91
  pasteBuffer: string;
@@ -84,6 +95,7 @@ export interface InputState {
84
95
  originalInputText: string;
85
96
  originalLongTextMap: Record<string, string>;
86
97
  isFileSearching: boolean;
98
+ btwState: BtwState;
87
99
  }
88
100
 
89
101
  export const initialState: InputState = {
@@ -109,6 +121,7 @@ export const initialState: InputState = {
109
121
  showStatusCommand: false,
110
122
  showPluginManager: false,
111
123
  permissionMode: "default",
124
+ allowBypassInCycle: false,
112
125
  selectorJustUsed: false,
113
126
  isPasting: false,
114
127
  pasteBuffer: "",
@@ -118,6 +131,11 @@ export const initialState: InputState = {
118
131
  originalInputText: "",
119
132
  originalLongTextMap: {},
120
133
  isFileSearching: false,
134
+ btwState: {
135
+ isActive: false,
136
+ question: "",
137
+ isLoading: false,
138
+ },
121
139
  };
122
140
 
123
141
  export type InputAction =
@@ -146,6 +164,7 @@ export type InputAction =
146
164
  | { type: "SET_SHOW_STATUS_COMMAND"; payload: boolean }
147
165
  | { type: "SET_SHOW_PLUGIN_MANAGER"; payload: boolean }
148
166
  | { type: "SET_PERMISSION_MODE"; payload: PermissionMode }
167
+ | { type: "SET_ALLOW_BYPASS_IN_CYCLE"; payload: boolean }
149
168
  | { type: "SET_SELECTOR_JUST_USED"; payload: boolean }
150
169
  | { type: "INSERT_TEXT_WITH_PLACEHOLDER"; payload: string }
151
170
  | { type: "CLEAR_LONG_TEXT_MAP" }
@@ -160,7 +179,8 @@ export type InputAction =
160
179
  | { type: "SET_HISTORY_ENTRIES"; payload: PromptEntry[] }
161
180
  | { type: "NAVIGATE_HISTORY"; payload: "up" | "down" }
162
181
  | { type: "RESET_HISTORY_NAVIGATION" }
163
- | { type: "SELECT_HISTORY_ENTRY"; payload: PromptEntry };
182
+ | { type: "SELECT_HISTORY_ENTRY"; payload: PromptEntry }
183
+ | { type: "SET_BTW_STATE"; payload: Partial<BtwState> };
164
184
 
165
185
  export function inputReducer(
166
186
  state: InputState,
@@ -340,6 +360,8 @@ export function inputReducer(
340
360
  };
341
361
  case "SET_PERMISSION_MODE":
342
362
  return { ...state, permissionMode: action.payload };
363
+ case "SET_ALLOW_BYPASS_IN_CYCLE":
364
+ return { ...state, allowBypassInCycle: action.payload };
343
365
  case "SET_SELECTOR_JUST_USED":
344
366
  return { ...state, selectorJustUsed: action.payload };
345
367
  case "INSERT_TEXT_WITH_PLACEHOLDER": {
@@ -483,6 +505,14 @@ export function inputReducer(
483
505
  originalInputText: "",
484
506
  originalLongTextMap: {},
485
507
  };
508
+ case "SET_BTW_STATE":
509
+ return {
510
+ ...state,
511
+ btwState: {
512
+ ...state.btwState,
513
+ ...action.payload,
514
+ },
515
+ };
486
516
  default:
487
517
  return state;
488
518
  }
@@ -0,0 +1,8 @@
1
+ import semver from "semver";
2
+
3
+ export function isUpdateAvailable(
4
+ currentVersion: string,
5
+ latestVersion: string,
6
+ ): boolean {
7
+ return semver.gt(latestVersion, currentVersion);
8
+ }