wave-code 0.19.9 → 1.0.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.
Files changed (83) hide show
  1. package/dist/cli.js +20 -1
  2. package/dist/components/App.js +7 -0
  3. package/dist/components/BtwDisplay.js +13 -3
  4. package/dist/components/ChatInterface.js +21 -6
  5. package/dist/components/HelpView.js +6 -0
  6. package/dist/components/InputBox.d.ts +1 -2
  7. package/dist/components/InputBox.js +16 -5
  8. package/dist/components/LoadingIndicator.d.ts +1 -2
  9. package/dist/components/LoadingIndicator.js +2 -2
  10. package/dist/components/Markdown.js +13 -16
  11. package/dist/components/MessageList.d.ts +2 -1
  12. package/dist/components/MessageList.js +2 -2
  13. package/dist/components/RewindCommand.js +4 -2
  14. package/dist/components/StatusLine.d.ts +0 -2
  15. package/dist/components/StatusLine.js +6 -6
  16. package/dist/components/TaskList.js +2 -1
  17. package/dist/components/ToolDisplay.d.ts +1 -0
  18. package/dist/components/ToolDisplay.js +17 -9
  19. package/dist/constants/commands.js +0 -6
  20. package/dist/contexts/useChat.d.ts +3 -5
  21. package/dist/contexts/useChat.js +242 -82
  22. package/dist/daemon-cli.d.ts +10 -0
  23. package/dist/daemon-cli.js +15 -0
  24. package/dist/hooks/useInputManager.d.ts +1 -0
  25. package/dist/hooks/useInputManager.js +120 -40
  26. package/dist/index.js +10 -0
  27. package/dist/managers/inputHandlers.js +55 -30
  28. package/dist/managers/inputReducer.d.ts +22 -22
  29. package/dist/managers/inputReducer.js +361 -177
  30. package/dist/print-cli.js +36 -10
  31. package/dist/stdio/agentBridge.d.ts +23 -0
  32. package/dist/stdio/agentBridge.js +151 -18
  33. package/dist/stdio/daemonServer.d.ts +67 -0
  34. package/dist/stdio/daemonServer.js +191 -0
  35. package/dist/stdio/index.d.ts +2 -0
  36. package/dist/stdio/index.js +2 -0
  37. package/dist/stdio/jsonRpcConnection.d.ts +30 -0
  38. package/dist/stdio/jsonRpcConnection.js +127 -0
  39. package/dist/stdio/protocol.d.ts +2 -2
  40. package/dist/stdio/stdioServer.d.ts +2 -7
  41. package/dist/stdio/stdioServer.js +9 -100
  42. package/dist/utils/bracketedPaste.d.ts +39 -0
  43. package/dist/utils/bracketedPaste.js +122 -0
  44. package/dist/utils/markdownTable.d.ts +34 -0
  45. package/dist/utils/markdownTable.js +302 -0
  46. package/dist/utils/rewindCheckpoints.d.ts +8 -0
  47. package/dist/utils/rewindCheckpoints.js +15 -0
  48. package/dist/utils/throttle.d.ts +3 -3
  49. package/dist/utils/worktree.d.ts +8 -0
  50. package/dist/utils/worktree.js +32 -1
  51. package/package.json +4 -2
  52. package/src/cli.tsx +20 -1
  53. package/src/components/App.tsx +5 -0
  54. package/src/components/BtwDisplay.tsx +36 -12
  55. package/src/components/ChatInterface.tsx +26 -11
  56. package/src/components/HelpView.tsx +6 -0
  57. package/src/components/InputBox.tsx +21 -10
  58. package/src/components/LoadingIndicator.tsx +1 -4
  59. package/src/components/Markdown.tsx +15 -18
  60. package/src/components/MessageList.tsx +6 -0
  61. package/src/components/RewindCommand.tsx +4 -2
  62. package/src/components/StatusLine.tsx +0 -10
  63. package/src/components/TaskList.tsx +2 -1
  64. package/src/components/ToolDisplay.tsx +17 -6
  65. package/src/constants/commands.ts +0 -6
  66. package/src/contexts/useChat.tsx +310 -95
  67. package/src/daemon-cli.ts +17 -0
  68. package/src/hooks/useInputManager.ts +135 -43
  69. package/src/index.ts +12 -0
  70. package/src/managers/inputHandlers.ts +55 -32
  71. package/src/managers/inputReducer.ts +442 -214
  72. package/src/print-cli.ts +48 -11
  73. package/src/stdio/agentBridge.ts +213 -18
  74. package/src/stdio/daemonServer.ts +212 -0
  75. package/src/stdio/index.ts +2 -0
  76. package/src/stdio/jsonRpcConnection.ts +160 -0
  77. package/src/stdio/protocol.ts +5 -2
  78. package/src/stdio/stdioServer.ts +14 -120
  79. package/src/utils/bracketedPaste.ts +170 -0
  80. package/src/utils/markdownTable.ts +359 -0
  81. package/src/utils/rewindCheckpoints.ts +15 -0
  82. package/src/utils/throttle.ts +8 -8
  83. package/src/utils/worktree.ts +50 -1
@@ -1,10 +1,20 @@
1
- import { useEffect, useReducer, useCallback } from "react";
2
- import { inputReducer, initialState, } from "../managers/inputReducer.js";
1
+ import { useEffect, useReducer, useCallback, useRef } from "react";
2
+ import { inputReducer, initialState, ESC_DOUBLE_PRESS_TIMEOUT_MS, btwOverlayActiveRef, } from "../managers/inputReducer.js";
3
+ import { createBracketedPasteDetector } from "../utils/bracketedPaste.js";
3
4
  import { searchFiles as searchFilesUtil, PromptHistoryManager, } from "wave-agent-sdk";
4
5
  import * as handlers from "../managers/inputHandlers.js";
5
6
  export const useInputManager = (callbacks = {}) => {
6
7
  const [state, dispatch] = useReducer(inputReducer, initialState);
7
- const { onInputTextChange, onCursorPositionChange, onFileSelectorStateChange, onCommandSelectorStateChange, onHistorySearchStateChange, onBackgroundTaskManagerStateChange, onMcpManagerStateChange, onRewindManagerStateChange, onHelpStateChange, onStatusCommandStateChange, onPluginManagerStateChange, onModelSelectorStateChange, onWorkflowManagerStateChange, onImagesStateChange, onSendMessage, onHasSlashCommand, onAbortMessage, onBackgroundCurrentTask, onPermissionModeChange, onAskBtw, sessionId, workdir, getFullMessageThread, logger, hasQueuedMessages: hasQueuedMessagesProp, onRecallQueuedMessage, onClearMessages, onCompact, onGoalCommand, } = callbacks;
8
+ // Detects bracketed paste (DECSET 2004) markers so pasted text is inserted
9
+ // without triggering submit — a pasted trailing \r must not be treated as
10
+ // Enter (see utils/bracketedPaste.ts).
11
+ const pasteDetectorRef = useRef(createBracketedPasteDetector());
12
+ // Abort plumbing for the /btw side question: ABORT_BTW (set when the overlay
13
+ // is dismissed while loading) aborts the in-flight onAskBtw call; the
14
+ // dismissed ref suppresses the late SET_BTW_STATE dispatch / error logging.
15
+ const btwAbortRef = useRef(null);
16
+ const btwDismissedRef = useRef(false);
17
+ const { onInputTextChange, onCursorPositionChange, onFileSelectorStateChange, onCommandSelectorStateChange, onHistorySearchStateChange, onBackgroundTaskManagerStateChange, onMcpManagerStateChange, onRewindManagerStateChange, onHelpStateChange, onStatusCommandStateChange, onPluginManagerStateChange, onModelSelectorStateChange, onWorkflowManagerStateChange, onImagesStateChange, onSendMessage, onHasSlashCommand, onAbortMessage, onBackgroundCurrentTask, onPermissionModeChange, onAskBtw, sessionId, workdir, getFullMessageThread, logger, hasQueuedMessages: hasQueuedMessagesProp, onRecallQueuedMessage, onClearMessages, onCompact, isIdle: isIdleProp, } = callbacks;
8
18
  // Handle debounced file search
9
19
  useEffect(() => {
10
20
  if (state.showFileSelector) {
@@ -24,22 +34,16 @@ export const useInputManager = (callbacks = {}) => {
24
34
  return () => clearTimeout(timer);
25
35
  }
26
36
  }, [state.showFileSelector, state.fileSearchQuery]);
27
- // Handle paste debouncing
37
+ // Auto-expire the double-Esc clear pending flag after the timeout window
38
+ // (aligned with Claude Code's useDoublePress timeout-based expiry).
28
39
  useEffect(() => {
29
- if (state.isPasting) {
30
- const pasteDebounceDelay = parseInt(process.env.PASTE_DEBOUNCE_MS || "30", 10);
31
- const timer = setTimeout(() => {
32
- const processedInput = state.pasteBuffer.replace(/\r/g, "\n");
33
- dispatch({
34
- type: "INSERT_TEXT_WITH_PLACEHOLDER",
35
- payload: processedInput,
36
- });
37
- dispatch({ type: "END_PASTE" });
38
- dispatch({ type: "RESET_HISTORY_NAVIGATION" });
39
- }, pasteDebounceDelay);
40
- return () => clearTimeout(timer);
41
- }
42
- }, [state.isPasting, state.pasteBuffer]);
40
+ if (!state.escClearPending)
41
+ return;
42
+ const timer = setTimeout(() => {
43
+ dispatch({ type: "RESET_ESC_CLEAR_PENDING" });
44
+ }, ESC_DOUBLE_PRESS_TIMEOUT_MS);
45
+ return () => clearTimeout(timer);
46
+ }, [state.escClearPending]);
43
47
  // Sync state changes with callbacks
44
48
  useEffect(() => {
45
49
  onInputTextChange?.(state.inputText);
@@ -65,28 +69,57 @@ export const useInputManager = (callbacks = {}) => {
65
69
  case "ABORT_MESSAGE":
66
70
  onAbortMessage?.();
67
71
  break;
72
+ case "SAVE_PROMPT_HISTORY":
73
+ PromptHistoryManager.addEntry(effect.content, sessionId, effect.longTextMap, workdir).catch((err) => {
74
+ logger?.error("Failed to save prompt history", err);
75
+ });
76
+ break;
68
77
  case "BACKGROUND_CURRENT_TASK":
69
78
  onBackgroundCurrentTask?.();
70
79
  break;
71
- case "ASK_BTW":
80
+ case "ASK_BTW": {
81
+ const controller = new AbortController();
82
+ btwAbortRef.current = controller;
83
+ btwDismissedRef.current = false;
72
84
  try {
73
- const answer = await onAskBtw?.(effect.question);
74
- dispatch({
75
- type: "SET_BTW_STATE",
76
- payload: { answer, isLoading: false },
85
+ const answer = await onAskBtw?.(effect.question, controller.signal, (content) => {
86
+ // Stream partial answers into the overlay so the user sees
87
+ // the response grow in real time (same visual language as
88
+ // assistant text / thinking blocks) instead of a static
89
+ // "Answering..." indicator.
90
+ if (!btwDismissedRef.current) {
91
+ dispatch({
92
+ type: "SET_BTW_STATE",
93
+ payload: { answer: content, isLoading: true },
94
+ });
95
+ }
77
96
  });
97
+ if (!btwDismissedRef.current) {
98
+ dispatch({
99
+ type: "SET_BTW_STATE",
100
+ payload: { answer, isLoading: false },
101
+ });
102
+ }
78
103
  }
79
104
  catch (error) {
80
- console.error("Failed to ask side question:", error);
81
- dispatch({
82
- type: "SET_BTW_STATE",
83
- payload: {
84
- answer: "Error: Failed to get an answer for your side question.",
85
- isLoading: false,
86
- },
87
- });
105
+ if (!btwDismissedRef.current) {
106
+ console.error("Failed to ask side question:", error);
107
+ dispatch({
108
+ type: "SET_BTW_STATE",
109
+ payload: {
110
+ answer: "Error: Failed to get an answer for your side question.",
111
+ isLoading: false,
112
+ },
113
+ });
114
+ }
88
115
  }
89
116
  break;
117
+ }
118
+ case "ABORT_BTW":
119
+ btwDismissedRef.current = true;
120
+ btwAbortRef.current?.abort();
121
+ btwAbortRef.current = null;
122
+ break;
90
123
  case "PERMISSION_MODE_CHANGE":
91
124
  onPermissionModeChange?.(effect.mode);
92
125
  break;
@@ -156,6 +189,18 @@ export const useInputManager = (callbacks = {}) => {
156
189
  else if (command === "logout") {
157
190
  dispatch({ type: "SET_SHOW_LOGIN_COMMAND", payload: true });
158
191
  }
192
+ else if (command === "btw") {
193
+ // Bare /btw executed via the command selector — show usage
194
+ // (aligned with Claude Code's empty-args message).
195
+ dispatch({
196
+ type: "SET_BTW_STATE",
197
+ payload: {
198
+ question: "",
199
+ isLoading: false,
200
+ answer: "Usage: /btw <your question>",
201
+ },
202
+ });
203
+ }
159
204
  else if (command === "plugin") {
160
205
  dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
161
206
  }
@@ -171,9 +216,6 @@ export const useInputManager = (callbacks = {}) => {
171
216
  else if (command === "compact") {
172
217
  await onCompact?.(effect.args);
173
218
  }
174
- else if (command === "goal") {
175
- await onGoalCommand?.(effect.args);
176
- }
177
219
  }
178
220
  break;
179
221
  case "RECALL_QUEUED_MESSAGE":
@@ -201,7 +243,6 @@ export const useInputManager = (callbacks = {}) => {
201
243
  onRecallQueuedMessage,
202
244
  onClearMessages,
203
245
  onCompact,
204
- onGoalCommand,
205
246
  ]);
206
247
  useEffect(() => {
207
248
  onFileSelectorStateChange?.(state.showFileSelector, state.filteredFiles, state.fileSearchQuery, state.atPosition);
@@ -254,7 +295,13 @@ export const useInputManager = (callbacks = {}) => {
254
295
  useEffect(() => {
255
296
  onImagesStateChange?.(state.attachedImages);
256
297
  }, [state.attachedImages, onImagesStateChange]);
257
- // /btw side question is handled via pendingEffect "ASK_BTW" above
298
+ // Keep the shared overlay-active flag in sync so App's Ctrl+C exit handler
299
+ // can defer to the /btw overlay (ink runs ALL useInput handlers per keypress
300
+ // with no propagation control).
301
+ useEffect(() => {
302
+ btwOverlayActiveRef.current =
303
+ state.btwState.question !== "" || state.btwState.answer !== undefined;
304
+ }, [state.btwState.question, state.btwState.answer]);
258
305
  // Methods
259
306
  const insertTextAtCursor = useCallback((text) => {
260
307
  dispatch({ type: "INSERT_TEXT", payload: text });
@@ -375,9 +422,10 @@ export const useInputManager = (callbacks = {}) => {
375
422
  key: {},
376
423
  hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
377
424
  hasQueuedMessages: hasQueuedMessagesProp ?? false,
425
+ isIdle: isIdleProp ?? false,
378
426
  },
379
427
  });
380
- }, [onHasSlashCommand, hasQueuedMessagesProp]);
428
+ }, [onHasSlashCommand, hasQueuedMessagesProp, isIdleProp]);
381
429
  const handleSubmit = useCallback(async () => {
382
430
  dispatch({
383
431
  type: "HANDLE_KEY",
@@ -386,9 +434,10 @@ export const useInputManager = (callbacks = {}) => {
386
434
  key: { return: true },
387
435
  hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
388
436
  hasQueuedMessages: hasQueuedMessagesProp ?? false,
437
+ isIdle: isIdleProp ?? false,
389
438
  },
390
439
  });
391
- }, [onHasSlashCommand, hasQueuedMessagesProp]);
440
+ }, [onHasSlashCommand, hasQueuedMessagesProp, isIdleProp]);
392
441
  const expandLongTextPlaceholders = useCallback((text) => {
393
442
  return handlers.expandLongTextPlaceholders(text, state.longTextMap);
394
443
  }, [state.longTextMap]);
@@ -396,17 +445,47 @@ export const useInputManager = (callbacks = {}) => {
396
445
  dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
397
446
  }, []);
398
447
  const handleInput = useCallback(async (input, key) => {
448
+ const result = pasteDetectorRef.current.process(input);
449
+ if (result.kind === "consume") {
450
+ // Content of an in-flight bracketed paste (or an empty paste):
451
+ // hold it, never submit or insert prematurely.
452
+ return true;
453
+ }
454
+ if (result.kind === "paste") {
455
+ if (result.leadingInput) {
456
+ dispatch({
457
+ type: "HANDLE_KEY",
458
+ payload: {
459
+ input: result.leadingInput,
460
+ key,
461
+ hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
462
+ hasQueuedMessages: hasQueuedMessagesProp ?? false,
463
+ isIdle: isIdleProp ?? false,
464
+ },
465
+ });
466
+ }
467
+ if (result.text !== "") {
468
+ // Insert-only: \r → \n normalizes CRLF terminals, matching the
469
+ // canonical paste path (inputHandlers.handlePasteInput).
470
+ dispatch({
471
+ type: "INSERT_TEXT_WITH_PLACEHOLDER",
472
+ payload: result.text.replace(/\r/g, "\n"),
473
+ });
474
+ }
475
+ return true;
476
+ }
399
477
  dispatch({
400
478
  type: "HANDLE_KEY",
401
479
  payload: {
402
- input,
480
+ input: result.input,
403
481
  key,
404
482
  hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
405
483
  hasQueuedMessages: hasQueuedMessagesProp ?? false,
484
+ isIdle: isIdleProp ?? false,
406
485
  },
407
486
  });
408
487
  return true;
409
- }, [onHasSlashCommand, hasQueuedMessagesProp]);
488
+ }, [onHasSlashCommand, hasQueuedMessagesProp, isIdleProp]);
410
489
  return {
411
490
  // State
412
491
  inputText: state.inputText,
@@ -433,6 +512,7 @@ export const useInputManager = (callbacks = {}) => {
433
512
  permissionMode: state.permissionMode,
434
513
  attachedImages: state.attachedImages,
435
514
  btwState: state.btwState,
515
+ escClearPending: state.escClearPending,
436
516
  isManagerReady: true,
437
517
  // Methods
438
518
  insertTextAtCursor,
package/dist/index.js CHANGED
@@ -44,6 +44,11 @@ export async function main() {
44
44
  type: "boolean",
45
45
  default: false,
46
46
  global: false,
47
+ })
48
+ .option("daemon", {
49
+ description: "Start as a background daemon (JSON-RPC over a unix socket at PATH)",
50
+ type: "string",
51
+ global: false,
47
52
  })
48
53
  .option("show-stats", {
49
54
  description: "Show timing and usage statistics in print mode",
@@ -300,6 +305,11 @@ export async function main() {
300
305
  const { startStdioCli } = await import("./stdio-cli.js");
301
306
  return startStdioCli();
302
307
  }
308
+ // Handle daemon mode (remote background sessions)
309
+ if (typeof argv.daemon === "string") {
310
+ const { startDaemonCli } = await import("./daemon-cli.js");
311
+ return startDaemonCli(argv.daemon);
312
+ }
303
313
  await startCli({
304
314
  restoreSessionId: argv.restore,
305
315
  continueLastSession: argv.continue,
@@ -33,15 +33,27 @@ export const handleSubmit = async (state, dispatch, callbacks, attachedImagesOve
33
33
  const question = contentWithPlaceholders.startsWith("/btw ")
34
34
  ? contentWithPlaceholders.substring(5).trim()
35
35
  : "";
36
- const payload = {
37
- isActive: true,
38
- question,
39
- isLoading: question !== "",
40
- };
41
- dispatch({
42
- type: "SET_BTW_STATE",
43
- payload,
44
- });
36
+ if (question) {
37
+ dispatch({
38
+ type: "SET_BTW_STATE",
39
+ payload: {
40
+ question,
41
+ isLoading: true,
42
+ answer: undefined,
43
+ },
44
+ });
45
+ }
46
+ else {
47
+ // Bare /btw — show usage (aligned with Claude Code)
48
+ dispatch({
49
+ type: "SET_BTW_STATE",
50
+ payload: {
51
+ question: "",
52
+ isLoading: false,
53
+ answer: "Usage: /btw <your question>",
54
+ },
55
+ });
56
+ }
45
57
  dispatch({ type: "CLEAR_INPUT" });
46
58
  dispatch({ type: "RESET_HISTORY_NAVIGATION" });
47
59
  dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
@@ -196,15 +208,12 @@ export const processSelectorInput = (state, dispatch, char) => {
196
208
  };
197
209
  export const handlePasteInput = (state, dispatch, callbacks, input) => {
198
210
  const inputString = input;
199
- const isPasteOperation = inputString.length > 1 ||
200
- inputString.includes("\n") ||
201
- inputString.includes("\r");
202
- if (isPasteOperation) {
203
- // Dispatch a single action type; the reducer determines start vs append
204
- // by checking pasteBuffer, avoiding stale state issues
211
+ if (inputString.length > 1) {
212
+ // Multi-char chunk: insert immediately (\r → \n normalizes CRLF
213
+ // terminals), matching the reducer's HANDLE_KEY path.
205
214
  dispatch({
206
- type: "APPEND_PASTE_CHUNK",
207
- payload: { chunk: inputString, cursorPosition: state.cursorPosition },
215
+ type: "INSERT_TEXT_WITH_PLACEHOLDER",
216
+ payload: inputString.replace(/\r/g, "\n"),
208
217
  });
209
218
  }
210
219
  else {
@@ -264,6 +273,18 @@ export const handleCommandSelect = (state, dispatch, callbacks, command) => {
264
273
  else if (command === "status") {
265
274
  dispatch({ type: "SET_SHOW_STATUS_COMMAND", payload: true });
266
275
  }
276
+ else if (command === "btw") {
277
+ // Bare /btw executed via the command selector — show usage
278
+ // (aligned with Claude Code's empty-args message).
279
+ dispatch({
280
+ type: "SET_BTW_STATE",
281
+ payload: {
282
+ question: "",
283
+ isLoading: false,
284
+ answer: "Usage: /btw <your question>",
285
+ },
286
+ });
287
+ }
267
288
  else if (command === "plugin") {
268
289
  dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
269
290
  }
@@ -279,9 +300,6 @@ export const handleCommandSelect = (state, dispatch, callbacks, command) => {
279
300
  else if (command === "compact") {
280
301
  await callbacks.onCompact?.();
281
302
  }
282
- else if (command === "goal") {
283
- await callbacks.onGoalCommand?.();
284
- }
285
303
  }
286
304
  })();
287
305
  dispatch({ type: "CANCEL_COMMAND_SELECTOR" });
@@ -510,16 +528,23 @@ export const handleNormalInput = async (state, dispatch, callbacks, input, key,
510
528
  return false;
511
529
  };
512
530
  export const handleInput = async (state, dispatch, callbacks, input, key, clearImages) => {
513
- // Handle ESC to dismiss btw answer
514
- if (key.escape && state.btwState.question && !state.btwState.isLoading) {
515
- dispatch({
516
- type: "SET_BTW_STATE",
517
- payload: {
518
- question: "",
519
- answer: undefined,
520
- isLoading: false,
521
- },
522
- });
531
+ // /btw overlay handling (mirrors inputReducer's HANDLE_KEY block; this
532
+ // handler is not wired, kept in sync for consistency). Active while a
533
+ // question is displayed, or the bare-/btw usage message shows. Only
534
+ // Escape dismisses; every other key is ignored.
535
+ if (state.btwState.question || state.btwState.answer) {
536
+ if (key.escape) {
537
+ dispatch({
538
+ type: "SET_BTW_STATE",
539
+ payload: {
540
+ question: "",
541
+ answer: undefined,
542
+ isLoading: false,
543
+ },
544
+ });
545
+ return true;
546
+ }
547
+ // Any other key while the overlay is up is ignored
523
548
  return true;
524
549
  }
525
550
  if (key.escape) {
@@ -10,6 +10,16 @@ export interface BtwState {
10
10
  answer?: string;
11
11
  isLoading: boolean;
12
12
  }
13
+ /**
14
+ * True while the /btw overlay is up (a question is on display, loading or
15
+ * answered). App's Ctrl+C exit handler checks this so Ctrl+C does not quit
16
+ * the app while the overlay owns the keys. Synced from useInputManager via
17
+ * an effect.
18
+ */
19
+ export declare const btwOverlayActiveRef: {
20
+ current: boolean;
21
+ };
22
+ export declare const ESC_DOUBLE_PRESS_TIMEOUT_MS = 1000;
13
23
  export type PendingEffect = {
14
24
  type: "SEND_MESSAGE";
15
25
  content: string;
@@ -18,6 +28,10 @@ export type PendingEffect = {
18
28
  mimeType: string;
19
29
  }>;
20
30
  longTextMap: Record<string, string>;
31
+ } | {
32
+ type: "SAVE_PROMPT_HISTORY";
33
+ content: string;
34
+ longTextMap: Record<string, string>;
21
35
  } | {
22
36
  type: "ABORT_MESSAGE";
23
37
  } | {
@@ -25,6 +39,8 @@ export type PendingEffect = {
25
39
  } | {
26
40
  type: "ASK_BTW";
27
41
  question: string;
42
+ } | {
43
+ type: "ABORT_BTW";
28
44
  } | {
29
45
  type: "PERMISSION_MODE_CHANGE";
30
46
  mode: PermissionMode;
@@ -62,10 +78,9 @@ export interface InputManagerCallbacks {
62
78
  onAbortMessage?: () => void;
63
79
  onBackgroundCurrentTask?: () => void;
64
80
  onPermissionModeChange?: (mode: PermissionMode) => void;
65
- onAskBtw?: (question: string) => Promise<string>;
81
+ onAskBtw?: (question: string, abortSignal?: AbortSignal, onContent?: (content: string) => void) => Promise<string>;
66
82
  onClearMessages?: () => Promise<void>;
67
83
  onCompact?: (instructions?: string) => Promise<void>;
68
- onGoalCommand?: (args?: string) => Promise<void>;
69
84
  sessionId?: string;
70
85
  workdir?: string;
71
86
  getFullMessageThread?: () => Promise<{
@@ -74,6 +89,7 @@ export interface InputManagerCallbacks {
74
89
  }>;
75
90
  logger?: Logger;
76
91
  hasQueuedMessages?: boolean;
92
+ isIdle?: boolean;
77
93
  onRecallQueuedMessage?: () => void;
78
94
  }
79
95
  export interface InputState {
@@ -103,9 +119,6 @@ export interface InputState {
103
119
  showWorkflowManager: boolean;
104
120
  permissionMode: PermissionMode;
105
121
  selectorJustUsed: boolean;
106
- isPasting: boolean;
107
- pasteBuffer: string;
108
- initialPasteCursorPosition: number;
109
122
  history: PromptEntry[];
110
123
  historyIndex: number;
111
124
  originalInputText: string;
@@ -113,6 +126,7 @@ export interface InputState {
113
126
  isFileSearching: boolean;
114
127
  btwState: BtwState;
115
128
  pendingEffect: PendingEffect | null;
129
+ escClearPending: boolean;
116
130
  }
117
131
  export declare const initialState: InputState;
118
132
  export type InputAction = {
@@ -206,23 +220,6 @@ export type InputAction = {
206
220
  type: "CLEAR_LONG_TEXT_MAP";
207
221
  } | {
208
222
  type: "CLEAR_INPUT";
209
- } | {
210
- type: "START_PASTE";
211
- payload: {
212
- buffer: string;
213
- cursorPosition: number;
214
- };
215
- } | {
216
- type: "APPEND_PASTE_BUFFER";
217
- payload: string;
218
- } | {
219
- type: "APPEND_PASTE_CHUNK";
220
- payload: {
221
- chunk: string;
222
- cursorPosition: number;
223
- };
224
- } | {
225
- type: "END_PASTE";
226
223
  } | {
227
224
  type: "ADD_IMAGE_AND_INSERT_PLACEHOLDER";
228
225
  payload: {
@@ -254,6 +251,8 @@ export type InputAction = {
254
251
  payload: Partial<BtwState>;
255
252
  } | {
256
253
  type: "CLEAR_PENDING_EFFECT";
254
+ } | {
255
+ type: "RESET_ESC_CLEAR_PENDING";
257
256
  } | {
258
257
  type: "HANDLE_KEY";
259
258
  payload: {
@@ -261,6 +260,7 @@ export type InputAction = {
261
260
  key: Key;
262
261
  hasSlashCommand: (cmd: string) => boolean;
263
262
  hasQueuedMessages?: boolean;
263
+ isIdle?: boolean;
264
264
  };
265
265
  };
266
266
  export declare function inputReducer(state: InputState, action: InputAction): InputState;