wave-code 0.12.3 → 0.12.5

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.
package/dist/acp/agent.js CHANGED
@@ -249,7 +249,7 @@ export class WaveAcpAgent {
249
249
  });
250
250
  }
251
251
  }
252
- const textContent = textBlocks.join("");
252
+ const textContent = textBlocks.join("\n");
253
253
  try {
254
254
  await agent.sendMessage(textContent, images.length > 0 ? images : undefined);
255
255
  logger.info(`Message sent successfully for session ${sessionId}`);
@@ -5,7 +5,6 @@ interface AppProps extends BaseAppProps {
5
5
  continueLastSession?: boolean;
6
6
  onExit: (shouldRemove: boolean) => void;
7
7
  }
8
- export declare const ChatInterfaceWithRemount: React.FC;
9
8
  export declare const App: React.FC<AppProps>;
10
9
  export {};
11
10
  //# sourceMappingURL=App.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAWxE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,QAAS,SAAQ,YAAY;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAiGD,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAuC5C,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAmClC,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAWhE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,QAAS,SAAQ,YAAY;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAiGD,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAmClC,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef, useCallback } from "react";
3
- import { useStdout, useInput } from "ink";
2
+ import { useState, useEffect, useCallback } from "react";
3
+ import { useInput } from "ink";
4
4
  import { ChatInterface } from "./ChatInterface.js";
5
- import { ChatProvider, useChat } from "../contexts/useChat.js";
5
+ import { ChatProvider } from "../contexts/useChat.js";
6
6
  import { AppProvider } from "../contexts/useAppConfig.js";
7
7
  import { WorktreeExitPrompt } from "./WorktreeExitPrompt.js";
8
8
  import { hasUncommittedChanges, hasNewCommits, getDefaultRemoteBranch, } from "wave-agent-sdk";
@@ -48,38 +48,7 @@ const AppWithProviders = ({ bypassPermissions, permissionMode, pluginDirs, tools
48
48
  if (isExiting && worktreeSession && worktreeStatus) {
49
49
  return (_jsx(WorktreeExitPrompt, { name: worktreeSession.name, path: worktreeSession.path, hasUncommittedChanges: worktreeStatus.hasUncommittedChanges, hasNewCommits: worktreeStatus.hasNewCommits, onKeep: () => onExit(false), onRemove: () => onExit(true), onCancel: () => setIsExiting(false) }));
50
50
  }
51
- return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, children: _jsx(ChatInterfaceWithRemount, {}) }));
52
- };
53
- export const ChatInterfaceWithRemount = () => {
54
- const { stdout } = useStdout();
55
- const { isExpanded, rewindId, sessionId } = useChat();
56
- const [remountKey, setRemountKey] = useState(String(isExpanded) + rewindId);
57
- const prevSessionId = useRef(sessionId);
58
- const isRemountScheduled = useRef(false);
59
- useEffect(() => {
60
- const newKey = String(isExpanded) +
61
- rewindId +
62
- (prevSessionId.current && sessionId && prevSessionId.current !== sessionId
63
- ? sessionId
64
- : "");
65
- if (newKey !== remountKey && !isRemountScheduled.current) {
66
- isRemountScheduled.current = true;
67
- const timeout = setTimeout(() => {
68
- stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", (err) => {
69
- if (err) {
70
- console.error("Failed to clear terminal:", err);
71
- }
72
- setRemountKey(newKey);
73
- isRemountScheduled.current = false;
74
- });
75
- }, 100);
76
- return () => clearTimeout(timeout);
77
- }
78
- if (sessionId) {
79
- prevSessionId.current = sessionId;
80
- }
81
- }, [isExpanded, rewindId, sessionId, remountKey, stdout]);
82
- return _jsx(ChatInterface, { remountKey: remountKey });
51
+ return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, children: _jsx(ChatInterface, {}) }));
83
52
  };
84
53
  export const App = ({ restoreSessionId, continueLastSession, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, onExit, }) => {
85
54
  return (_jsx(AppProvider, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, children: _jsx(AppWithProviders, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, onExit: onExit }) }));
@@ -1,5 +1,3 @@
1
1
  import React from "react";
2
- export declare const ChatInterface: React.FC<{
3
- remountKey: string;
4
- }>;
2
+ export declare const ChatInterface: React.FC;
5
3
  //# sourceMappingURL=ChatInterface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CA0F1D,CAAC"}
1
+ {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAc3D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAiIjC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box } from "ink";
2
+ import { useState, useRef, useEffect } from "react";
3
+ import { Box, useStdout, measureElement, Static } from "ink";
3
4
  import { MessageList } from "./MessageList.js";
4
5
  import { BtwDisplay } from "./BtwDisplay.js";
5
6
  import { InputBox } from "./InputBox.js";
@@ -9,10 +10,33 @@ import { QueuedMessageList } from "./QueuedMessageList.js";
9
10
  import { ConfirmationDetails } from "./ConfirmationDetails.js";
10
11
  import { ConfirmationSelector } from "./ConfirmationSelector.js";
11
12
  import { useChat } from "../contexts/useChat.js";
12
- export const ChatInterface = ({ remountKey, }) => {
13
- const { messages, isLoading, isCommandRunning, isCompressing, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, btwState, } = useChat();
13
+ export const ChatInterface = () => {
14
+ const { messages, isLoading, isCommandRunning, isCompressing, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, btwState, remountKey, requestRemount, } = useChat();
14
15
  const displayMessages = messages;
16
+ const [forceStatic, setForceStatic] = useState(false);
17
+ const { stdout } = useStdout();
18
+ const terminalHeight = stdout?.rows ?? 24;
19
+ const chatInterfaceRef = useRef(null);
20
+ // Handle forceStatic mode for overflow and request remount when exiting
21
+ useEffect(() => {
22
+ if (isConfirmationVisible && chatInterfaceRef.current) {
23
+ const { height } = measureElement(chatInterfaceRef.current);
24
+ if (height > terminalHeight) {
25
+ setForceStatic(true);
26
+ }
27
+ }
28
+ else if (forceStatic && !hasPendingConfirmations) {
29
+ setForceStatic(false);
30
+ requestRemount();
31
+ }
32
+ }, [
33
+ isConfirmationVisible,
34
+ terminalHeight,
35
+ forceStatic,
36
+ hasPendingConfirmations,
37
+ requestRemount,
38
+ ]);
15
39
  if (!sessionId)
16
40
  return null;
17
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(BtwDisplay, { btwState: btwState }), (isLoading || isCommandRunning || isCompressing) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel, onAbort: abortMessage })] }))] }));
41
+ return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(BtwDisplay, { btwState: btwState }), (isLoading || isCommandRunning || isCompressing) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel, onAbort: abortMessage })] }))] }));
18
42
  };
@@ -7,5 +7,5 @@ export interface MessageListProps {
7
7
  version?: string;
8
8
  workdir?: string;
9
9
  }
10
- export declare const MessageList: React.MemoExoticComponent<({ messages, isExpanded, version, workdir }: MessageListProps) => import("react/jsx-runtime").JSX.Element>;
10
+ export declare const MessageList: React.MemoExoticComponent<({ messages, isExpanded, forceStatic, version, workdir, }: MessageListProps) => import("react/jsx-runtime").JSX.Element>;
11
11
  //# sourceMappingURL=MessageList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,yEAC+B,gBAAgB,6CAgHtE,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,uFAOnB,gBAAgB,6CAiHpB,CAAC"}
@@ -3,7 +3,7 @@ import React from "react";
3
3
  import os from "os";
4
4
  import { Box, Text, Static } from "ink";
5
5
  import { MessageBlockItem } from "./MessageBlockItem.js";
6
- export const MessageList = React.memo(({ messages, isExpanded = false, version, workdir }) => {
6
+ export const MessageList = React.memo(({ messages, isExpanded = false, forceStatic = false, version, workdir, }) => {
7
7
  const welcomeMessage = (_jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [_jsxs(Text, { color: "gray", children: ["WAVE", version ? ` v${version}` : ""] }), workdir && (_jsx(Text, { color: "gray", wrap: "truncate-middle", children: workdir.replace(os.homedir(), "~") }))] }));
8
8
  // Limit messages to prevent long rendering times
9
9
  const maxMessages = 10;
@@ -29,7 +29,8 @@ export const MessageList = React.memo(({ messages, isExpanded = false, version,
29
29
  // Determine which blocks are static vs dynamic
30
30
  const blocksWithStatus = allBlocks.map((item) => {
31
31
  const { block } = item;
32
- const isDynamic = messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block);
32
+ const isDynamic = !forceStatic &&
33
+ (messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block));
33
34
  return { ...item, isDynamic };
34
35
  });
35
36
  const staticBlocks = blocksWithStatus.filter((b) => !b.isDynamic);
@@ -47,6 +47,7 @@ export interface ChatContextType {
47
47
  setPermissionMode: (mode: PermissionMode) => void;
48
48
  allowBypassInCycle: boolean;
49
49
  isConfirmationVisible: boolean;
50
+ hasPendingConfirmations: boolean;
50
51
  confirmingTool?: {
51
52
  name: string;
52
53
  input?: Record<string, unknown>;
@@ -59,7 +60,8 @@ export interface ChatContextType {
59
60
  handleConfirmationDecision: (decision: PermissionDecision) => void;
60
61
  handleConfirmationCancel: () => void;
61
62
  backgroundCurrentTask: () => void;
62
- rewindId: number;
63
+ remountKey: number;
64
+ requestRemount: () => void;
63
65
  handleRewindSelect: (index: number) => Promise<void>;
64
66
  getFullMessageThread: () => Promise<{
65
67
  messages: Message[];
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useCallback, useRef, useEffect, useState, useMemo, } from "react";
3
- import { useInput } from "ink";
3
+ import { useInput, useStdout } from "ink";
4
4
  import { useAppConfig } from "./useAppConfig.js";
5
5
  import { Agent, OPERATION_CANCELLED_BY_USER, cloneMessage, } from "wave-agent-sdk";
6
6
  import { logger } from "../utils/logger.js";
@@ -17,6 +17,7 @@ export const useChat = () => {
17
17
  };
18
18
  export const ChatProvider = ({ children, bypassPermissions, permissionMode: initialPermissionMode, pluginDirs, tools, allowedTools, disallowedTools, workdir, worktreeSession, version, model, }) => {
19
19
  const { restoreSessionId, continueLastSession } = useAppConfig();
20
+ const { stdout } = useStdout();
20
21
  // Message Display State
21
22
  const [isExpanded, setIsExpanded] = useState(false);
22
23
  const isExpandedRef = useRef(isExpanded);
@@ -62,8 +63,26 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
62
63
  const [confirmingTool, setConfirmingTool] = useState();
63
64
  const [confirmationQueue, setConfirmationQueue] = useState([]);
64
65
  const [currentConfirmation, setCurrentConfirmation] = useState(null);
65
- // Rewind state
66
- const [rewindId, setRewindId] = useState(0);
66
+ // Remount state
67
+ const [remountKey, setRemountKey] = useState(0);
68
+ const prevSessionId = useRef(null);
69
+ const requestRemount = useCallback(() => {
70
+ logger.info("requesting remount");
71
+ stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", () => {
72
+ setRemountKey((prev) => prev + 1);
73
+ });
74
+ }, [stdout]);
75
+ // Track sessionId changes to trigger remount
76
+ useEffect(() => {
77
+ if (prevSessionId.current &&
78
+ sessionId &&
79
+ prevSessionId.current !== sessionId) {
80
+ requestRemount();
81
+ }
82
+ if (sessionId) {
83
+ prevSessionId.current = sessionId;
84
+ }
85
+ }, [sessionId, requestRemount]);
67
86
  // Status metadata state
68
87
  const [workingDirectory, setWorkingDirectory] = useState("");
69
88
  // /btw state
@@ -370,14 +389,13 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
370
389
  if (agentRef.current) {
371
390
  try {
372
391
  await agentRef.current.truncateHistory(index);
373
- // Clear terminal screen after rewind
374
- setRewindId((prev) => prev + 1);
392
+ requestRemount();
375
393
  }
376
394
  catch (error) {
377
395
  logger.error("Failed to rewind:", error);
378
396
  }
379
397
  }
380
- }, []);
398
+ }, [requestRemount]);
381
399
  const getFullMessageThread = useCallback(async () => {
382
400
  if (agentRef.current) {
383
401
  return await agentRef.current.getFullMessageThread();
@@ -486,13 +504,15 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
486
504
  setPermissionMode,
487
505
  allowBypassInCycle,
488
506
  isConfirmationVisible,
507
+ hasPendingConfirmations: confirmationQueue.length > 0,
489
508
  confirmingTool,
490
509
  showConfirmation,
491
510
  hideConfirmation,
492
511
  handleConfirmationDecision,
493
512
  handleConfirmationCancel,
494
513
  backgroundCurrentTask,
495
- rewindId,
514
+ remountKey,
515
+ requestRemount,
496
516
  handleRewindSelect,
497
517
  getFullMessageThread,
498
518
  getGatewayConfig,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,wBAAsB,IAAI,kBAgYzB;AAGD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,wBAAsB,IAAI,kBAoXzB;AAGD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import yargs from "yargs";
2
2
  import { hideBin } from "yargs/helpers";
3
3
  import { startCli } from "./cli.js";
4
- import { logger } from "./utils/logger.js";
5
4
  import { generateRandomName } from "wave-agent-sdk";
6
5
  import { createWorktree } from "./utils/worktree.js";
7
6
  import path from "path";
@@ -13,15 +12,6 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
13
12
  const version = packageJson.version;
14
13
  // Export main function for external use
15
14
  export async function main() {
16
- // Start memory monitoring in debug mode
17
- if (process.env.LOG_LEVEL === "DEBUG") {
18
- setInterval(() => {
19
- const usage = process.memoryUsage();
20
- logger.debug(`[Memory] RSS: ${Math.round(usage.rss / 1024 / 1024)}MB, ` +
21
- `Heap: ${Math.round(usage.heapUsed / 1024 / 1024)}/${Math.round(usage.heapTotal / 1024 / 1024)}MB, ` +
22
- `External: ${Math.round(usage.external / 1024 / 1024)}MB`);
23
- }, 10000).unref();
24
- }
25
15
  try {
26
16
  const originalCwd = process.cwd();
27
17
  const argv = await yargs(hideBin(process.argv))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.12.3",
3
+ "version": "0.12.5",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,7 +42,7 @@
42
42
  "semver": "^7.7.4",
43
43
  "yargs": "^17.7.2",
44
44
  "zod": "^3.23.8",
45
- "wave-agent-sdk": "0.12.3"
45
+ "wave-agent-sdk": "0.12.5"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "^19.1.8",
package/src/acp/agent.ts CHANGED
@@ -360,7 +360,7 @@ export class WaveAcpAgent implements AcpAgent {
360
360
  }
361
361
  }
362
362
 
363
- const textContent = textBlocks.join("");
363
+ const textContent = textBlocks.join("\n");
364
364
 
365
365
  try {
366
366
  await agent.sendMessage(
@@ -1,7 +1,7 @@
1
- import React, { useState, useEffect, useRef, useCallback } from "react";
2
- import { useStdout, useInput } from "ink";
1
+ import React, { useState, useEffect, useCallback } from "react";
2
+ import { useInput } from "ink";
3
3
  import { ChatInterface } from "./ChatInterface.js";
4
- import { ChatProvider, useChat } from "../contexts/useChat.js";
4
+ import { ChatProvider } from "../contexts/useChat.js";
5
5
  import { AppProvider } from "../contexts/useAppConfig.js";
6
6
  import { WorktreeExitPrompt } from "./WorktreeExitPrompt.js";
7
7
  import {
@@ -107,52 +107,11 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
107
107
  version={version}
108
108
  model={model}
109
109
  >
110
- <ChatInterfaceWithRemount />
110
+ <ChatInterface />
111
111
  </ChatProvider>
112
112
  );
113
113
  };
114
114
 
115
- export const ChatInterfaceWithRemount: React.FC = () => {
116
- const { stdout } = useStdout();
117
- const { isExpanded, rewindId, sessionId } = useChat();
118
-
119
- const [remountKey, setRemountKey] = useState(String(isExpanded) + rewindId);
120
-
121
- const prevSessionId = useRef(sessionId);
122
- const isRemountScheduled = useRef(false);
123
-
124
- useEffect(() => {
125
- const newKey =
126
- String(isExpanded) +
127
- rewindId +
128
- (prevSessionId.current && sessionId && prevSessionId.current !== sessionId
129
- ? sessionId
130
- : "");
131
-
132
- if (newKey !== remountKey && !isRemountScheduled.current) {
133
- isRemountScheduled.current = true;
134
-
135
- const timeout = setTimeout(() => {
136
- stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", (err?: Error | null) => {
137
- if (err) {
138
- console.error("Failed to clear terminal:", err);
139
- }
140
- setRemountKey(newKey);
141
- isRemountScheduled.current = false;
142
- });
143
- }, 100);
144
-
145
- return () => clearTimeout(timeout);
146
- }
147
-
148
- if (sessionId) {
149
- prevSessionId.current = sessionId;
150
- }
151
- }, [isExpanded, rewindId, sessionId, remountKey, stdout]);
152
-
153
- return <ChatInterface remountKey={remountKey} />;
154
- };
155
-
156
115
  export const App: React.FC<AppProps> = ({
157
116
  restoreSessionId,
158
117
  continueLastSession,
@@ -1,5 +1,6 @@
1
- import React from "react";
2
- import { Box } from "ink";
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import { Box, useStdout, measureElement, Static } from "ink";
3
+ import type { DOMElement } from "ink";
3
4
  import { MessageList } from "./MessageList.js";
4
5
  import { BtwDisplay } from "./BtwDisplay.js";
5
6
  import { InputBox } from "./InputBox.js";
@@ -11,9 +12,7 @@ import { ConfirmationSelector } from "./ConfirmationSelector.js";
11
12
 
12
13
  import { useChat } from "../contexts/useChat.js";
13
14
 
14
- export const ChatInterface: React.FC<{ remountKey: string }> = ({
15
- remountKey,
16
- }) => {
15
+ export const ChatInterface: React.FC = () => {
17
16
  const {
18
17
  messages,
19
18
  isLoading,
@@ -30,26 +29,54 @@ export const ChatInterface: React.FC<{ remountKey: string }> = ({
30
29
  slashCommands,
31
30
  hasSlashCommand,
32
31
  isConfirmationVisible,
32
+ hasPendingConfirmations,
33
33
  confirmingTool,
34
34
  handleConfirmationDecision,
35
35
  handleConfirmationCancel,
36
36
  version,
37
37
  workdir,
38
38
  btwState,
39
+ remountKey,
40
+ requestRemount,
39
41
  } = useChat();
40
42
 
41
43
  const displayMessages = messages;
42
44
 
45
+ const [forceStatic, setForceStatic] = useState(false);
46
+ const { stdout } = useStdout();
47
+ const terminalHeight = stdout?.rows ?? 24;
48
+ const chatInterfaceRef = useRef<DOMElement>(null);
49
+
50
+ // Handle forceStatic mode for overflow and request remount when exiting
51
+ useEffect(() => {
52
+ if (isConfirmationVisible && chatInterfaceRef.current) {
53
+ const { height } = measureElement(chatInterfaceRef.current);
54
+ if (height > terminalHeight) {
55
+ setForceStatic(true);
56
+ }
57
+ } else if (forceStatic && !hasPendingConfirmations) {
58
+ setForceStatic(false);
59
+ requestRemount();
60
+ }
61
+ }, [
62
+ isConfirmationVisible,
63
+ terminalHeight,
64
+ forceStatic,
65
+ hasPendingConfirmations,
66
+ requestRemount,
67
+ ]);
68
+
43
69
  if (!sessionId) return null;
44
70
 
45
71
  return (
46
- <Box flexDirection="column">
72
+ <Box ref={chatInterfaceRef} flexDirection="column">
47
73
  <MessageList
48
74
  key={remountKey}
49
75
  messages={displayMessages}
50
76
  isExpanded={isExpanded}
51
77
  version={version}
52
78
  workdir={workdir}
79
+ forceStatic={forceStatic}
53
80
  />
54
81
 
55
82
  {!isConfirmationVisible && !isExpanded && (
@@ -81,12 +108,25 @@ export const ChatInterface: React.FC<{ remountKey: string }> = ({
81
108
 
82
109
  {isConfirmationVisible && (
83
110
  <>
84
- <ConfirmationDetails
85
- toolName={confirmingTool!.name}
86
- toolInput={confirmingTool!.input}
87
- planContent={confirmingTool!.planContent}
88
- isExpanded={isExpanded}
89
- />
111
+ {forceStatic ? (
112
+ <Static items={[{ key: "confirmation-details" }]}>
113
+ {() => (
114
+ <ConfirmationDetails
115
+ toolName={confirmingTool!.name}
116
+ toolInput={confirmingTool!.input}
117
+ planContent={confirmingTool!.planContent}
118
+ isExpanded={isExpanded}
119
+ />
120
+ )}
121
+ </Static>
122
+ ) : (
123
+ <ConfirmationDetails
124
+ toolName={confirmingTool!.name}
125
+ toolInput={confirmingTool!.input}
126
+ planContent={confirmingTool!.planContent}
127
+ isExpanded={isExpanded}
128
+ />
129
+ )}
90
130
  <ConfirmationSelector
91
131
  toolName={confirmingTool!.name}
92
132
  toolInput={confirmingTool!.input}
@@ -13,7 +13,13 @@ export interface MessageListProps {
13
13
  }
14
14
 
15
15
  export const MessageList = React.memo(
16
- ({ messages, isExpanded = false, version, workdir }: MessageListProps) => {
16
+ ({
17
+ messages,
18
+ isExpanded = false,
19
+ forceStatic = false,
20
+ version,
21
+ workdir,
22
+ }: MessageListProps) => {
17
23
  const welcomeMessage = (
18
24
  <Box flexDirection="column" paddingTop={1}>
19
25
  <Text color="gray">WAVE{version ? ` v${version}` : ""}</Text>
@@ -58,7 +64,8 @@ export const MessageList = React.memo(
58
64
  const blocksWithStatus = allBlocks.map((item) => {
59
65
  const { block } = item;
60
66
  const isDynamic =
61
- messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block);
67
+ !forceStatic &&
68
+ (messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block));
62
69
  return { ...item, isDynamic };
63
70
  });
64
71
 
@@ -7,7 +7,7 @@ import React, {
7
7
  useState,
8
8
  useMemo,
9
9
  } from "react";
10
- import { useInput } from "ink";
10
+ import { useInput, useStdout } from "ink";
11
11
  import { useAppConfig } from "./useAppConfig.js";
12
12
  import type {
13
13
  Message,
@@ -86,6 +86,7 @@ export interface ChatContextType {
86
86
  allowBypassInCycle: boolean;
87
87
  // Permission confirmation state
88
88
  isConfirmationVisible: boolean;
89
+ hasPendingConfirmations: boolean;
89
90
  confirmingTool?: {
90
91
  name: string;
91
92
  input?: Record<string, unknown>;
@@ -105,8 +106,10 @@ export interface ChatContextType {
105
106
  handleConfirmationCancel: () => void;
106
107
  // Background current task
107
108
  backgroundCurrentTask: () => void;
109
+ // Remount functionality
110
+ remountKey: number;
111
+ requestRemount: () => void;
108
112
  // Rewind functionality
109
- rewindId: number;
110
113
  handleRewindSelect: (index: number) => Promise<void>;
111
114
  getFullMessageThread: () => Promise<{
112
115
  messages: Message[];
@@ -152,6 +155,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
152
155
  model,
153
156
  }) => {
154
157
  const { restoreSessionId, continueLastSession } = useAppConfig();
158
+ const { stdout } = useStdout();
155
159
 
156
160
  // Message Display State
157
161
  const [isExpanded, setIsExpanded] = useState(false);
@@ -250,8 +254,30 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
250
254
  reject: () => void;
251
255
  } | null>(null);
252
256
 
253
- // Rewind state
254
- const [rewindId, setRewindId] = useState(0);
257
+ // Remount state
258
+ const [remountKey, setRemountKey] = useState(0);
259
+ const prevSessionId = useRef<string | null>(null);
260
+
261
+ const requestRemount = useCallback(() => {
262
+ logger.info("requesting remount");
263
+ stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", () => {
264
+ setRemountKey((prev) => prev + 1);
265
+ });
266
+ }, [stdout]);
267
+
268
+ // Track sessionId changes to trigger remount
269
+ useEffect(() => {
270
+ if (
271
+ prevSessionId.current &&
272
+ sessionId &&
273
+ prevSessionId.current !== sessionId
274
+ ) {
275
+ requestRemount();
276
+ }
277
+ if (sessionId) {
278
+ prevSessionId.current = sessionId;
279
+ }
280
+ }, [sessionId, requestRemount]);
255
281
 
256
282
  // Status metadata state
257
283
  const [workingDirectory, setWorkingDirectory] = useState("");
@@ -623,18 +649,19 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
623
649
  agentRef.current?.backgroundCurrentTask();
624
650
  }, []);
625
651
 
626
- const handleRewindSelect = useCallback(async (index: number) => {
627
- if (agentRef.current) {
628
- try {
629
- await agentRef.current.truncateHistory(index);
630
-
631
- // Clear terminal screen after rewind
632
- setRewindId((prev) => prev + 1);
633
- } catch (error) {
634
- logger.error("Failed to rewind:", error);
652
+ const handleRewindSelect = useCallback(
653
+ async (index: number) => {
654
+ if (agentRef.current) {
655
+ try {
656
+ await agentRef.current.truncateHistory(index);
657
+ requestRemount();
658
+ } catch (error) {
659
+ logger.error("Failed to rewind:", error);
660
+ }
635
661
  }
636
- }
637
- }, []);
662
+ },
663
+ [requestRemount],
664
+ );
638
665
 
639
666
  const getFullMessageThread = useCallback(async () => {
640
667
  if (agentRef.current) {
@@ -749,13 +776,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
749
776
  setPermissionMode,
750
777
  allowBypassInCycle,
751
778
  isConfirmationVisible,
779
+ hasPendingConfirmations: confirmationQueue.length > 0,
752
780
  confirmingTool,
753
781
  showConfirmation,
754
782
  hideConfirmation,
755
783
  handleConfirmationDecision,
756
784
  handleConfirmationCancel,
757
785
  backgroundCurrentTask,
758
- rewindId,
786
+ remountKey,
787
+ requestRemount,
759
788
  handleRewindSelect,
760
789
  getFullMessageThread,
761
790
 
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import yargs from "yargs";
2
2
  import { hideBin } from "yargs/helpers";
3
3
  import { startCli } from "./cli.js";
4
- import { logger } from "./utils/logger.js";
5
4
  import { Scope, generateRandomName, type PermissionMode } from "wave-agent-sdk";
6
5
  import { createWorktree, type WorktreeSession } from "./utils/worktree.js";
7
6
  import path from "path";
@@ -15,18 +14,6 @@ const version = packageJson.version;
15
14
 
16
15
  // Export main function for external use
17
16
  export async function main() {
18
- // Start memory monitoring in debug mode
19
- if (process.env.LOG_LEVEL === "DEBUG") {
20
- setInterval(() => {
21
- const usage = process.memoryUsage();
22
- logger.debug(
23
- `[Memory] RSS: ${Math.round(usage.rss / 1024 / 1024)}MB, ` +
24
- `Heap: ${Math.round(usage.heapUsed / 1024 / 1024)}/${Math.round(usage.heapTotal / 1024 / 1024)}MB, ` +
25
- `External: ${Math.round(usage.external / 1024 / 1024)}MB`,
26
- );
27
- }, 10000).unref();
28
- }
29
-
30
17
  try {
31
18
  const originalCwd = process.cwd();
32
19
  const argv = await yargs(hideBin(process.argv))