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
@@ -22,27 +22,104 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
22
22
  const [isExpanded, setIsExpanded] = useState(false);
23
23
  const isExpandedRef = useRef(isExpanded);
24
24
  const [isTaskListVisible, setIsTaskListVisible] = useState(true);
25
+ const [isBtwActive, setIsBtwActive] = useState(false);
25
26
  const [messages, setMessages] = useState([]);
26
27
  const [latestTotalTokens, setLatestTotalTokens] = useState(0);
27
28
  const [maxInputTokens, setMaxInputTokens] = useState(200000);
28
- const throttledSetMessages = useMemo(() => throttle(() => {
29
- if (!isExpandedRef.current && agentRef.current) {
30
- const msgs = [...agentRef.current.messages];
31
- setMessages(msgs);
32
- setLatestTotalTokens(extractLatestTotalTokens(msgs));
33
- }
34
- }, 500, { leading: true, trailing: true }), []);
29
+ // Throttled incremental streaming updaters 500ms leading+trailing, the same interval
30
+ // as the pre-incremental throttledSetMessages. `stage === "end"` flushes the final
31
+ // update immediately so completion results are never delayed.
32
+ const throttledContentUpdate = useMemo(() => throttle((params) => {
33
+ const { messageId, accumulated, stage } = params;
34
+ setMessages((prev) => prev.map((m) => {
35
+ if (m.id !== messageId)
36
+ return m;
37
+ const textBlockIndex = m.blocks.findIndex((b) => b.type === "text");
38
+ if (textBlockIndex === -1) {
39
+ return {
40
+ ...m,
41
+ blocks: [
42
+ ...m.blocks,
43
+ { type: "text", content: accumulated, stage },
44
+ ],
45
+ };
46
+ }
47
+ return {
48
+ ...m,
49
+ blocks: m.blocks.map((b, idx) => idx === textBlockIndex && b.type === "text"
50
+ ? { ...b, content: accumulated, stage }
51
+ : b),
52
+ };
53
+ }));
54
+ }, 500), []);
55
+ const throttledReasoningUpdate = useMemo(() => throttle((params) => {
56
+ const { messageId, accumulated, stage } = params;
57
+ setMessages((prev) => prev.map((m) => {
58
+ if (m.id !== messageId)
59
+ return m;
60
+ const reasoningBlockIndex = m.blocks.findIndex((b) => b.type === "reasoning");
61
+ if (reasoningBlockIndex === -1) {
62
+ return {
63
+ ...m,
64
+ blocks: [
65
+ ...m.blocks,
66
+ { type: "reasoning", content: accumulated, stage },
67
+ ],
68
+ };
69
+ }
70
+ return {
71
+ ...m,
72
+ blocks: m.blocks.map((b, idx) => idx === reasoningBlockIndex && b.type === "reasoning"
73
+ ? { ...b, content: accumulated, stage }
74
+ : b),
75
+ };
76
+ }));
77
+ }, 500), []);
78
+ const throttledToolBlockUpdate = useMemo(() => throttle((params) => {
79
+ const { messageId, id: toolBlockId, ...updates } = params;
80
+ setMessages((prev) => prev.map((m) => {
81
+ if (m.id !== messageId)
82
+ return m;
83
+ const toolBlockIndex = m.blocks.findIndex((b) => b.type === "tool" && b.id === toolBlockId);
84
+ if (toolBlockIndex === -1) {
85
+ return {
86
+ ...m,
87
+ blocks: [
88
+ ...m.blocks,
89
+ {
90
+ type: "tool",
91
+ id: toolBlockId,
92
+ name: updates.name || "",
93
+ stage: updates.stage || "start",
94
+ parameters: updates.parameters || "",
95
+ result: updates.result || "",
96
+ ...updates,
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ return {
102
+ ...m,
103
+ blocks: m.blocks.map((b, idx) => idx === toolBlockIndex && b.type === "tool"
104
+ ? { ...b, ...updates }
105
+ : b),
106
+ };
107
+ }));
108
+ }, 500), []);
35
109
  useEffect(() => {
36
110
  isExpandedRef.current = isExpanded;
37
111
  if (isExpanded) {
38
- throttledSetMessages.cancel();
112
+ // Cancel pending throttled updates so the frozen expanded view isn't overwritten
113
+ throttledContentUpdate.cancel();
114
+ throttledReasoningUpdate.cancel();
115
+ throttledToolBlockUpdate.cancel();
39
116
  }
40
- }, [isExpanded, throttledSetMessages]);
41
- useEffect(() => {
42
- return () => {
43
- throttledSetMessages.cancel();
44
- };
45
- }, [throttledSetMessages]);
117
+ }, [
118
+ isExpanded,
119
+ throttledContentUpdate,
120
+ throttledReasoningUpdate,
121
+ throttledToolBlockUpdate,
122
+ ]);
46
123
  const [isLoading, setIsLoading] = useState(false);
47
124
  const [sessionId, setSessionId] = useState("");
48
125
  const [isCommandRunning, setIsCommandRunning] = useState(false);
@@ -50,29 +127,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
50
127
  const [currentModel, setCurrentModelState] = useState("");
51
128
  const [configuredModels, setConfiguredModels] = useState([]);
52
129
  const [queuedMessages, setQueuedMessages] = useState([]);
53
- const [isGoalActive, setIsGoalActive] = useState(false);
54
- const [goalElapsed, setGoalElapsed] = useState();
55
- const [isGoalEvaluating, setIsGoalEvaluating] = useState(false);
56
- const goalStartedAt = useRef(null);
57
- // Update goal elapsed time every 30s while active
58
- useEffect(() => {
59
- if (!isGoalActive || goalStartedAt.current === null)
60
- return;
61
- const formatElapsed = (ms) => {
62
- const minutes = Math.floor(ms / 60000);
63
- if (minutes < 1)
64
- return "<1m";
65
- if (minutes < 60)
66
- return `${minutes}m`;
67
- const hours = Math.floor(minutes / 60);
68
- const remainingMin = minutes % 60;
69
- return `${hours}h${remainingMin}m`;
70
- };
71
- const update = () => setGoalElapsed(formatElapsed(Date.now() - goalStartedAt.current));
72
- update();
73
- const timer = setInterval(update, 30000);
74
- return () => clearInterval(timer);
75
- }, [isGoalActive]);
76
130
  // MCP State
77
131
  const [mcpServerStatuses, setMcpServerStatuses] = useState([]);
78
132
  // Background tasks state
@@ -93,32 +147,27 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
93
147
  const [currentConfirmation, setCurrentConfirmation] = useState(null);
94
148
  // Remount state
95
149
  const [remountKey, setRemountKey] = useState(0);
96
- const prevSessionId = useRef(null);
97
- const requestRemount = useMemo(() => throttle(() => {
98
- logger.debug("requesting remount");
150
+ // Full terminal clear + remount so Ink's append-only <Static> re-renders.
151
+ // Used on structural actions (/clear, /compact, rewind, ctrl-o, forceStatic
152
+ // exit) where stale Static output must not linger on screen.
153
+ const forceRemount = useCallback(() => {
99
154
  stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", () => {
100
155
  setRemountKey((prev) => prev + 1);
101
156
  });
102
- }, 1000, { leading: true, trailing: false }), [stdout]);
103
- useEffect(() => {
104
- return () => {
105
- requestRemount.cancel();
106
- };
107
- }, [requestRemount]);
108
- // Track sessionId changes to trigger remount
109
- useEffect(() => {
110
- if (prevSessionId.current &&
111
- sessionId &&
112
- prevSessionId.current !== sessionId) {
113
- requestRemount();
114
- }
115
- if (sessionId) {
116
- prevSessionId.current = sessionId;
117
- }
118
- }, [sessionId, requestRemount]);
157
+ }, [stdout]);
119
158
  // Status metadata state
120
159
  const [workingDirectory, setWorkingDirectory] = useState("");
121
160
  const agentRef = useRef(null);
161
+ // Full-list refresh — one-shot pull from the agent, used only for structural
162
+ // changes (compact/clear/rewind/collapse/init). Streaming updates flow through
163
+ // the incremental callbacks in initializeAgent below.
164
+ const refreshMessages = useCallback(() => {
165
+ if (!isExpandedRef.current && agentRef.current) {
166
+ const msgs = [...agentRef.current.messages];
167
+ setMessages(msgs);
168
+ setLatestTotalTokens(extractLatestTotalTokens(msgs));
169
+ }
170
+ }, []);
122
171
  // Permission confirmation methods with queue support
123
172
  const showConfirmation = useCallback(async (toolName, toolInput, suggestedPrefix, hidePersistentOption, planContent) => {
124
173
  return new Promise((resolve, reject) => {
@@ -139,8 +188,123 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
139
188
  const initializeAgent = useCallback(async (restoreSessionIdOverride) => {
140
189
  const effectiveRestoreSessionId = restoreSessionIdOverride ?? restoreSessionId;
141
190
  const callbacks = {
142
- onMessagesChange: () => {
143
- throttledSetMessages();
191
+ // ── Incremental message updates (no full-list pushes) ──────
192
+ onUserMessageAdded: () => {
193
+ if (isExpandedRef.current || !agentRef.current)
194
+ return;
195
+ const msgs = agentRef.current.messages;
196
+ const last = msgs[msgs.length - 1];
197
+ if (!last || last.role !== "user")
198
+ return;
199
+ setMessages((prev) => prev.some((m) => m.id === last.id) ? prev : [...prev, last]);
200
+ },
201
+ onAssistantMessageAdded: (messageId) => {
202
+ if (isExpandedRef.current || !agentRef.current)
203
+ return;
204
+ const msg = agentRef.current.messages.find((m) => m.id === messageId);
205
+ if (!msg)
206
+ return;
207
+ setMessages((prev) => prev.some((m) => m.id === messageId) ? prev : [...prev, msg]);
208
+ },
209
+ onAssistantContentUpdated: (params) => {
210
+ if (isExpandedRef.current)
211
+ return;
212
+ throttledContentUpdate(params);
213
+ if (params.stage === "end")
214
+ throttledContentUpdate.flush();
215
+ },
216
+ onAssistantReasoningUpdated: (params) => {
217
+ if (isExpandedRef.current)
218
+ return;
219
+ throttledReasoningUpdate(params);
220
+ if (params.stage === "end")
221
+ throttledReasoningUpdate.flush();
222
+ },
223
+ onToolBlockUpdated: (params) => {
224
+ if (isExpandedRef.current)
225
+ return;
226
+ throttledToolBlockUpdate(params);
227
+ if (params.stage === "end")
228
+ throttledToolBlockUpdate.flush();
229
+ },
230
+ onErrorBlockAdded: (error) => {
231
+ if (isExpandedRef.current)
232
+ return;
233
+ setMessages((prev) => {
234
+ // Append to the last assistant message, or create one if none exists
235
+ for (let i = prev.length - 1; i >= 0; i--) {
236
+ if (prev[i].role === "assistant") {
237
+ return prev.map((m, idx) => idx === i
238
+ ? {
239
+ ...m,
240
+ blocks: [
241
+ ...m.blocks,
242
+ { type: "error", content: error },
243
+ ],
244
+ }
245
+ : m);
246
+ }
247
+ }
248
+ return [
249
+ ...prev,
250
+ {
251
+ id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
252
+ role: "assistant",
253
+ timestamp: new Date().toISOString(),
254
+ blocks: [{ type: "error", content: error }],
255
+ },
256
+ ];
257
+ });
258
+ },
259
+ onAddBangMessage: (command, messageId) => {
260
+ if (isExpandedRef.current)
261
+ return;
262
+ setMessages((prev) => prev.some((m) => m.id === messageId)
263
+ ? prev
264
+ : [
265
+ ...prev,
266
+ {
267
+ id: messageId,
268
+ role: "user",
269
+ timestamp: new Date().toISOString(),
270
+ blocks: [
271
+ {
272
+ type: "bang",
273
+ command,
274
+ output: "",
275
+ stage: "running",
276
+ exitCode: null,
277
+ },
278
+ ],
279
+ },
280
+ ]);
281
+ },
282
+ onUpdateBangMessage: (command, output, messageId) => {
283
+ if (isExpandedRef.current)
284
+ return;
285
+ setMessages((prev) => prev.map((m) => m.id === messageId
286
+ ? {
287
+ ...m,
288
+ blocks: m.blocks.map((b, idx) => idx === m.blocks.length - 1 && b.type === "bang"
289
+ ? { ...b, command, output }
290
+ : b),
291
+ }
292
+ : m));
293
+ },
294
+ onCompleteBangMessage: (command, exitCode, messageId) => {
295
+ if (isExpandedRef.current)
296
+ return;
297
+ setMessages((prev) => prev.map((m) => m.id === messageId
298
+ ? {
299
+ ...m,
300
+ blocks: m.blocks.map((b, idx) => idx === m.blocks.length - 1 && b.type === "bang"
301
+ ? { ...b, command, exitCode, stage: "end" }
302
+ : b),
303
+ }
304
+ : m));
305
+ },
306
+ onLatestTotalTokensChange: (tokens) => {
307
+ setLatestTotalTokens(tokens);
144
308
  },
145
309
  onMcpServersChange: (servers) => {
146
310
  setMcpServerStatuses([...servers]);
@@ -185,19 +349,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
185
349
  onQueuedMessagesChange: (messages) => {
186
350
  setQueuedMessages([...messages]);
187
351
  },
188
- onGoalStateChange: (active, _condition, elapsed) => {
189
- setIsGoalActive(active);
190
- if (active) {
191
- goalStartedAt.current = Date.now();
192
- }
193
- else {
194
- goalStartedAt.current = null;
195
- }
196
- setGoalElapsed(elapsed);
197
- },
198
- onGoalEvaluating: (evaluating) => {
199
- setIsGoalEvaluating(evaluating);
200
- },
201
352
  };
202
353
  try {
203
354
  // Create the permission callback inside the try block to access showConfirmation
@@ -283,7 +434,10 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
283
434
  originalCwd,
284
435
  model,
285
436
  initialPermissionMode,
286
- throttledSetMessages,
437
+ refreshMessages,
438
+ throttledContentUpdate,
439
+ throttledReasoningUpdate,
440
+ throttledToolBlockUpdate,
287
441
  mcpServers,
288
442
  ]);
289
443
  // Recreate agent (e.g. after plugin install) — destroys current agent and reinitializes
@@ -318,6 +472,9 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
318
472
  // Cleanup on unmount
319
473
  useEffect(() => {
320
474
  return () => {
475
+ throttledContentUpdate.cancel();
476
+ throttledReasoningUpdate.cancel();
477
+ throttledToolBlockUpdate.cancel();
321
478
  if (agentRef.current) {
322
479
  try {
323
480
  // Display usage summary before cleanup
@@ -331,7 +488,11 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
331
488
  agentRef.current.destroy();
332
489
  }
333
490
  };
334
- }, []);
491
+ }, [
492
+ throttledContentUpdate,
493
+ throttledReasoningUpdate,
494
+ throttledToolBlockUpdate,
495
+ ]);
335
496
  // Trigger WorktreeRemove hook BEFORE agent destruction
336
497
  const triggerWorktreeRemoveHook = useCallback(async (worktreePath) => {
337
498
  await agentRef.current?.triggerWorktreeRemoveHook(worktreePath);
@@ -368,30 +529,22 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
368
529
  console.error("Failed to send message:", error);
369
530
  }
370
531
  }, []);
371
- const askBtw = useCallback(async (question) => {
532
+ const askBtw = useCallback(async (question, abortSignal, onContent) => {
372
533
  if (!agentRef.current) {
373
534
  throw new Error("Agent not initialized");
374
535
  }
375
- return await agentRef.current.askBtw(question);
536
+ return await agentRef.current.askBtw(question, abortSignal, onContent);
376
537
  }, []);
377
538
  const clearMessages = useCallback(async () => {
378
539
  await agentRef.current?.clearMessages();
379
- }, []);
540
+ refreshMessages();
541
+ forceRemount();
542
+ }, [refreshMessages, forceRemount]);
380
543
  const compact = useCallback(async (instructions) => {
381
544
  await agentRef.current?.compact(instructions);
382
- }, []);
383
- const goalCommand = useCallback(async (args) => {
384
- const trimmed = args?.trim() ?? "";
385
- if (!trimmed) {
386
- await agentRef.current?.showGoalStatus();
387
- }
388
- else if (["clear", "stop", "off", "reset", "none", "cancel"].includes(trimmed)) {
389
- await agentRef.current?.clearGoal();
390
- }
391
- else {
392
- await agentRef.current?.setGoal(trimmed);
393
- }
394
- }, []);
545
+ refreshMessages();
546
+ forceRemount();
547
+ }, [refreshMessages, forceRemount]);
395
548
  // Unified interrupt method, interrupt both AI messages and command execution
396
549
  const abortMessage = useCallback(() => {
397
550
  agentRef.current?.abortMessage();
@@ -484,13 +637,14 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
484
637
  if (agentRef.current) {
485
638
  try {
486
639
  await agentRef.current.truncateHistory(index);
487
- requestRemount();
640
+ refreshMessages();
641
+ forceRemount();
488
642
  }
489
643
  catch (error) {
490
644
  logger.error("Failed to rewind:", error);
491
645
  }
492
646
  }
493
- }, [requestRemount]);
647
+ }, [forceRemount, refreshMessages]);
494
648
  const getFullMessageThread = useCallback(async () => {
495
649
  if (agentRef.current) {
496
650
  return await agentRef.current.getFullMessageThread();
@@ -530,23 +684,14 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
530
684
  isExpandedRef.current = nextExpanded;
531
685
  if (nextExpanded) {
532
686
  // Transitioning to EXPANDED: Freeze the current view
533
- // Cancel any pending throttled updates to avoid overwriting the frozen state
534
- throttledSetMessages.cancel();
687
+ // Incremental updates are skipped while expanded (isExpandedRef guard)
535
688
  }
536
689
  else {
537
690
  // Transitioning to COLLAPSED: Restore from agent's actual state
538
- if (agentRef.current) {
539
- const msgs = [...agentRef.current.messages];
540
- setMessages(msgs);
541
- setLatestTotalTokens(extractLatestTotalTokens(msgs));
542
- }
691
+ refreshMessages();
543
692
  }
544
- // Force remount directly (bypass throttle) to ensure Static items re-render
545
- // The throttled requestRemount can be dropped if pressed too quickly after
546
- // a previous remount, leaving the UI stuck without a visual update
547
- stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", () => {
548
- setRemountKey((prev) => prev + 1);
549
- });
693
+ // Force remount to ensure Static items re-render
694
+ forceRemount();
550
695
  }
551
696
  if (key.ctrl && input === "t") {
552
697
  setIsTaskListVisible((prev) => !prev);
@@ -563,13 +708,14 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
563
708
  isExpanded,
564
709
  isTaskListVisible,
565
710
  setIsTaskListVisible,
711
+ isBtwActive,
712
+ setIsBtwActive,
566
713
  queuedMessages,
567
714
  sessionId,
568
715
  sendMessage,
569
716
  askBtw,
570
717
  clearMessages,
571
718
  compact,
572
- goalCommand,
573
719
  abortMessage,
574
720
  recallQueuedMessage,
575
721
  removeQueuedMessageById,
@@ -602,7 +748,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
602
748
  handleConfirmationCancel,
603
749
  backgroundCurrentTask,
604
750
  remountKey,
605
- requestRemount: requestRemount,
751
+ forceRemount,
606
752
  handleRewindSelect,
607
753
  getFullMessageThread,
608
754
  getGatewayConfig,
@@ -612,9 +758,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
612
758
  workdir,
613
759
  recreateAgent,
614
760
  triggerWorktreeRemoveHook,
615
- isGoalActive,
616
- goalElapsed,
617
- isGoalEvaluating,
618
761
  };
619
762
  return (_jsx(ChatContext.Provider, { value: contextValue, children: children }));
620
763
  };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * daemon-cli.ts — Entry point for `wave --daemon <socket-path>` mode.
3
+ *
4
+ * Starts a DaemonServer that serves JSON-RPC over a unix socket. The desktop
5
+ * app launches this on a remote host via nohup/setsid and tunnels the socket
6
+ * back with `ssh -L`; multiple attach/detach cycles share one process, so
7
+ * sessions and pending permissions survive client disconnects. The net server
8
+ * keeps the process alive — there is no stdin to wait on.
9
+ */
10
+ export declare function startDaemonCli(socketPath: string): Promise<void>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * daemon-cli.ts — Entry point for `wave --daemon <socket-path>` mode.
3
+ *
4
+ * Starts a DaemonServer that serves JSON-RPC over a unix socket. The desktop
5
+ * app launches this on a remote host via nohup/setsid and tunnels the socket
6
+ * back with `ssh -L`; multiple attach/detach cycles share one process, so
7
+ * sessions and pending permissions survive client disconnects. The net server
8
+ * keeps the process alive — there is no stdin to wait on.
9
+ */
10
+ import { DaemonServer } from "./stdio/daemonServer.js";
11
+ export async function startDaemonCli(socketPath) {
12
+ const server = new DaemonServer({ socketPath });
13
+ await server.start();
14
+ // Ready — any error that follows goes to the daemon log via stderr.
15
+ }