wave-code 0.12.1 → 0.12.3
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/components/App.d.ts +1 -0
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +9 -14
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +2 -1
- package/dist/components/BangDisplay.d.ts.map +1 -1
- package/dist/components/BangDisplay.js +2 -14
- package/dist/components/ChatInterface.d.ts +3 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +4 -65
- package/dist/components/ConfirmationDetails.d.ts +0 -2
- package/dist/components/ConfirmationDetails.d.ts.map +1 -1
- package/dist/components/ConfirmationDetails.js +3 -15
- package/dist/components/ConfirmationSelector.d.ts +0 -1
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +4 -12
- package/dist/components/MessageList.d.ts +1 -2
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +5 -16
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +2 -4
- package/dist/components/ToolDisplay.d.ts.map +1 -1
- package/dist/components/ToolDisplay.js +2 -5
- package/dist/contexts/useChat.d.ts +0 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +26 -15
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -6
- package/dist/utils/throttle.d.ts +15 -0
- package/dist/utils/throttle.d.ts.map +1 -0
- package/dist/utils/throttle.js +55 -0
- package/package.json +2 -2
- package/src/components/App.tsx +11 -17
- package/src/components/BackgroundTaskManager.tsx +3 -6
- package/src/components/BangDisplay.tsx +4 -22
- package/src/components/ChatInterface.tsx +8 -84
- package/src/components/ConfirmationDetails.tsx +2 -25
- package/src/components/ConfirmationSelector.tsx +3 -15
- package/src/components/MessageList.tsx +5 -25
- package/src/components/RewindCommand.tsx +3 -5
- package/src/components/ToolDisplay.tsx +2 -5
- package/src/contexts/useChat.tsx +34 -16
- package/src/utils/logger.ts +3 -7
- package/src/utils/throttle.ts +75 -0
package/src/contexts/useChat.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import React, {
|
|
|
5
5
|
useRef,
|
|
6
6
|
useEffect,
|
|
7
7
|
useState,
|
|
8
|
+
useMemo,
|
|
8
9
|
} from "react";
|
|
9
10
|
import { useInput } from "ink";
|
|
10
11
|
import { useAppConfig } from "./useAppConfig.js";
|
|
@@ -22,8 +23,10 @@ import {
|
|
|
22
23
|
AgentCallbacks,
|
|
23
24
|
type ToolPermissionContext,
|
|
24
25
|
OPERATION_CANCELLED_BY_USER,
|
|
26
|
+
cloneMessage,
|
|
25
27
|
} from "wave-agent-sdk";
|
|
26
28
|
import { logger } from "../utils/logger.js";
|
|
29
|
+
import { throttle } from "../utils/throttle.js";
|
|
27
30
|
import { displayUsageSummary } from "../utils/usageSummary.js";
|
|
28
31
|
import { expandLongTextPlaceholders } from "../managers/inputHandlers.js";
|
|
29
32
|
|
|
@@ -109,8 +112,6 @@ export interface ChatContextType {
|
|
|
109
112
|
messages: Message[];
|
|
110
113
|
sessionIds: string[];
|
|
111
114
|
}>;
|
|
112
|
-
wasLastDetailsTooTall: number;
|
|
113
|
-
setWasLastDetailsTooTall: React.Dispatch<React.SetStateAction<number>>;
|
|
114
115
|
// Status metadata
|
|
115
116
|
getGatewayConfig: () => import("wave-agent-sdk").GatewayConfig;
|
|
116
117
|
getModelConfig: () => import("wave-agent-sdk").ModelConfig;
|
|
@@ -156,10 +157,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
156
157
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
157
158
|
const isExpandedRef = useRef(isExpanded);
|
|
158
159
|
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
isExpandedRef.current = isExpanded;
|
|
161
|
-
}, [isExpanded]);
|
|
162
|
-
|
|
163
160
|
const [isTaskListVisible, setIsTaskListVisible] = useState(true);
|
|
164
161
|
|
|
165
162
|
const [queuedMessages, setQueuedMessages] = useState<
|
|
@@ -170,9 +167,31 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
170
167
|
}>
|
|
171
168
|
>([]);
|
|
172
169
|
|
|
173
|
-
// AI State
|
|
174
170
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
175
171
|
|
|
172
|
+
const throttledSetMessages = useMemo(
|
|
173
|
+
() =>
|
|
174
|
+
throttle(() => {
|
|
175
|
+
if (!isExpandedRef.current && agentRef.current) {
|
|
176
|
+
setMessages([...agentRef.current.messages]);
|
|
177
|
+
}
|
|
178
|
+
}, 300),
|
|
179
|
+
[],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
isExpandedRef.current = isExpanded;
|
|
184
|
+
if (isExpanded) {
|
|
185
|
+
throttledSetMessages.cancel();
|
|
186
|
+
}
|
|
187
|
+
}, [isExpanded, throttledSetMessages]);
|
|
188
|
+
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
return () => {
|
|
191
|
+
throttledSetMessages.cancel();
|
|
192
|
+
};
|
|
193
|
+
}, [throttledSetMessages]);
|
|
194
|
+
|
|
176
195
|
const [isLoading, setIsLoading] = useState(false);
|
|
177
196
|
const [latestTotalTokens, setlatestTotalTokens] = useState(0);
|
|
178
197
|
const [sessionId, setSessionId] = useState("");
|
|
@@ -259,8 +278,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
259
278
|
[],
|
|
260
279
|
);
|
|
261
280
|
|
|
262
|
-
// Confirmation too tall state
|
|
263
|
-
const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
|
|
264
281
|
const allowBypassInCycle =
|
|
265
282
|
!!bypassPermissions || initialPermissionMode === "bypassPermissions";
|
|
266
283
|
|
|
@@ -298,9 +315,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
298
315
|
const initializeAgent = async () => {
|
|
299
316
|
const callbacks: AgentCallbacks = {
|
|
300
317
|
onMessagesChange: () => {
|
|
301
|
-
|
|
302
|
-
setMessages([...agentRef.current.messages]);
|
|
303
|
-
}
|
|
318
|
+
throttledSetMessages();
|
|
304
319
|
},
|
|
305
320
|
onServersChange: (servers) => {
|
|
306
321
|
setMcpServers([...servers]);
|
|
@@ -415,6 +430,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
415
430
|
worktreeSession,
|
|
416
431
|
model,
|
|
417
432
|
initialPermissionMode,
|
|
433
|
+
throttledSetMessages,
|
|
418
434
|
]);
|
|
419
435
|
|
|
420
436
|
// Cleanup on unmount
|
|
@@ -471,7 +487,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
471
487
|
if (command) {
|
|
472
488
|
setIsCommandRunning(true);
|
|
473
489
|
try {
|
|
474
|
-
await agentRef.current?.
|
|
490
|
+
await agentRef.current?.bang(command);
|
|
475
491
|
} finally {
|
|
476
492
|
setIsCommandRunning(false);
|
|
477
493
|
}
|
|
@@ -661,13 +677,16 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
661
677
|
// Clear terminal screen when expanded state changes
|
|
662
678
|
setIsExpanded((prev) => {
|
|
663
679
|
const newExpanded = !prev;
|
|
680
|
+
isExpandedRef.current = newExpanded;
|
|
664
681
|
if (newExpanded) {
|
|
665
682
|
// Transitioning to EXPANDED: Freeze the current view
|
|
683
|
+
// Cancel any pending throttled updates to avoid overwriting the frozen state
|
|
684
|
+
throttledSetMessages.cancel();
|
|
666
685
|
// Deep copy the last message to ensure it doesn't update if the agent is still writing to it
|
|
667
686
|
setMessages((currentMessages) => {
|
|
668
687
|
if (currentMessages.length === 0) return currentMessages;
|
|
669
688
|
const lastMessage = currentMessages[currentMessages.length - 1];
|
|
670
|
-
const frozenLastMessage =
|
|
689
|
+
const frozenLastMessage = cloneMessage(lastMessage);
|
|
671
690
|
return [...currentMessages.slice(0, -1), frozenLastMessage];
|
|
672
691
|
});
|
|
673
692
|
} else {
|
|
@@ -739,8 +758,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
739
758
|
rewindId,
|
|
740
759
|
handleRewindSelect,
|
|
741
760
|
getFullMessageThread,
|
|
742
|
-
|
|
743
|
-
setWasLastDetailsTooTall,
|
|
761
|
+
|
|
744
762
|
getGatewayConfig,
|
|
745
763
|
getModelConfig,
|
|
746
764
|
workingDirectory,
|
package/src/utils/logger.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import * as fs from "fs";
|
|
12
12
|
import { Chalk } from "chalk";
|
|
13
|
+
import { getLastLines } from "wave-agent-sdk";
|
|
13
14
|
import { LOG_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
14
15
|
|
|
15
16
|
const chalk = new Chalk({ level: 3 });
|
|
@@ -269,19 +270,14 @@ const truncateLogFileIfNeeded = (config: LogCleanupConfig): void => {
|
|
|
269
270
|
// If file size exceeds limit, truncate file
|
|
270
271
|
if (stats.size > config.maxFileSize) {
|
|
271
272
|
const content = fs.readFileSync(logFile, "utf8");
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
// Keep the last specified number of lines
|
|
275
|
-
const keepLines = Math.min(config.keepLines, lines.length);
|
|
276
|
-
const truncatedContent = lines.slice(-keepLines).join("\n");
|
|
273
|
+
const truncatedContent = getLastLines(content, config.keepLines);
|
|
277
274
|
|
|
278
275
|
// Write truncated content
|
|
279
276
|
fs.writeFileSync(logFile, truncatedContent);
|
|
280
277
|
|
|
281
278
|
// Record truncation operation
|
|
282
|
-
const removedLines = lines.length - keepLines;
|
|
283
279
|
logger.debug(
|
|
284
|
-
`Log file truncated:
|
|
280
|
+
`Log file truncated: file size ${stats.size} exceeded limit ${config.maxFileSize}, kept last ${config.keepLines} lines`,
|
|
285
281
|
);
|
|
286
282
|
}
|
|
287
283
|
} catch (error) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export interface ThrottleOptions {
|
|
2
|
+
leading?: boolean;
|
|
3
|
+
trailing?: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ThrottledFunction<T extends (...args: unknown[]) => void> {
|
|
7
|
+
(...args: Parameters<T>): void;
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
flush: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a throttled function that only invokes `func` at most once per
|
|
14
|
+
* every `wait` milliseconds.
|
|
15
|
+
*/
|
|
16
|
+
export function throttle<T extends (...args: unknown[]) => void>(
|
|
17
|
+
func: T,
|
|
18
|
+
wait: number,
|
|
19
|
+
options: ThrottleOptions = { leading: true, trailing: true },
|
|
20
|
+
): ThrottledFunction<T> {
|
|
21
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
22
|
+
let lastArgs: Parameters<T> | null = null;
|
|
23
|
+
let lastCallTime = 0;
|
|
24
|
+
|
|
25
|
+
const invokeFunc = () => {
|
|
26
|
+
if (lastArgs) {
|
|
27
|
+
func(...lastArgs);
|
|
28
|
+
lastCallTime = Date.now();
|
|
29
|
+
lastArgs = null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const throttled = (...args: Parameters<T>) => {
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const remaining = wait - (now - lastCallTime);
|
|
36
|
+
|
|
37
|
+
lastArgs = args;
|
|
38
|
+
|
|
39
|
+
if (remaining <= 0 || remaining > wait) {
|
|
40
|
+
if (timeoutId) {
|
|
41
|
+
clearTimeout(timeoutId);
|
|
42
|
+
timeoutId = null;
|
|
43
|
+
}
|
|
44
|
+
if (options.leading !== false || lastCallTime !== 0) {
|
|
45
|
+
invokeFunc();
|
|
46
|
+
} else {
|
|
47
|
+
lastCallTime = now;
|
|
48
|
+
}
|
|
49
|
+
} else if (!timeoutId && options.trailing !== false) {
|
|
50
|
+
timeoutId = setTimeout(() => {
|
|
51
|
+
timeoutId = null;
|
|
52
|
+
invokeFunc();
|
|
53
|
+
}, remaining);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
throttled.cancel = () => {
|
|
58
|
+
if (timeoutId) {
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
timeoutId = null;
|
|
61
|
+
}
|
|
62
|
+
lastArgs = null;
|
|
63
|
+
lastCallTime = 0;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
throttled.flush = () => {
|
|
67
|
+
if (timeoutId) {
|
|
68
|
+
clearTimeout(timeoutId);
|
|
69
|
+
timeoutId = null;
|
|
70
|
+
}
|
|
71
|
+
invokeFunc();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return throttled as ThrottledFunction<T>;
|
|
75
|
+
}
|