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
@@ -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
@@ -119,6 +173,16 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
119
173
  // Status metadata state
120
174
  const [workingDirectory, setWorkingDirectory] = useState("");
121
175
  const agentRef = useRef(null);
176
+ // Full-list refresh — one-shot pull from the agent, used only for structural
177
+ // changes (compact/clear/rewind/collapse/init). Streaming updates flow through
178
+ // the incremental callbacks in initializeAgent below.
179
+ const refreshMessages = useCallback(() => {
180
+ if (!isExpandedRef.current && agentRef.current) {
181
+ const msgs = [...agentRef.current.messages];
182
+ setMessages(msgs);
183
+ setLatestTotalTokens(extractLatestTotalTokens(msgs));
184
+ }
185
+ }, []);
122
186
  // Permission confirmation methods with queue support
123
187
  const showConfirmation = useCallback(async (toolName, toolInput, suggestedPrefix, hidePersistentOption, planContent) => {
124
188
  return new Promise((resolve, reject) => {
@@ -139,8 +203,123 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
139
203
  const initializeAgent = useCallback(async (restoreSessionIdOverride) => {
140
204
  const effectiveRestoreSessionId = restoreSessionIdOverride ?? restoreSessionId;
141
205
  const callbacks = {
142
- onMessagesChange: () => {
143
- throttledSetMessages();
206
+ // ── Incremental message updates (no full-list pushes) ──────
207
+ onUserMessageAdded: () => {
208
+ if (isExpandedRef.current || !agentRef.current)
209
+ return;
210
+ const msgs = agentRef.current.messages;
211
+ const last = msgs[msgs.length - 1];
212
+ if (!last || last.role !== "user")
213
+ return;
214
+ setMessages((prev) => prev.some((m) => m.id === last.id) ? prev : [...prev, last]);
215
+ },
216
+ onAssistantMessageAdded: (messageId) => {
217
+ if (isExpandedRef.current || !agentRef.current)
218
+ return;
219
+ const msg = agentRef.current.messages.find((m) => m.id === messageId);
220
+ if (!msg)
221
+ return;
222
+ setMessages((prev) => prev.some((m) => m.id === messageId) ? prev : [...prev, msg]);
223
+ },
224
+ onAssistantContentUpdated: (params) => {
225
+ if (isExpandedRef.current)
226
+ return;
227
+ throttledContentUpdate(params);
228
+ if (params.stage === "end")
229
+ throttledContentUpdate.flush();
230
+ },
231
+ onAssistantReasoningUpdated: (params) => {
232
+ if (isExpandedRef.current)
233
+ return;
234
+ throttledReasoningUpdate(params);
235
+ if (params.stage === "end")
236
+ throttledReasoningUpdate.flush();
237
+ },
238
+ onToolBlockUpdated: (params) => {
239
+ if (isExpandedRef.current)
240
+ return;
241
+ throttledToolBlockUpdate(params);
242
+ if (params.stage === "end")
243
+ throttledToolBlockUpdate.flush();
244
+ },
245
+ onErrorBlockAdded: (error) => {
246
+ if (isExpandedRef.current)
247
+ return;
248
+ setMessages((prev) => {
249
+ // Append to the last assistant message, or create one if none exists
250
+ for (let i = prev.length - 1; i >= 0; i--) {
251
+ if (prev[i].role === "assistant") {
252
+ return prev.map((m, idx) => idx === i
253
+ ? {
254
+ ...m,
255
+ blocks: [
256
+ ...m.blocks,
257
+ { type: "error", content: error },
258
+ ],
259
+ }
260
+ : m);
261
+ }
262
+ }
263
+ return [
264
+ ...prev,
265
+ {
266
+ id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
267
+ role: "assistant",
268
+ timestamp: new Date().toISOString(),
269
+ blocks: [{ type: "error", content: error }],
270
+ },
271
+ ];
272
+ });
273
+ },
274
+ onAddBangMessage: (command, messageId) => {
275
+ if (isExpandedRef.current)
276
+ return;
277
+ setMessages((prev) => prev.some((m) => m.id === messageId)
278
+ ? prev
279
+ : [
280
+ ...prev,
281
+ {
282
+ id: messageId,
283
+ role: "user",
284
+ timestamp: new Date().toISOString(),
285
+ blocks: [
286
+ {
287
+ type: "bang",
288
+ command,
289
+ output: "",
290
+ stage: "running",
291
+ exitCode: null,
292
+ },
293
+ ],
294
+ },
295
+ ]);
296
+ },
297
+ onUpdateBangMessage: (command, output, messageId) => {
298
+ if (isExpandedRef.current)
299
+ return;
300
+ setMessages((prev) => prev.map((m) => m.id === messageId
301
+ ? {
302
+ ...m,
303
+ blocks: m.blocks.map((b, idx) => idx === m.blocks.length - 1 && b.type === "bang"
304
+ ? { ...b, command, output }
305
+ : b),
306
+ }
307
+ : m));
308
+ },
309
+ onCompleteBangMessage: (command, exitCode, messageId) => {
310
+ if (isExpandedRef.current)
311
+ return;
312
+ setMessages((prev) => prev.map((m) => m.id === messageId
313
+ ? {
314
+ ...m,
315
+ blocks: m.blocks.map((b, idx) => idx === m.blocks.length - 1 && b.type === "bang"
316
+ ? { ...b, command, exitCode, stage: "end" }
317
+ : b),
318
+ }
319
+ : m));
320
+ },
321
+ onLatestTotalTokensChange: (tokens) => {
322
+ setLatestTotalTokens(tokens);
144
323
  },
145
324
  onMcpServersChange: (servers) => {
146
325
  setMcpServerStatuses([...servers]);
@@ -185,19 +364,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
185
364
  onQueuedMessagesChange: (messages) => {
186
365
  setQueuedMessages([...messages]);
187
366
  },
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
367
  };
202
368
  try {
203
369
  // Create the permission callback inside the try block to access showConfirmation
@@ -283,7 +449,10 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
283
449
  originalCwd,
284
450
  model,
285
451
  initialPermissionMode,
286
- throttledSetMessages,
452
+ refreshMessages,
453
+ throttledContentUpdate,
454
+ throttledReasoningUpdate,
455
+ throttledToolBlockUpdate,
287
456
  mcpServers,
288
457
  ]);
289
458
  // Recreate agent (e.g. after plugin install) — destroys current agent and reinitializes
@@ -318,6 +487,9 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
318
487
  // Cleanup on unmount
319
488
  useEffect(() => {
320
489
  return () => {
490
+ throttledContentUpdate.cancel();
491
+ throttledReasoningUpdate.cancel();
492
+ throttledToolBlockUpdate.cancel();
321
493
  if (agentRef.current) {
322
494
  try {
323
495
  // Display usage summary before cleanup
@@ -331,7 +503,11 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
331
503
  agentRef.current.destroy();
332
504
  }
333
505
  };
334
- }, []);
506
+ }, [
507
+ throttledContentUpdate,
508
+ throttledReasoningUpdate,
509
+ throttledToolBlockUpdate,
510
+ ]);
335
511
  // Trigger WorktreeRemove hook BEFORE agent destruction
336
512
  const triggerWorktreeRemoveHook = useCallback(async (worktreePath) => {
337
513
  await agentRef.current?.triggerWorktreeRemoveHook(worktreePath);
@@ -368,30 +544,20 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
368
544
  console.error("Failed to send message:", error);
369
545
  }
370
546
  }, []);
371
- const askBtw = useCallback(async (question) => {
547
+ const askBtw = useCallback(async (question, abortSignal, onContent) => {
372
548
  if (!agentRef.current) {
373
549
  throw new Error("Agent not initialized");
374
550
  }
375
- return await agentRef.current.askBtw(question);
551
+ return await agentRef.current.askBtw(question, abortSignal, onContent);
376
552
  }, []);
377
553
  const clearMessages = useCallback(async () => {
378
554
  await agentRef.current?.clearMessages();
379
- }, []);
555
+ refreshMessages();
556
+ }, [refreshMessages]);
380
557
  const compact = useCallback(async (instructions) => {
381
558
  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
- }, []);
559
+ refreshMessages();
560
+ }, [refreshMessages]);
395
561
  // Unified interrupt method, interrupt both AI messages and command execution
396
562
  const abortMessage = useCallback(() => {
397
563
  agentRef.current?.abortMessage();
@@ -484,13 +650,14 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
484
650
  if (agentRef.current) {
485
651
  try {
486
652
  await agentRef.current.truncateHistory(index);
653
+ refreshMessages();
487
654
  requestRemount();
488
655
  }
489
656
  catch (error) {
490
657
  logger.error("Failed to rewind:", error);
491
658
  }
492
659
  }
493
- }, [requestRemount]);
660
+ }, [requestRemount, refreshMessages]);
494
661
  const getFullMessageThread = useCallback(async () => {
495
662
  if (agentRef.current) {
496
663
  return await agentRef.current.getFullMessageThread();
@@ -530,16 +697,11 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
530
697
  isExpandedRef.current = nextExpanded;
531
698
  if (nextExpanded) {
532
699
  // Transitioning to EXPANDED: Freeze the current view
533
- // Cancel any pending throttled updates to avoid overwriting the frozen state
534
- throttledSetMessages.cancel();
700
+ // Incremental updates are skipped while expanded (isExpandedRef guard)
535
701
  }
536
702
  else {
537
703
  // 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
- }
704
+ refreshMessages();
543
705
  }
544
706
  // Force remount directly (bypass throttle) to ensure Static items re-render
545
707
  // The throttled requestRemount can be dropped if pressed too quickly after
@@ -563,13 +725,14 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
563
725
  isExpanded,
564
726
  isTaskListVisible,
565
727
  setIsTaskListVisible,
728
+ isBtwActive,
729
+ setIsBtwActive,
566
730
  queuedMessages,
567
731
  sessionId,
568
732
  sendMessage,
569
733
  askBtw,
570
734
  clearMessages,
571
735
  compact,
572
- goalCommand,
573
736
  abortMessage,
574
737
  recallQueuedMessage,
575
738
  removeQueuedMessageById,
@@ -612,9 +775,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
612
775
  workdir,
613
776
  recreateAgent,
614
777
  triggerWorktreeRemoveHook,
615
- isGoalActive,
616
- goalElapsed,
617
- isGoalEvaluating,
618
778
  };
619
779
  return (_jsx(ChatContext.Provider, { value: contextValue, children: children }));
620
780
  };
@@ -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
+ }
@@ -26,6 +26,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
26
26
  permissionMode: PermissionMode;
27
27
  attachedImages: import("../managers/inputReducer.js").AttachedImage[];
28
28
  btwState: import("../managers/inputReducer.js").BtwState;
29
+ escClearPending: boolean;
29
30
  isManagerReady: boolean;
30
31
  insertTextAtCursor: (text: string) => void;
31
32
  deleteCharAtCursor: () => void;