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 +1 -1
- package/dist/components/App.d.ts +0 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -35
- package/dist/components/ChatInterface.d.ts +1 -3
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +28 -4
- package/dist/components/MessageList.d.ts +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +3 -2
- package/dist/contexts/useChat.d.ts +3 -1
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +27 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -10
- package/package.json +2 -2
- package/src/acp/agent.ts +1 -1
- package/src/components/App.tsx +4 -45
- package/src/components/ChatInterface.tsx +52 -12
- package/src/components/MessageList.tsx +9 -2
- package/src/contexts/useChat.tsx +45 -16
- package/src/index.ts +0 -13
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}`);
|
package/dist/components/App.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/components/App.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect,
|
|
3
|
-
import {
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { useInput } from "ink";
|
|
4
4
|
import { ChatInterface } from "./ChatInterface.js";
|
|
5
|
-
import { ChatProvider
|
|
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(
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
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 {
|
|
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 = (
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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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"}
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -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
|
-
//
|
|
66
|
-
const [
|
|
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
|
-
|
|
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
|
-
|
|
514
|
+
remountKey,
|
|
515
|
+
requestRemount,
|
|
496
516
|
handleRewindSelect,
|
|
497
517
|
getFullMessageThread,
|
|
498
518
|
getGatewayConfig,
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
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
|
+
"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.
|
|
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
package/src/components/App.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { useState, useEffect,
|
|
2
|
-
import {
|
|
1
|
+
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import { useInput } from "ink";
|
|
3
3
|
import { ChatInterface } from "./ChatInterface.js";
|
|
4
|
-
import { ChatProvider
|
|
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
|
-
<
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
({
|
|
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
|
-
|
|
67
|
+
!forceStatic &&
|
|
68
|
+
(messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block));
|
|
62
69
|
return { ...item, isDynamic };
|
|
63
70
|
});
|
|
64
71
|
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -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
|
-
//
|
|
254
|
-
const [
|
|
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(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
-
|
|
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))
|