wave-code 1.0.8 → 1.0.9
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/bin/wave-code.js +21 -0
- package/dist/contexts/useChat.js +25 -5
- package/package.json +2 -2
- package/src/contexts/useChat.tsx +26 -5
package/bin/wave-code.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readFileSync, writeSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
// `wave -v` / `wave --version` must be fast: editors probe the installed CLI
|
|
8
|
+
// version on every launch (e.g. the desktop app's auto-update check). Loading
|
|
9
|
+
// the full app graph (wave-agent-sdk, ink, highlight.js, ...) just to print
|
|
10
|
+
// the version takes 2-3s+ on a warm machine and can exceed callers' probe
|
|
11
|
+
// timeouts on cold starts (AV scan of freshly installed files), which they
|
|
12
|
+
// misread as "CLI missing/corrupt" → spurious re-installs. Print the version
|
|
13
|
+
// straight from package.json and exit before touching the app graph.
|
|
14
|
+
const versionArgs = ["-v", "--version"];
|
|
15
|
+
if (process.argv.slice(2).some((a) => versionArgs.includes(a))) {
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const packageJson = JSON.parse(
|
|
18
|
+
readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"),
|
|
19
|
+
);
|
|
20
|
+
writeSync(1, `${packageJson.version}\n`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
3
24
|
// Import and start the CLI
|
|
4
25
|
import("../dist/index.js")
|
|
5
26
|
.then(async ({ main }) => {
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -14,6 +14,21 @@ export const useChat = () => {
|
|
|
14
14
|
}
|
|
15
15
|
return context;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Snapshot a SDK message for consumer state. The SDK mutates its internal
|
|
19
|
+
* message blocks in-place BEFORE firing the delta callback (it writes the full
|
|
20
|
+
* accumulated value to the shared block, then computes the chunk delta by
|
|
21
|
+
* slicing the new value). A consumer that pushed the SDK message object by
|
|
22
|
+
* live reference would read the already-updated block and append the delta
|
|
23
|
+
* again — the first delta is double-counted ("LetLet me think..."), affecting
|
|
24
|
+
* reasoning and text content alike. See docs/specs/core/stream-content-updates.md.
|
|
25
|
+
* The clone must be at least one layer deep (message + blocks) so the in-place
|
|
26
|
+
* block mutation never leaks into consumer state.
|
|
27
|
+
*/
|
|
28
|
+
const snapshotMessage = (message) => ({
|
|
29
|
+
...message,
|
|
30
|
+
blocks: message.blocks.map((block) => ({ ...block })),
|
|
31
|
+
});
|
|
17
32
|
/**
|
|
18
33
|
* Window-concat throttle for pure-delta streaming updates: chunks arriving
|
|
19
34
|
* within the cooldown window are merged so no delta is lost (a dropped delta
|
|
@@ -322,7 +337,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
322
337
|
// the incremental callbacks in initializeAgent below.
|
|
323
338
|
const refreshMessages = useCallback(() => {
|
|
324
339
|
if (!isExpandedRef.current && agentRef.current) {
|
|
325
|
-
const msgs =
|
|
340
|
+
const msgs = agentRef.current.messages.map(snapshotMessage);
|
|
326
341
|
setMessages(msgs);
|
|
327
342
|
setLatestTotalTokens(extractLatestTotalTokens(msgs));
|
|
328
343
|
}
|
|
@@ -357,7 +372,9 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
357
372
|
const last = msgs[msgs.length - 1];
|
|
358
373
|
if (!last || last.role !== "user")
|
|
359
374
|
return;
|
|
360
|
-
setMessages((prev) => prev.some((m) => m.id === last.id)
|
|
375
|
+
setMessages((prev) => prev.some((m) => m.id === last.id)
|
|
376
|
+
? prev
|
|
377
|
+
: [...prev, snapshotMessage(last)]);
|
|
361
378
|
},
|
|
362
379
|
onAssistantMessageAdded: (messageId) => {
|
|
363
380
|
if (isExpandedRef.current || !agentRef.current)
|
|
@@ -365,7 +382,9 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
365
382
|
const msg = agentRef.current.messages.find((m) => m.id === messageId);
|
|
366
383
|
if (!msg)
|
|
367
384
|
return;
|
|
368
|
-
setMessages((prev) => prev.some((m) => m.id === messageId)
|
|
385
|
+
setMessages((prev) => prev.some((m) => m.id === messageId)
|
|
386
|
+
? prev
|
|
387
|
+
: [...prev, snapshotMessage(msg)]);
|
|
369
388
|
},
|
|
370
389
|
onAssistantContentUpdated: (params) => {
|
|
371
390
|
if (isExpandedRef.current)
|
|
@@ -562,9 +581,10 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
562
581
|
};
|
|
563
582
|
agent.setWorktreeSession(session);
|
|
564
583
|
}
|
|
565
|
-
// Get initial state
|
|
584
|
+
// Get initial state — snapshot the SDK messages (never hold live
|
|
585
|
+
// references; see snapshotMessage)
|
|
566
586
|
setSessionId(agent.sessionId);
|
|
567
|
-
setMessages(agent.messages);
|
|
587
|
+
setMessages(agent.messages.map(snapshotMessage));
|
|
568
588
|
setIsLoading(agent.isLoading);
|
|
569
589
|
setLatestTotalTokens(extractLatestTotalTokens(agent.messages));
|
|
570
590
|
setIsCommandRunning(agent.isCommandRunning);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"wrap-ansi": "^10.0.0",
|
|
44
44
|
"yargs": "^17.7.2",
|
|
45
45
|
"zod": "^3.23.8",
|
|
46
|
-
"wave-agent-sdk": "1.0.
|
|
46
|
+
"wave-agent-sdk": "1.0.9"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/react": "^19.1.8",
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -168,6 +168,22 @@ interface StreamingUpdateParams {
|
|
|
168
168
|
stage: "streaming" | "end";
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Snapshot a SDK message for consumer state. The SDK mutates its internal
|
|
173
|
+
* message blocks in-place BEFORE firing the delta callback (it writes the full
|
|
174
|
+
* accumulated value to the shared block, then computes the chunk delta by
|
|
175
|
+
* slicing the new value). A consumer that pushed the SDK message object by
|
|
176
|
+
* live reference would read the already-updated block and append the delta
|
|
177
|
+
* again — the first delta is double-counted ("LetLet me think..."), affecting
|
|
178
|
+
* reasoning and text content alike. See docs/specs/core/stream-content-updates.md.
|
|
179
|
+
* The clone must be at least one layer deep (message + blocks) so the in-place
|
|
180
|
+
* block mutation never leaks into consumer state.
|
|
181
|
+
*/
|
|
182
|
+
const snapshotMessage = (message: Message): Message => ({
|
|
183
|
+
...message,
|
|
184
|
+
blocks: message.blocks.map((block) => ({ ...block })),
|
|
185
|
+
});
|
|
186
|
+
|
|
171
187
|
/**
|
|
172
188
|
* Window-concat throttle for pure-delta streaming updates: chunks arriving
|
|
173
189
|
* within the cooldown window are merged so no delta is lost (a dropped delta
|
|
@@ -605,7 +621,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
605
621
|
// the incremental callbacks in initializeAgent below.
|
|
606
622
|
const refreshMessages = useCallback(() => {
|
|
607
623
|
if (!isExpandedRef.current && agentRef.current) {
|
|
608
|
-
const msgs =
|
|
624
|
+
const msgs = agentRef.current.messages.map(snapshotMessage);
|
|
609
625
|
setMessages(msgs);
|
|
610
626
|
setLatestTotalTokens(extractLatestTotalTokens(msgs));
|
|
611
627
|
}
|
|
@@ -656,7 +672,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
656
672
|
const last = msgs[msgs.length - 1];
|
|
657
673
|
if (!last || last.role !== "user") return;
|
|
658
674
|
setMessages((prev) =>
|
|
659
|
-
prev.some((m) => m.id === last.id)
|
|
675
|
+
prev.some((m) => m.id === last.id)
|
|
676
|
+
? prev
|
|
677
|
+
: [...prev, snapshotMessage(last)],
|
|
660
678
|
);
|
|
661
679
|
},
|
|
662
680
|
onAssistantMessageAdded: (messageId: string) => {
|
|
@@ -664,7 +682,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
664
682
|
const msg = agentRef.current.messages.find((m) => m.id === messageId);
|
|
665
683
|
if (!msg) return;
|
|
666
684
|
setMessages((prev) =>
|
|
667
|
-
prev.some((m) => m.id === messageId)
|
|
685
|
+
prev.some((m) => m.id === messageId)
|
|
686
|
+
? prev
|
|
687
|
+
: [...prev, snapshotMessage(msg)],
|
|
668
688
|
);
|
|
669
689
|
},
|
|
670
690
|
onAssistantContentUpdated: (params) => {
|
|
@@ -889,9 +909,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
889
909
|
agent.setWorktreeSession(session);
|
|
890
910
|
}
|
|
891
911
|
|
|
892
|
-
// Get initial state
|
|
912
|
+
// Get initial state — snapshot the SDK messages (never hold live
|
|
913
|
+
// references; see snapshotMessage)
|
|
893
914
|
setSessionId(agent.sessionId);
|
|
894
|
-
setMessages(agent.messages);
|
|
915
|
+
setMessages(agent.messages.map(snapshotMessage));
|
|
895
916
|
setIsLoading(agent.isLoading);
|
|
896
917
|
setLatestTotalTokens(extractLatestTotalTokens(agent.messages));
|
|
897
918
|
setIsCommandRunning(agent.isCommandRunning);
|