wave-code 1.0.0 → 1.0.2

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 (72) 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 +25 -8
  5. package/dist/components/InputBox.d.ts +1 -3
  6. package/dist/components/InputBox.js +12 -9
  7. package/dist/components/LoadingIndicator.d.ts +1 -2
  8. package/dist/components/LoadingIndicator.js +2 -2
  9. package/dist/components/LoginCommand.js +4 -2
  10. package/dist/components/Markdown.js +13 -16
  11. package/dist/components/Notifications.d.ts +7 -0
  12. package/dist/components/Notifications.js +9 -0
  13. package/dist/components/StatusLine.d.ts +0 -4
  14. package/dist/components/StatusLine.js +6 -10
  15. package/dist/components/TaskList.js +2 -1
  16. package/dist/components/ToolDisplay.d.ts +1 -0
  17. package/dist/components/ToolDisplay.js +17 -9
  18. package/dist/constants/commands.js +0 -6
  19. package/dist/contexts/useChat.d.ts +4 -6
  20. package/dist/contexts/useChat.js +253 -110
  21. package/dist/daemon-cli.d.ts +10 -0
  22. package/dist/daemon-cli.js +15 -0
  23. package/dist/hooks/useInputManager.js +99 -22
  24. package/dist/index.js +10 -0
  25. package/dist/managers/inputHandlers.js +50 -22
  26. package/dist/managers/inputReducer.d.ts +12 -2
  27. package/dist/managers/inputReducer.js +57 -9
  28. package/dist/stdio/agentBridge.d.ts +23 -0
  29. package/dist/stdio/agentBridge.js +134 -16
  30. package/dist/stdio/daemonServer.d.ts +67 -0
  31. package/dist/stdio/daemonServer.js +191 -0
  32. package/dist/stdio/index.d.ts +2 -0
  33. package/dist/stdio/index.js +2 -0
  34. package/dist/stdio/jsonRpcConnection.d.ts +30 -0
  35. package/dist/stdio/jsonRpcConnection.js +127 -0
  36. package/dist/stdio/protocol.d.ts +2 -2
  37. package/dist/stdio/stdioServer.d.ts +2 -7
  38. package/dist/stdio/stdioServer.js +9 -100
  39. package/dist/utils/bracketedPaste.d.ts +39 -0
  40. package/dist/utils/bracketedPaste.js +122 -0
  41. package/dist/utils/markdownTable.d.ts +34 -0
  42. package/dist/utils/markdownTable.js +302 -0
  43. package/dist/utils/throttle.d.ts +3 -3
  44. package/package.json +4 -2
  45. package/src/cli.tsx +20 -1
  46. package/src/components/App.tsx +5 -0
  47. package/src/components/BtwDisplay.tsx +36 -12
  48. package/src/components/ChatInterface.tsx +30 -15
  49. package/src/components/InputBox.tsx +25 -24
  50. package/src/components/LoadingIndicator.tsx +1 -4
  51. package/src/components/LoginCommand.tsx +4 -2
  52. package/src/components/Markdown.tsx +15 -18
  53. package/src/components/Notifications.tsx +31 -0
  54. package/src/components/StatusLine.tsx +17 -44
  55. package/src/components/TaskList.tsx +2 -1
  56. package/src/components/ToolDisplay.tsx +17 -6
  57. package/src/constants/commands.ts +0 -6
  58. package/src/contexts/useChat.tsx +326 -140
  59. package/src/daemon-cli.ts +17 -0
  60. package/src/hooks/useInputManager.ts +108 -22
  61. package/src/index.ts +12 -0
  62. package/src/managers/inputHandlers.ts +49 -22
  63. package/src/managers/inputReducer.ts +66 -11
  64. package/src/stdio/agentBridge.ts +196 -17
  65. package/src/stdio/daemonServer.ts +212 -0
  66. package/src/stdio/index.ts +2 -0
  67. package/src/stdio/jsonRpcConnection.ts +160 -0
  68. package/src/stdio/protocol.ts +5 -2
  69. package/src/stdio/stdioServer.ts +14 -120
  70. package/src/utils/bracketedPaste.ts +170 -0
  71. package/src/utils/markdownTable.ts +359 -0
  72. package/src/utils/throttle.ts +8 -8
@@ -1,10 +1,20 @@
1
- import { useEffect, useReducer, useCallback } from "react";
2
- import { inputReducer, initialState, ESC_DOUBLE_PRESS_TIMEOUT_MS, } 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, isIdle: isIdleProp, } = 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) {
@@ -67,25 +77,49 @@ export const useInputManager = (callbacks = {}) => {
67
77
  case "BACKGROUND_CURRENT_TASK":
68
78
  onBackgroundCurrentTask?.();
69
79
  break;
70
- case "ASK_BTW":
80
+ case "ASK_BTW": {
81
+ const controller = new AbortController();
82
+ btwAbortRef.current = controller;
83
+ btwDismissedRef.current = false;
71
84
  try {
72
- const answer = await onAskBtw?.(effect.question);
73
- dispatch({
74
- type: "SET_BTW_STATE",
75
- 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
+ }
76
96
  });
97
+ if (!btwDismissedRef.current) {
98
+ dispatch({
99
+ type: "SET_BTW_STATE",
100
+ payload: { answer, isLoading: false },
101
+ });
102
+ }
77
103
  }
78
104
  catch (error) {
79
- console.error("Failed to ask side question:", error);
80
- dispatch({
81
- type: "SET_BTW_STATE",
82
- payload: {
83
- answer: "Error: Failed to get an answer for your side question.",
84
- isLoading: false,
85
- },
86
- });
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
+ }
87
115
  }
88
116
  break;
117
+ }
118
+ case "ABORT_BTW":
119
+ btwDismissedRef.current = true;
120
+ btwAbortRef.current?.abort();
121
+ btwAbortRef.current = null;
122
+ break;
89
123
  case "PERMISSION_MODE_CHANGE":
90
124
  onPermissionModeChange?.(effect.mode);
91
125
  break;
@@ -155,6 +189,18 @@ export const useInputManager = (callbacks = {}) => {
155
189
  else if (command === "logout") {
156
190
  dispatch({ type: "SET_SHOW_LOGIN_COMMAND", payload: true });
157
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
+ }
158
204
  else if (command === "plugin") {
159
205
  dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
160
206
  }
@@ -170,9 +216,6 @@ export const useInputManager = (callbacks = {}) => {
170
216
  else if (command === "compact") {
171
217
  await onCompact?.(effect.args);
172
218
  }
173
- else if (command === "goal") {
174
- await onGoalCommand?.(effect.args);
175
- }
176
219
  }
177
220
  break;
178
221
  case "RECALL_QUEUED_MESSAGE":
@@ -200,7 +243,6 @@ export const useInputManager = (callbacks = {}) => {
200
243
  onRecallQueuedMessage,
201
244
  onClearMessages,
202
245
  onCompact,
203
- onGoalCommand,
204
246
  ]);
205
247
  useEffect(() => {
206
248
  onFileSelectorStateChange?.(state.showFileSelector, state.filteredFiles, state.fileSearchQuery, state.atPosition);
@@ -253,7 +295,13 @@ export const useInputManager = (callbacks = {}) => {
253
295
  useEffect(() => {
254
296
  onImagesStateChange?.(state.attachedImages);
255
297
  }, [state.attachedImages, onImagesStateChange]);
256
- // /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]);
257
305
  // Methods
258
306
  const insertTextAtCursor = useCallback((text) => {
259
307
  dispatch({ type: "INSERT_TEXT", payload: text });
@@ -397,10 +445,39 @@ export const useInputManager = (callbacks = {}) => {
397
445
  dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
398
446
  }, []);
399
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
+ }
400
477
  dispatch({
401
478
  type: "HANDLE_KEY",
402
479
  payload: {
403
- input,
480
+ input: result.input,
404
481
  key,
405
482
  hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
406
483
  hasQueuedMessages: hasQueuedMessagesProp ?? false,
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" });
@@ -261,6 +273,18 @@ export const handleCommandSelect = (state, dispatch, callbacks, command) => {
261
273
  else if (command === "status") {
262
274
  dispatch({ type: "SET_SHOW_STATUS_COMMAND", payload: true });
263
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
+ }
264
288
  else if (command === "plugin") {
265
289
  dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
266
290
  }
@@ -276,9 +300,6 @@ export const handleCommandSelect = (state, dispatch, callbacks, command) => {
276
300
  else if (command === "compact") {
277
301
  await callbacks.onCompact?.();
278
302
  }
279
- else if (command === "goal") {
280
- await callbacks.onGoalCommand?.();
281
- }
282
303
  }
283
304
  })();
284
305
  dispatch({ type: "CANCEL_COMMAND_SELECTOR" });
@@ -507,16 +528,23 @@ export const handleNormalInput = async (state, dispatch, callbacks, input, key,
507
528
  return false;
508
529
  };
509
530
  export const handleInput = async (state, dispatch, callbacks, input, key, clearImages) => {
510
- // Handle ESC to dismiss btw answer
511
- if (key.escape && state.btwState.question && !state.btwState.isLoading) {
512
- dispatch({
513
- type: "SET_BTW_STATE",
514
- payload: {
515
- question: "",
516
- answer: undefined,
517
- isLoading: false,
518
- },
519
- });
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
520
548
  return true;
521
549
  }
522
550
  if (key.escape) {
@@ -10,6 +10,15 @@ 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
+ };
13
22
  export declare const ESC_DOUBLE_PRESS_TIMEOUT_MS = 1000;
14
23
  export type PendingEffect = {
15
24
  type: "SEND_MESSAGE";
@@ -30,6 +39,8 @@ export type PendingEffect = {
30
39
  } | {
31
40
  type: "ASK_BTW";
32
41
  question: string;
42
+ } | {
43
+ type: "ABORT_BTW";
33
44
  } | {
34
45
  type: "PERMISSION_MODE_CHANGE";
35
46
  mode: PermissionMode;
@@ -67,10 +78,9 @@ export interface InputManagerCallbacks {
67
78
  onAbortMessage?: () => void;
68
79
  onBackgroundCurrentTask?: () => void;
69
80
  onPermissionModeChange?: (mode: PermissionMode) => void;
70
- onAskBtw?: (question: string) => Promise<string>;
81
+ onAskBtw?: (question: string, abortSignal?: AbortSignal, onContent?: (content: string) => void) => Promise<string>;
71
82
  onClearMessages?: () => Promise<void>;
72
83
  onCompact?: (instructions?: string) => Promise<void>;
73
- onGoalCommand?: (args?: string) => Promise<void>;
74
84
  sessionId?: string;
75
85
  workdir?: string;
76
86
  getFullMessageThread?: () => Promise<{
@@ -1,5 +1,12 @@
1
1
  import { getAtSelectorPosition, getSlashSelectorPosition, getWordEnd, SELECTOR_TRIGGERS, getProjectedState, } from "../utils/inputUtils.js";
2
2
  import { AVAILABLE_COMMANDS } from "../constants/commands.js";
3
+ /**
4
+ * True while the /btw overlay is up (a question is on display, loading or
5
+ * answered). App's Ctrl+C exit handler checks this so Ctrl+C does not quit
6
+ * the app while the overlay owns the keys. Synced from useInputManager via
7
+ * an effect.
8
+ */
9
+ export const btwOverlayActiveRef = { current: false };
3
10
  export const ESC_DOUBLE_PRESS_TIMEOUT_MS = 1000;
4
11
  export const initialState = {
5
12
  inputText: "",
@@ -91,7 +98,7 @@ function insertTextWithPlaceholder(textToInsert, state) {
91
98
  /**
92
99
  * Submit the current input text: extract [Image #N] references, route /btw
93
100
  * and CLI-internal slash commands, otherwise send as a message. Returns null
94
- * when there is nothing to submit (empty text, bare /btw).
101
+ * when there is nothing to submit (empty text).
95
102
  */
96
103
  function submitInput(state) {
97
104
  if (!state.inputText.trim()) {
@@ -112,8 +119,20 @@ function submitInput(state) {
112
119
  if (contentWithPlaceholders.startsWith("/btw ")) {
113
120
  const question = contentWithPlaceholders.substring(5).trim();
114
121
  if (!question) {
115
- // Bare /btw with no question text — ignore
116
- return null;
122
+ // "/btw " with no question text — show usage (aligned with Claude Code)
123
+ return {
124
+ ...state,
125
+ inputText: "",
126
+ cursorPosition: 0,
127
+ historyIndex: -1,
128
+ longTextMap: {},
129
+ attachedImages: [],
130
+ btwState: {
131
+ question: "",
132
+ isLoading: false,
133
+ answer: "Usage: /btw <your question>",
134
+ },
135
+ };
117
136
  }
118
137
  return {
119
138
  ...state,
@@ -131,8 +150,20 @@ function submitInput(state) {
131
150
  };
132
151
  }
133
152
  if (contentWithPlaceholders === "/btw") {
134
- // Bare /btw — ignore
135
- return null;
153
+ // Bare /btw — show usage (aligned with Claude Code)
154
+ return {
155
+ ...state,
156
+ inputText: "",
157
+ cursorPosition: 0,
158
+ historyIndex: -1,
159
+ longTextMap: {},
160
+ attachedImages: [],
161
+ btwState: {
162
+ question: "",
163
+ isLoading: false,
164
+ answer: "Usage: /btw <your question>",
165
+ },
166
+ };
136
167
  }
137
168
  // Check if the content is a CLI-internal slash command (help, tasks,
138
169
  // etc.) that should be executed locally rather than sent as a message.
@@ -593,10 +624,22 @@ export function inputReducer(state, action) {
593
624
  }
594
625
  return state;
595
626
  }
596
- // 1. Escape Handling
597
- if (key.escape) {
598
- // Dismiss btw answer
599
- if (state.btwState.question && !state.btwState.isLoading) {
627
+ // 1. /btw overlay handling (active while a question is displayed, or
628
+ // the bare-/btw usage message). Only Escape dismisses (or aborts the
629
+ // in-flight side question while loading); every other key is ignored.
630
+ if (state.btwState.question || state.btwState.answer) {
631
+ if (key.escape) {
632
+ if (state.btwState.isLoading) {
633
+ return {
634
+ ...state,
635
+ btwState: {
636
+ question: "",
637
+ answer: undefined,
638
+ isLoading: false,
639
+ },
640
+ pendingEffect: { type: "ABORT_BTW" },
641
+ };
642
+ }
600
643
  return {
601
644
  ...state,
602
645
  btwState: {
@@ -606,6 +649,11 @@ export function inputReducer(state, action) {
606
649
  },
607
650
  };
608
651
  }
652
+ // Any other key while the overlay is up is ignored
653
+ return state;
654
+ }
655
+ // 1. Escape Handling
656
+ if (key.escape) {
609
657
  if (state.showFileSelector) {
610
658
  return {
611
659
  ...state,
@@ -21,6 +21,9 @@ export interface AgentBridgeOptions {
21
21
  }
22
22
  export declare class AgentBridge {
23
23
  private sessions;
24
+ /** Pending approval requests, keyed by requestId. Stored with the resolve +
25
+ * context so a re-attached client can list and respond to them (daemon mode:
26
+ * approvals outlive any single connection). */
24
27
  private pendingPermissions;
25
28
  private permissionCounter;
26
29
  private emit;
@@ -31,6 +34,21 @@ export declare class AgentBridge {
31
34
  handleNotification(method: string, params: unknown): void;
32
35
  private initialize;
33
36
  private destroy;
37
+ /**
38
+ * True when every hosted session has settled: not generating, nothing queued,
39
+ * and no background work (background bash / subagents / workflows) — the same
40
+ * condition `wave -p` waits on before exiting (print-cli.ts). Pending
41
+ * permission approvals keep the owning agent's isLoading true, so they are
42
+ * covered without an explicit check.
43
+ */
44
+ isIdle(): boolean;
45
+ /**
46
+ * Destroy every hosted session agent. Each Agent.destroy() saves its
47
+ * transcript, drains in-flight auto-memory extraction, and cleans up
48
+ * background tasks/subagents. Best-effort: one failing destroy must not
49
+ * block the rest of the shutdown.
50
+ */
51
+ destroyAll(): Promise<void>;
34
52
  private restoreSession;
35
53
  private listSessions;
36
54
  private listGitBranches;
@@ -40,6 +58,7 @@ export declare class AgentBridge {
40
58
  private updateConfig;
41
59
  private sendMessage;
42
60
  private bang;
61
+ private askBtw;
43
62
  private abortMessage;
44
63
  private clearMessages;
45
64
  private rewindToMessage;
@@ -64,6 +83,10 @@ export declare class AgentBridge {
64
83
  private getPromptHistory;
65
84
  private searchPromptHistory;
66
85
  private canUseTool;
86
+ /** Attach snapshot: re-surface approvals that are still pending after a
87
+ * client disconnected (daemon mode). Responding to any listed requestId
88
+ * resolves the in-process promise. */
89
+ private listPendingPermissions;
67
90
  private getAuthStatus;
68
91
  private login;
69
92
  private logout;