wave-code 0.0.5 → 0.0.8

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 (93) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/components/App.d.ts +1 -0
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +4 -4
  8. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  9. package/dist/components/BashHistorySelector.js +17 -3
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +6 -24
  12. package/dist/components/CommandSelector.js +4 -4
  13. package/dist/components/Confirmation.d.ts +11 -0
  14. package/dist/components/Confirmation.d.ts.map +1 -0
  15. package/dist/components/Confirmation.js +148 -0
  16. package/dist/components/DiffDisplay.d.ts +8 -0
  17. package/dist/components/DiffDisplay.d.ts.map +1 -0
  18. package/dist/components/DiffDisplay.js +168 -0
  19. package/dist/components/FileSelector.d.ts +2 -4
  20. package/dist/components/FileSelector.d.ts.map +1 -1
  21. package/dist/components/FileSelector.js +2 -2
  22. package/dist/components/InputBox.d.ts.map +1 -1
  23. package/dist/components/InputBox.js +30 -50
  24. package/dist/components/Markdown.d.ts +6 -0
  25. package/dist/components/Markdown.d.ts.map +1 -0
  26. package/dist/components/Markdown.js +22 -0
  27. package/dist/components/MemoryDisplay.js +1 -1
  28. package/dist/components/MessageItem.d.ts +8 -0
  29. package/dist/components/MessageItem.d.ts.map +1 -0
  30. package/dist/components/MessageItem.js +15 -0
  31. package/dist/components/MessageList.d.ts +1 -1
  32. package/dist/components/MessageList.d.ts.map +1 -1
  33. package/dist/components/MessageList.js +33 -33
  34. package/dist/components/ReasoningDisplay.d.ts +8 -0
  35. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  36. package/dist/components/ReasoningDisplay.js +10 -0
  37. package/dist/components/SubagentBlock.d.ts +0 -1
  38. package/dist/components/SubagentBlock.d.ts.map +1 -1
  39. package/dist/components/SubagentBlock.js +29 -30
  40. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  41. package/dist/components/ToolResultDisplay.js +6 -5
  42. package/dist/contexts/useChat.d.ts +14 -2
  43. package/dist/contexts/useChat.d.ts.map +1 -1
  44. package/dist/contexts/useChat.js +128 -17
  45. package/dist/hooks/useInputManager.d.ts +6 -1
  46. package/dist/hooks/useInputManager.d.ts.map +1 -1
  47. package/dist/hooks/useInputManager.js +32 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +30 -5
  50. package/dist/managers/InputManager.d.ts +11 -1
  51. package/dist/managers/InputManager.d.ts.map +1 -1
  52. package/dist/managers/InputManager.js +77 -26
  53. package/dist/print-cli.d.ts +2 -0
  54. package/dist/print-cli.d.ts.map +1 -1
  55. package/dist/print-cli.js +121 -23
  56. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  57. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  58. package/dist/utils/toolParameterTransforms.js +77 -0
  59. package/dist/utils/usageSummary.d.ts +6 -0
  60. package/dist/utils/usageSummary.d.ts.map +1 -1
  61. package/dist/utils/usageSummary.js +72 -0
  62. package/package.json +13 -8
  63. package/src/cli.tsx +3 -1
  64. package/src/components/App.tsx +7 -3
  65. package/src/components/BashHistorySelector.tsx +26 -3
  66. package/src/components/ChatInterface.tsx +38 -54
  67. package/src/components/CommandSelector.tsx +5 -5
  68. package/src/components/Confirmation.tsx +253 -0
  69. package/src/components/DiffDisplay.tsx +300 -0
  70. package/src/components/FileSelector.tsx +4 -6
  71. package/src/components/InputBox.tsx +58 -87
  72. package/src/components/Markdown.tsx +29 -0
  73. package/src/components/MemoryDisplay.tsx +1 -1
  74. package/src/components/MessageItem.tsx +96 -0
  75. package/src/components/MessageList.tsx +140 -202
  76. package/src/components/ReasoningDisplay.tsx +33 -0
  77. package/src/components/SubagentBlock.tsx +56 -84
  78. package/src/components/ToolResultDisplay.tsx +9 -5
  79. package/src/contexts/useChat.tsx +194 -21
  80. package/src/hooks/useInputManager.ts +40 -3
  81. package/src/index.ts +45 -5
  82. package/src/managers/InputManager.ts +101 -27
  83. package/src/print-cli.ts +143 -21
  84. package/src/utils/toolParameterTransforms.ts +104 -0
  85. package/src/utils/usageSummary.ts +109 -0
  86. package/dist/components/DiffViewer.d.ts +0 -9
  87. package/dist/components/DiffViewer.d.ts.map +0 -1
  88. package/dist/components/DiffViewer.js +0 -221
  89. package/dist/utils/fileSearch.d.ts +0 -20
  90. package/dist/utils/fileSearch.d.ts.map +0 -1
  91. package/dist/utils/fileSearch.js +0 -102
  92. package/src/components/DiffViewer.tsx +0 -321
  93. package/src/utils/fileSearch.ts +0 -133
@@ -1,5 +1,9 @@
1
1
  import { FileItem } from "../components/FileSelector.js";
2
- import { searchFiles as searchFilesUtil } from "../utils/fileSearch.js";
2
+ import {
3
+ searchFiles as searchFilesUtil,
4
+ PermissionMode,
5
+ Logger,
6
+ } from "wave-agent-sdk";
3
7
  import { readClipboardImage } from "../utils/clipboard.js";
4
8
  import type { Key } from "ink";
5
9
 
@@ -42,6 +46,8 @@ export interface InputManagerCallbacks {
42
46
  onSaveMemory?: (message: string, type: "project" | "user") => Promise<void>;
43
47
  onAbortMessage?: () => void;
44
48
  onResetHistoryNavigation?: () => void;
49
+ onPermissionModeChange?: (mode: PermissionMode) => void;
50
+ logger?: Logger;
45
51
  }
46
52
 
47
53
  export class InputManager {
@@ -93,18 +99,26 @@ export class InputManager {
93
99
  private showBashManager: boolean = false;
94
100
  private showMcpManager: boolean = false;
95
101
 
102
+ // Permission mode state
103
+ private permissionMode: PermissionMode = "default";
104
+
96
105
  // Flag to prevent handleInput conflicts when selector selection occurs
97
106
  private selectorJustUsed: boolean = false;
98
107
 
99
108
  private callbacks: InputManagerCallbacks;
109
+ private logger?: Logger;
100
110
 
101
111
  constructor(callbacks: InputManagerCallbacks = {}) {
102
112
  this.callbacks = callbacks;
113
+ this.logger = callbacks.logger;
103
114
  }
104
115
 
105
116
  // Update callbacks
106
117
  updateCallbacks(callbacks: Partial<InputManagerCallbacks>) {
107
118
  this.callbacks = { ...this.callbacks, ...callbacks };
119
+ if (callbacks.logger) {
120
+ this.logger = callbacks.logger;
121
+ }
108
122
  }
109
123
 
110
124
  // Core input methods
@@ -487,6 +501,16 @@ export class InputManager {
487
501
  return command; // Return command to execute
488
502
  }
489
503
 
504
+ handleBashHistoryExecuteAndSend(command: string): void {
505
+ const commandToExecute = this.handleBashHistoryExecute(command);
506
+ // Clear input box and execute command, ensure command starts with !
507
+ const bashCommand = commandToExecute.startsWith("!")
508
+ ? commandToExecute
509
+ : `!${commandToExecute}`;
510
+ this.clearInput();
511
+ this.callbacks.onSendMessage?.(bashCommand);
512
+ }
513
+
490
514
  checkForExclamationDeletion(cursorPosition: number): boolean {
491
515
  if (
492
516
  this.showBashHistorySelector &&
@@ -506,13 +530,18 @@ export class InputManager {
506
530
  this.callbacks.onMemoryTypeSelectorStateChange?.(true, message);
507
531
  }
508
532
 
509
- handleMemoryTypeSelect(type: "project" | "user"): void {
510
- // Note: type parameter will be used in future for different handling logic
511
- console.debug(`Memory type selected: ${type}`);
533
+ async handleMemoryTypeSelect(type: "project" | "user"): Promise<void> {
534
+ const currentMessage = this.inputText.trim();
535
+ if (currentMessage.startsWith("#")) {
536
+ await this.callbacks.onSaveMemory?.(currentMessage, type);
537
+ }
538
+ // Close the selector
512
539
  this.showMemoryTypeSelector = false;
513
540
  this.memoryMessage = "";
514
-
515
541
  this.callbacks.onMemoryTypeSelectorStateChange?.(false, "");
542
+
543
+ // Clear input box
544
+ this.clearInput();
516
545
  }
517
546
 
518
547
  handleCancelMemoryTypeSelect(): void {
@@ -631,6 +660,29 @@ export class InputManager {
631
660
  };
632
661
  }
633
662
 
663
+ // Update search queries for active selectors
664
+ private updateSearchQueriesForActiveSelectors(
665
+ inputText: string,
666
+ cursorPosition: number,
667
+ ): void {
668
+ if (this.showFileSelector && this.atPosition >= 0) {
669
+ const queryStart = this.atPosition + 1;
670
+ const queryEnd = cursorPosition;
671
+ const newQuery = inputText.substring(queryStart, queryEnd);
672
+ this.updateFileSearchQuery(newQuery);
673
+ } else if (this.showCommandSelector && this.slashPosition >= 0) {
674
+ const queryStart = this.slashPosition + 1;
675
+ const queryEnd = cursorPosition;
676
+ const newQuery = inputText.substring(queryStart, queryEnd);
677
+ this.updateCommandSearchQuery(newQuery);
678
+ } else if (this.showBashHistorySelector && this.exclamationPosition >= 0) {
679
+ const queryStart = this.exclamationPosition + 1;
680
+ const queryEnd = cursorPosition;
681
+ const newQuery = inputText.substring(queryStart, queryEnd);
682
+ this.updateBashHistorySearchQuery(newQuery);
683
+ }
684
+ }
685
+
634
686
  // Handle special character input that might trigger selectors
635
687
  handleSpecialCharInput(char: string): void {
636
688
  if (char === "@") {
@@ -644,25 +696,10 @@ export class InputManager {
644
696
  // Memory message detection will be handled in submit
645
697
  } else {
646
698
  // Update search queries for active selectors
647
- if (this.showFileSelector && this.atPosition >= 0) {
648
- const queryStart = this.atPosition + 1;
649
- const queryEnd = this.cursorPosition;
650
- const newQuery = this.inputText.substring(queryStart, queryEnd);
651
- this.updateFileSearchQuery(newQuery);
652
- } else if (this.showCommandSelector && this.slashPosition >= 0) {
653
- const queryStart = this.slashPosition + 1;
654
- const queryEnd = this.cursorPosition;
655
- const newQuery = this.inputText.substring(queryStart, queryEnd);
656
- this.updateCommandSearchQuery(newQuery);
657
- } else if (
658
- this.showBashHistorySelector &&
659
- this.exclamationPosition >= 0
660
- ) {
661
- const queryStart = this.exclamationPosition + 1;
662
- const queryEnd = this.cursorPosition;
663
- const newQuery = this.inputText.substring(queryStart, queryEnd);
664
- this.updateBashHistorySearchQuery(newQuery);
665
- }
699
+ this.updateSearchQueriesForActiveSelectors(
700
+ this.inputText,
701
+ this.cursorPosition,
702
+ );
666
703
  }
667
704
  }
668
705
 
@@ -831,6 +868,29 @@ export class InputManager {
831
868
  this.callbacks.onMcpManagerStateChange?.(show);
832
869
  }
833
870
 
871
+ // Permission mode methods
872
+ getPermissionMode(): PermissionMode {
873
+ return this.permissionMode;
874
+ }
875
+
876
+ setPermissionMode(mode: PermissionMode): void {
877
+ this.permissionMode = mode;
878
+ }
879
+
880
+ cyclePermissionMode(): void {
881
+ const modes: PermissionMode[] = ["default", "acceptEdits"];
882
+ const currentIndex = modes.indexOf(this.permissionMode);
883
+ const nextIndex =
884
+ currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
885
+ const nextMode = modes[nextIndex];
886
+ this.logger?.debug("Cycling permission mode", {
887
+ from: this.permissionMode,
888
+ to: nextMode,
889
+ });
890
+ this.permissionMode = nextMode;
891
+ this.callbacks.onPermissionModeChange?.(this.permissionMode);
892
+ }
893
+
834
894
  // Handle submit logic
835
895
  async handleSubmit(
836
896
  attachedImages: Array<{ id: number; path: string; mimeType: string }>,
@@ -884,20 +944,26 @@ export class InputManager {
884
944
 
885
945
  // Handle selector input (when any selector is active)
886
946
  handleSelectorInput(input: string, key: Key): boolean {
887
- if (key.backspace || key.delete) {
947
+ if (key.backspace || (key.delete && !this.showBashHistorySelector)) {
888
948
  if (this.cursorPosition > 0) {
889
949
  this.deleteCharAtCursor((newInput, newCursorPosition) => {
890
950
  // Check for special character deletion
891
951
  this.checkForAtDeletion(newCursorPosition);
892
952
  this.checkForSlashDeletion(newCursorPosition);
893
953
  this.checkForExclamationDeletion(newCursorPosition);
954
+
955
+ // Update search queries using the same logic as character input
956
+ this.updateSearchQueriesForActiveSelectors(
957
+ newInput,
958
+ newCursorPosition,
959
+ );
894
960
  });
895
961
  }
896
962
  return true;
897
963
  }
898
964
 
899
- // Arrow keys and Enter should be handled by selector components
900
- if (key.upArrow || key.downArrow || key.return) {
965
+ // Arrow keys, Enter and Tab should be handled by selector components
966
+ if (key.upArrow || key.downArrow || key.return || key.tab) {
901
967
  // Let selector component handle these keys, but prevent further processing
902
968
  // by returning true (indicating we've handled the input)
903
969
  return true;
@@ -909,6 +975,7 @@ export class InputManager {
909
975
  !("alt" in key && key.alt) &&
910
976
  !key.meta &&
911
977
  !key.return &&
978
+ !key.tab &&
912
979
  !key.escape &&
913
980
  !key.leftArrow &&
914
981
  !key.rightArrow &&
@@ -1058,6 +1125,13 @@ export class InputManager {
1058
1125
  return true;
1059
1126
  }
1060
1127
 
1128
+ // Handle Shift+Tab for permission mode cycling
1129
+ if (key.tab && key.shift) {
1130
+ this.logger?.debug("Shift+Tab detected, cycling permission mode");
1131
+ this.cyclePermissionMode();
1132
+ return true;
1133
+ }
1134
+
1061
1135
  // Check if any selector is active
1062
1136
  if (
1063
1137
  this.showFileSelector ||
package/src/print-cli.ts CHANGED
@@ -1,15 +1,37 @@
1
1
  import { Agent, AgentCallbacks } from "wave-agent-sdk";
2
- import { logger } from "./utils/logger.js";
3
2
  import { displayUsageSummary } from "./utils/usageSummary.js";
4
3
 
5
4
  export interface PrintCliOptions {
6
5
  restoreSessionId?: string;
7
6
  continueLastSession?: boolean;
8
7
  message?: string;
8
+ showStats?: boolean;
9
+ bypassPermissions?: boolean;
10
+ }
11
+
12
+ function displayTimingInfo(startTime: Date, showStats: boolean): void {
13
+ // Skip timing info in test environment or if stats are disabled
14
+ if (process.env.NODE_ENV === "test" || process.env.VITEST || !showStats) {
15
+ return;
16
+ }
17
+
18
+ const endTime = new Date();
19
+ const duration = endTime.getTime() - startTime.getTime();
20
+
21
+ process.stdout.write(`\n\n📅 Start time: ${startTime.toISOString()}\n`);
22
+ process.stdout.write(`📅 End time: ${endTime.toISOString()}\n`);
23
+ process.stdout.write(`⏱️ Duration: ${duration}ms\n`);
9
24
  }
10
25
 
11
26
  export async function startPrintCli(options: PrintCliOptions): Promise<void> {
12
- const { restoreSessionId, continueLastSession, message } = options;
27
+ const startTime = new Date();
28
+ const {
29
+ restoreSessionId,
30
+ continueLastSession,
31
+ message,
32
+ showStats = false,
33
+ bypassPermissions,
34
+ } = options;
13
35
 
14
36
  if (
15
37
  (!message || message.trim() === "") &&
@@ -23,14 +45,102 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
23
45
  }
24
46
 
25
47
  let agent: Agent;
48
+ let isReasoning = false;
49
+ let isContent = false;
50
+ const subagentReasoningStates = new Map<string, boolean>();
51
+ const subagentContentStates = new Map<string, boolean>();
26
52
 
27
53
  // Setup callbacks for agent
28
54
  const callbacks: AgentCallbacks = {
29
- onAssistantMessageAdded: (content?: string) => {
30
- // Only output the content field, not tool calls
31
- if (content) {
32
- console.log(content);
55
+ onAssistantMessageAdded: () => {
56
+ isReasoning = false;
57
+ isContent = false;
58
+ // Assistant message started - no content to output yet
59
+ process.stdout.write("\n");
60
+ },
61
+ onAssistantReasoningUpdated: (chunk: string) => {
62
+ if (!isReasoning) {
63
+ process.stdout.write("💭 Reasoning:\n");
64
+ isReasoning = true;
65
+ }
66
+ process.stdout.write(chunk);
67
+ },
68
+ onAssistantContentUpdated: (chunk: string) => {
69
+ if (isReasoning && !isContent) {
70
+ process.stdout.write("\n\n📝 Response:\n");
71
+ isContent = true;
72
+ }
73
+ // FR-001: Stream content updates for real-time display - output only the new chunk
74
+ process.stdout.write(chunk);
75
+ },
76
+
77
+ // Tool block callback - display tool name when tool starts
78
+ onToolBlockUpdated: (params) => {
79
+ // Print tool name only during 'running' stage (happens once per tool call)
80
+ if (params.stage === "running" && params.name) {
81
+ process.stdout.write(`\n🔧 ${params.name}`);
82
+ if (params.compactParams) {
83
+ process.stdout.write(` ${params.compactParams}`);
84
+ }
85
+ process.stdout.write(`\n`);
86
+ }
87
+ },
88
+
89
+ // Subagent block callbacks
90
+ onSubAgentBlockAdded: (subagentId: string, parameters) => {
91
+ // Display subagent creation with indentation
92
+ process.stdout.write(
93
+ `\n🤖 Subagent [${parameters.subagent_type}]: ${parameters.description}\n`,
94
+ );
95
+ },
96
+ onSubAgentBlockUpdated: (subagentId: string, status) => {
97
+ // Display subagent status updates
98
+ const statusIconMap = {
99
+ active: "🔄",
100
+ completed: "✅",
101
+ error: "❌",
102
+ aborted: "⚠️",
103
+ } as const;
104
+
105
+ const statusIcon = statusIconMap[status] ?? "🔄";
106
+ process.stdout.write(` ${statusIcon} Subagent status: ${status}\n`);
107
+ },
108
+ // Subagent message callbacks
109
+ onSubagentAssistantMessageAdded: (subagentId: string) => {
110
+ subagentReasoningStates.set(subagentId, false);
111
+ subagentContentStates.set(subagentId, false);
112
+ // Subagent assistant message started - add indentation
113
+ process.stdout.write("\n ");
114
+ },
115
+ onSubagentAssistantReasoningUpdated: (
116
+ subagentId: string,
117
+ chunk: string,
118
+ ) => {
119
+ if (!subagentReasoningStates.get(subagentId)) {
120
+ process.stdout.write("💭 Reasoning: ");
121
+ subagentReasoningStates.set(subagentId, true);
122
+ }
123
+ process.stdout.write(chunk);
124
+ },
125
+ onSubagentAssistantContentUpdated: (subagentId: string, chunk: string) => {
126
+ if (
127
+ subagentReasoningStates.get(subagentId) &&
128
+ !subagentContentStates.get(subagentId)
129
+ ) {
130
+ process.stdout.write("\n 📝 Response: ");
131
+ subagentContentStates.set(subagentId, true);
33
132
  }
133
+ // Stream subagent content with indentation - output only the new chunk
134
+ process.stdout.write(chunk);
135
+ },
136
+ onSubagentUserMessageAdded: (_subagentId: string, params) => {
137
+ // Display subagent user messages with indentation
138
+ process.stdout.write(`\n 👤 User: ${params.content}\n`);
139
+ },
140
+ // Error block callback
141
+ onErrorBlockAdded: (error: string) => {
142
+ // Display error blocks with distinct formatting
143
+ process.stdout.write(`\n❌ Error: ${error}\n`);
34
144
  },
35
145
  };
36
146
 
@@ -40,7 +150,8 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
40
150
  callbacks,
41
151
  restoreSessionId,
42
152
  continueLastSession,
43
- logger,
153
+ permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
154
+ // 保持流式模式以获得更好的命令行用户体验
44
155
  });
45
156
 
46
157
  // Send message if provided and not empty
@@ -49,29 +160,40 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
49
160
  }
50
161
 
51
162
  // Display usage summary before exit
52
- try {
53
- const usages = agent.usages;
54
- const sessionFilePath = agent.sessionFilePath;
55
- displayUsageSummary(usages, sessionFilePath);
56
- } catch {
57
- // Silently ignore usage summary errors
163
+ if (showStats) {
164
+ try {
165
+ const usages = agent.usages;
166
+ const sessionFilePath = agent.sessionFilePath;
167
+ displayUsageSummary(usages, sessionFilePath);
168
+ } catch {
169
+ // Silently ignore usage summary errors
170
+ }
58
171
  }
59
172
 
173
+ // Display timing information
174
+ displayTimingInfo(startTime, showStats);
175
+
60
176
  // Destroy agent and exit after sendMessage completes
61
- agent.destroy();
177
+ await agent.destroy();
62
178
  process.exit(0);
63
179
  } catch (error) {
64
180
  console.error("Failed to send message:", error);
65
181
  if (agent!) {
66
182
  // Display usage summary even on error
67
- try {
68
- const usages = agent.usages;
69
- const sessionFilePath = agent.sessionFilePath;
70
- displayUsageSummary(usages, sessionFilePath);
71
- } catch {
72
- // Silently ignore usage summary errors
183
+ if (showStats) {
184
+ try {
185
+ const usages = agent.usages;
186
+ const sessionFilePath = agent.sessionFilePath;
187
+ displayUsageSummary(usages, sessionFilePath);
188
+ } catch {
189
+ // Silently ignore usage summary errors
190
+ }
73
191
  }
74
- agent.destroy();
192
+
193
+ // Display timing information even on error
194
+ displayTimingInfo(startTime, showStats);
195
+
196
+ await agent.destroy();
75
197
  }
76
198
  process.exit(1);
77
199
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Tool parameter transformation utilities for UI rendering
3
+ * Forces type judgment based on tool name using type assertions
4
+ */
5
+
6
+ import {
7
+ type Change,
8
+ type WriteToolParameters,
9
+ type EditToolParameters,
10
+ type MultiEditToolParameters,
11
+ } from "wave-agent-sdk";
12
+ import { logger } from "./logger.js";
13
+
14
+ /**
15
+ * Parse tool block parameters
16
+ */
17
+ function parseToolParameters(parameters: string): unknown {
18
+ if (!parameters) {
19
+ return {};
20
+ }
21
+
22
+ try {
23
+ return JSON.parse(parameters);
24
+ } catch (error) {
25
+ logger.warn("Failed to parse tool parameters:", error);
26
+ return {};
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Transform Write tool parameters to changes
32
+ */
33
+ export function transformWriteParameters(
34
+ parameters: WriteToolParameters,
35
+ ): Change[] {
36
+ return [
37
+ {
38
+ oldContent: "", // No previous content for write operations
39
+ newContent: parameters.content,
40
+ },
41
+ ];
42
+ }
43
+
44
+ /**
45
+ * Transform Edit tool parameters to changes
46
+ */
47
+ export function transformEditParameters(
48
+ parameters: EditToolParameters,
49
+ ): Change[] {
50
+ return [
51
+ {
52
+ oldContent: parameters.old_string,
53
+ newContent: parameters.new_string,
54
+ },
55
+ ];
56
+ }
57
+
58
+ /**
59
+ * Transform MultiEdit tool parameters to changes
60
+ */
61
+ export function transformMultiEditParameters(
62
+ parameters: MultiEditToolParameters,
63
+ ): Change[] {
64
+ return parameters.edits.map((edit) => ({
65
+ oldContent: edit.old_string,
66
+ newContent: edit.new_string,
67
+ }));
68
+ }
69
+
70
+ /**
71
+ * Transform tool block parameters into standardized Change[] array for diff display
72
+ * Forces type judgment based on tool name using type assertions
73
+ */
74
+ export function transformToolBlockToChanges(
75
+ toolName: string,
76
+ parameters: string,
77
+ ): Change[] {
78
+ try {
79
+ if (!toolName) {
80
+ return [];
81
+ }
82
+
83
+ const parsedParams = parseToolParameters(parameters);
84
+
85
+ switch (toolName) {
86
+ case "Write":
87
+ return transformWriteParameters(parsedParams as WriteToolParameters);
88
+
89
+ case "Edit":
90
+ return transformEditParameters(parsedParams as EditToolParameters);
91
+
92
+ case "MultiEdit":
93
+ return transformMultiEditParameters(
94
+ parsedParams as MultiEditToolParameters,
95
+ );
96
+
97
+ default:
98
+ return [];
99
+ }
100
+ } catch (error) {
101
+ logger.warn("Failed to transform tool block to changes:", error);
102
+ return [];
103
+ }
104
+ }
@@ -12,6 +12,13 @@ export interface TokenSummary {
12
12
  agent_calls: number;
13
13
  compressions: number;
14
14
  };
15
+ // Cache-related tokens (for Claude models)
16
+ cache_read_input_tokens?: number;
17
+ cache_creation_input_tokens?: number;
18
+ cache_creation?: {
19
+ ephemeral_5m_input_tokens: number;
20
+ ephemeral_1h_input_tokens: number;
21
+ };
15
22
  }
16
23
 
17
24
  /**
@@ -45,6 +52,36 @@ export function calculateTokenSummary(
45
52
  summary.completion_tokens += usage.completion_tokens;
46
53
  summary.total_tokens += usage.total_tokens;
47
54
 
55
+ // Handle cache tokens if present and non-zero
56
+ if (usage.cache_read_input_tokens && usage.cache_read_input_tokens > 0) {
57
+ summary.cache_read_input_tokens =
58
+ (summary.cache_read_input_tokens || 0) + usage.cache_read_input_tokens;
59
+ }
60
+ if (
61
+ usage.cache_creation_input_tokens &&
62
+ usage.cache_creation_input_tokens > 0
63
+ ) {
64
+ summary.cache_creation_input_tokens =
65
+ (summary.cache_creation_input_tokens || 0) +
66
+ usage.cache_creation_input_tokens;
67
+ }
68
+ if (
69
+ usage.cache_creation &&
70
+ (usage.cache_creation.ephemeral_5m_input_tokens > 0 ||
71
+ usage.cache_creation.ephemeral_1h_input_tokens > 0)
72
+ ) {
73
+ if (!summary.cache_creation) {
74
+ summary.cache_creation = {
75
+ ephemeral_5m_input_tokens: 0,
76
+ ephemeral_1h_input_tokens: 0,
77
+ };
78
+ }
79
+ summary.cache_creation.ephemeral_5m_input_tokens +=
80
+ usage.cache_creation.ephemeral_5m_input_tokens || 0;
81
+ summary.cache_creation.ephemeral_1h_input_tokens +=
82
+ usage.cache_creation.ephemeral_1h_input_tokens || 0;
83
+ }
84
+
48
85
  // Track operation types
49
86
  if (usage.operation_type === "agent") {
50
87
  summary.operations.agent_calls += 1;
@@ -93,6 +130,11 @@ export function displayUsageSummary(
93
130
  let totalTokens = 0;
94
131
  let totalAgentCalls = 0;
95
132
  let totalCompressions = 0;
133
+ let totalCacheRead = 0;
134
+ let totalCacheCreation = 0;
135
+ let totalCache5m = 0;
136
+ let totalCache1h = 0;
137
+ let hasCacheData = false;
96
138
 
97
139
  for (const [, summary] of Object.entries(summaries)) {
98
140
  console.log(`Model: ${summary.model}`);
@@ -101,6 +143,52 @@ export function displayUsageSummary(
101
143
  ` Completion tokens: ${summary.completion_tokens.toLocaleString()}`,
102
144
  );
103
145
  console.log(` Total tokens: ${summary.total_tokens.toLocaleString()}`);
146
+
147
+ // Display cache information if available
148
+ if (
149
+ summary.cache_read_input_tokens ||
150
+ summary.cache_creation_input_tokens ||
151
+ summary.cache_creation
152
+ ) {
153
+ hasCacheData = true;
154
+ console.log(" Cache Usage:");
155
+
156
+ if (
157
+ summary.cache_read_input_tokens &&
158
+ summary.cache_read_input_tokens > 0
159
+ ) {
160
+ console.log(
161
+ ` Read from cache: ${summary.cache_read_input_tokens.toLocaleString()} tokens`,
162
+ );
163
+ totalCacheRead += summary.cache_read_input_tokens;
164
+ }
165
+
166
+ if (
167
+ summary.cache_creation_input_tokens &&
168
+ summary.cache_creation_input_tokens > 0
169
+ ) {
170
+ console.log(
171
+ ` Created cache: ${summary.cache_creation_input_tokens.toLocaleString()} tokens`,
172
+ );
173
+ totalCacheCreation += summary.cache_creation_input_tokens;
174
+ }
175
+
176
+ if (summary.cache_creation) {
177
+ if (summary.cache_creation.ephemeral_5m_input_tokens > 0) {
178
+ console.log(
179
+ ` 5m cache: ${summary.cache_creation.ephemeral_5m_input_tokens.toLocaleString()} tokens`,
180
+ );
181
+ totalCache5m += summary.cache_creation.ephemeral_5m_input_tokens;
182
+ }
183
+ if (summary.cache_creation.ephemeral_1h_input_tokens > 0) {
184
+ console.log(
185
+ ` 1h cache: ${summary.cache_creation.ephemeral_1h_input_tokens.toLocaleString()} tokens`,
186
+ );
187
+ totalCache1h += summary.cache_creation.ephemeral_1h_input_tokens;
188
+ }
189
+ }
190
+ }
191
+
104
192
  console.log(
105
193
  ` Operations: ${summary.operations.agent_calls} agent calls, ${summary.operations.compressions} compressions`,
106
194
  );
@@ -118,6 +206,27 @@ export function displayUsageSummary(
118
206
  console.log(` Prompt tokens: ${totalPrompt.toLocaleString()}`);
119
207
  console.log(` Completion tokens: ${totalCompletion.toLocaleString()}`);
120
208
  console.log(` Total tokens: ${totalTokens.toLocaleString()}`);
209
+
210
+ if (hasCacheData) {
211
+ console.log(" Cache Usage:");
212
+ if (totalCacheRead > 0) {
213
+ console.log(
214
+ ` Read from cache: ${totalCacheRead.toLocaleString()} tokens`,
215
+ );
216
+ }
217
+ if (totalCacheCreation > 0) {
218
+ console.log(
219
+ ` Created cache: ${totalCacheCreation.toLocaleString()} tokens`,
220
+ );
221
+ }
222
+ if (totalCache5m > 0) {
223
+ console.log(` 5m cache: ${totalCache5m.toLocaleString()} tokens`);
224
+ }
225
+ if (totalCache1h > 0) {
226
+ console.log(` 1h cache: ${totalCache1h.toLocaleString()} tokens`);
227
+ }
228
+ }
229
+
121
230
  console.log(
122
231
  ` Operations: ${totalAgentCalls} agent calls, ${totalCompressions} compressions`,
123
232
  );
@@ -1,9 +0,0 @@
1
- import React from "react";
2
- import type { DiffBlock } from "wave-agent-sdk";
3
- interface DiffViewerProps {
4
- block: DiffBlock;
5
- isExpanded?: boolean;
6
- }
7
- export declare const DiffViewer: React.FC<DiffViewerProps>;
8
- export {};
9
- //# sourceMappingURL=DiffViewer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DiffViewer.d.ts","sourceRoot":"","sources":["../../src/components/DiffViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAwCD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgRhD,CAAC"}