wave-code 0.0.5 → 0.0.6

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 (57) hide show
  1. package/README.md +2 -2
  2. package/dist/components/ChatInterface.d.ts.map +1 -1
  3. package/dist/components/ChatInterface.js +4 -24
  4. package/dist/components/CommandSelector.js +4 -4
  5. package/dist/components/DiffViewer.d.ts +1 -1
  6. package/dist/components/DiffViewer.d.ts.map +1 -1
  7. package/dist/components/DiffViewer.js +15 -15
  8. package/dist/components/FileSelector.js +2 -2
  9. package/dist/components/InputBox.d.ts.map +1 -1
  10. package/dist/components/InputBox.js +21 -50
  11. package/dist/components/Markdown.d.ts +6 -0
  12. package/dist/components/Markdown.d.ts.map +1 -0
  13. package/dist/components/Markdown.js +22 -0
  14. package/dist/components/MessageItem.d.ts +9 -0
  15. package/dist/components/MessageItem.d.ts.map +1 -0
  16. package/dist/components/MessageItem.js +15 -0
  17. package/dist/components/MessageList.d.ts +1 -1
  18. package/dist/components/MessageList.d.ts.map +1 -1
  19. package/dist/components/MessageList.js +33 -33
  20. package/dist/components/SubagentBlock.d.ts +0 -1
  21. package/dist/components/SubagentBlock.d.ts.map +1 -1
  22. package/dist/components/SubagentBlock.js +29 -30
  23. package/dist/components/ToolResultDisplay.js +5 -5
  24. package/dist/contexts/useChat.d.ts +2 -2
  25. package/dist/contexts/useChat.d.ts.map +1 -1
  26. package/dist/contexts/useChat.js +18 -9
  27. package/dist/hooks/useInputManager.d.ts +3 -1
  28. package/dist/hooks/useInputManager.d.ts.map +1 -1
  29. package/dist/hooks/useInputManager.js +15 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +9 -2
  32. package/dist/managers/InputManager.d.ts +3 -1
  33. package/dist/managers/InputManager.d.ts.map +1 -1
  34. package/dist/managers/InputManager.js +44 -24
  35. package/dist/print-cli.d.ts +1 -0
  36. package/dist/print-cli.d.ts.map +1 -1
  37. package/dist/print-cli.js +88 -23
  38. package/dist/utils/usageSummary.d.ts +6 -0
  39. package/dist/utils/usageSummary.d.ts.map +1 -1
  40. package/dist/utils/usageSummary.js +72 -0
  41. package/package.json +10 -6
  42. package/src/components/ChatInterface.tsx +13 -43
  43. package/src/components/CommandSelector.tsx +5 -5
  44. package/src/components/DiffViewer.tsx +18 -16
  45. package/src/components/FileSelector.tsx +2 -2
  46. package/src/components/InputBox.tsx +22 -74
  47. package/src/components/Markdown.tsx +29 -0
  48. package/src/components/MessageItem.tsx +104 -0
  49. package/src/components/MessageList.tsx +142 -202
  50. package/src/components/SubagentBlock.tsx +56 -84
  51. package/src/components/ToolResultDisplay.tsx +5 -5
  52. package/src/contexts/useChat.tsx +22 -13
  53. package/src/hooks/useInputManager.ts +21 -3
  54. package/src/index.ts +12 -2
  55. package/src/managers/InputManager.ts +55 -25
  56. package/src/print-cli.ts +103 -21
  57. package/src/utils/usageSummary.ts +109 -0
@@ -11,6 +11,7 @@ export const useInputManager = (
11
11
  callbacks: Partial<InputManagerCallbacks> = {},
12
12
  ) => {
13
13
  const managerRef = useRef<InputManager | null>(null);
14
+ const [isManagerReady, setIsManagerReady] = useState(false);
14
15
 
15
16
  // React state that mirrors InputManager state
16
17
  const [inputText, setInputText] = useState("");
@@ -65,10 +66,13 @@ export const useInputManager = (
65
66
  setShowMcpManager(show);
66
67
  },
67
68
  onImagesStateChange: setAttachedImages,
69
+ onShowBashManager: () => setShowBashManager(true),
70
+ onShowMcpManager: () => setShowMcpManager(true),
68
71
  ...callbacks,
69
72
  });
70
73
 
71
74
  managerRef.current = manager;
75
+ setIsManagerReady(true);
72
76
  } else {
73
77
  // Update callbacks on existing manager
74
78
  managerRef.current.updateCallbacks({
@@ -93,6 +97,8 @@ export const useInputManager = (
93
97
  setShowMcpManager(show);
94
98
  },
95
99
  onImagesStateChange: setAttachedImages,
100
+ onShowBashManager: () => setShowBashManager(true),
101
+ onShowMcpManager: () => setShowMcpManager(true),
96
102
  ...callbacks,
97
103
  });
98
104
  }
@@ -253,9 +259,12 @@ export const useInputManager = (
253
259
  managerRef.current?.activateMemoryTypeSelector(message);
254
260
  }, []);
255
261
 
256
- const handleMemoryTypeSelect = useCallback((type: "project" | "user") => {
257
- managerRef.current?.handleMemoryTypeSelect(type);
258
- }, []);
262
+ const handleMemoryTypeSelect = useCallback(
263
+ async (type: "project" | "user") => {
264
+ await managerRef.current?.handleMemoryTypeSelect(type);
265
+ },
266
+ [],
267
+ );
259
268
 
260
269
  const handleCancelMemoryTypeSelect = useCallback(() => {
261
270
  managerRef.current?.handleCancelMemoryTypeSelect();
@@ -296,6 +305,11 @@ export const useInputManager = (
296
305
  managerRef.current?.setCursorPosition(position);
297
306
  }, []);
298
307
 
308
+ // Complex handlers that combine multiple operations
309
+ const handleBashHistoryExecuteAndSend = useCallback((command: string) => {
310
+ managerRef.current?.handleBashHistoryExecuteAndSend(command);
311
+ }, []);
312
+
299
313
  return {
300
314
  // State
301
315
  inputText,
@@ -315,6 +329,7 @@ export const useInputManager = (
315
329
  showBashManager,
316
330
  showMcpManager,
317
331
  attachedImages,
332
+ isManagerReady,
318
333
 
319
334
  // Methods
320
335
  insertTextAtCursor,
@@ -409,6 +424,9 @@ export const useInputManager = (
409
424
  managerRef.current?.clearLongTextMap();
410
425
  }, []),
411
426
 
427
+ // Complex handlers combining multiple operations
428
+ handleBashHistoryExecuteAndSend,
429
+
412
430
  // Main input handler
413
431
  handleInput: useCallback(
414
432
  async (
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import yargs from "yargs";
2
2
  import { hideBin } from "yargs/helpers";
3
3
  import { startCli } from "./cli.js";
4
- import { listSessions } from "wave-agent-sdk";
4
+ import { listSessions, getSessionFilePath } from "wave-agent-sdk";
5
5
 
6
6
  // Export main function for external use
7
7
  export async function main() {
@@ -21,6 +21,10 @@ export async function main() {
21
21
  description: "Print response without interactive mode",
22
22
  type: "string",
23
23
  })
24
+ .option("show-stats", {
25
+ description: "Show timing and usage statistics in print mode",
26
+ type: "boolean",
27
+ })
24
28
  .option("list-sessions", {
25
29
  description: "List all available sessions",
26
30
  type: "boolean",
@@ -31,7 +35,10 @@ export async function main() {
31
35
  .example("$0 --restore session_123", "Restore specific session")
32
36
  .example("$0 --continue", "Continue from last session")
33
37
  .example("$0 --print 'Hello'", "Send message in print mode")
34
- .example("$0 -p 'Hello'", "Send message in print mode (short)")
38
+ .example(
39
+ "$0 -p 'Hello' --show-stats",
40
+ "Send message in print mode with statistics",
41
+ )
35
42
  .example("$0 --list-sessions", "List all available sessions")
36
43
  .help("h")
37
44
  .parseAsync();
@@ -53,9 +60,11 @@ export async function main() {
53
60
  for (const session of sessions) {
54
61
  const startedAt = new Date(session.startedAt).toLocaleString();
55
62
  const lastActiveAt = new Date(session.lastActiveAt).toLocaleString();
63
+ const filePath = await getSessionFilePath(session.id, session.workdir);
56
64
 
57
65
  console.log(`ID: ${session.id}`);
58
66
  console.log(` Workdir: ${session.workdir}`);
67
+ console.log(` File Path: ${filePath}`);
59
68
  console.log(` Started: ${startedAt}`);
60
69
  console.log(` Last Active: ${lastActiveAt}`);
61
70
  console.log(` Last Message Tokens: ${session.latestTotalTokens}`);
@@ -76,6 +85,7 @@ export async function main() {
76
85
  restoreSessionId: argv.restore,
77
86
  continueLastSession: argv.continue,
78
87
  message: argv.print,
88
+ showStats: argv.showStats,
79
89
  });
80
90
  }
81
91
 
@@ -487,6 +487,16 @@ export class InputManager {
487
487
  return command; // Return command to execute
488
488
  }
489
489
 
490
+ handleBashHistoryExecuteAndSend(command: string): void {
491
+ const commandToExecute = this.handleBashHistoryExecute(command);
492
+ // Clear input box and execute command, ensure command starts with !
493
+ const bashCommand = commandToExecute.startsWith("!")
494
+ ? commandToExecute
495
+ : `!${commandToExecute}`;
496
+ this.clearInput();
497
+ this.callbacks.onSendMessage?.(bashCommand);
498
+ }
499
+
490
500
  checkForExclamationDeletion(cursorPosition: number): boolean {
491
501
  if (
492
502
  this.showBashHistorySelector &&
@@ -506,13 +516,18 @@ export class InputManager {
506
516
  this.callbacks.onMemoryTypeSelectorStateChange?.(true, message);
507
517
  }
508
518
 
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}`);
519
+ async handleMemoryTypeSelect(type: "project" | "user"): Promise<void> {
520
+ const currentMessage = this.inputText.trim();
521
+ if (currentMessage.startsWith("#")) {
522
+ await this.callbacks.onSaveMemory?.(currentMessage, type);
523
+ }
524
+ // Close the selector
512
525
  this.showMemoryTypeSelector = false;
513
526
  this.memoryMessage = "";
514
-
515
527
  this.callbacks.onMemoryTypeSelectorStateChange?.(false, "");
528
+
529
+ // Clear input box
530
+ this.clearInput();
516
531
  }
517
532
 
518
533
  handleCancelMemoryTypeSelect(): void {
@@ -631,6 +646,29 @@ export class InputManager {
631
646
  };
632
647
  }
633
648
 
649
+ // Update search queries for active selectors
650
+ private updateSearchQueriesForActiveSelectors(
651
+ inputText: string,
652
+ cursorPosition: number,
653
+ ): void {
654
+ if (this.showFileSelector && this.atPosition >= 0) {
655
+ const queryStart = this.atPosition + 1;
656
+ const queryEnd = cursorPosition;
657
+ const newQuery = inputText.substring(queryStart, queryEnd);
658
+ this.updateFileSearchQuery(newQuery);
659
+ } else if (this.showCommandSelector && this.slashPosition >= 0) {
660
+ const queryStart = this.slashPosition + 1;
661
+ const queryEnd = cursorPosition;
662
+ const newQuery = inputText.substring(queryStart, queryEnd);
663
+ this.updateCommandSearchQuery(newQuery);
664
+ } else if (this.showBashHistorySelector && this.exclamationPosition >= 0) {
665
+ const queryStart = this.exclamationPosition + 1;
666
+ const queryEnd = cursorPosition;
667
+ const newQuery = inputText.substring(queryStart, queryEnd);
668
+ this.updateBashHistorySearchQuery(newQuery);
669
+ }
670
+ }
671
+
634
672
  // Handle special character input that might trigger selectors
635
673
  handleSpecialCharInput(char: string): void {
636
674
  if (char === "@") {
@@ -644,25 +682,10 @@ export class InputManager {
644
682
  // Memory message detection will be handled in submit
645
683
  } else {
646
684
  // 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
- }
685
+ this.updateSearchQueriesForActiveSelectors(
686
+ this.inputText,
687
+ this.cursorPosition,
688
+ );
666
689
  }
667
690
  }
668
691
 
@@ -891,13 +914,19 @@ export class InputManager {
891
914
  this.checkForAtDeletion(newCursorPosition);
892
915
  this.checkForSlashDeletion(newCursorPosition);
893
916
  this.checkForExclamationDeletion(newCursorPosition);
917
+
918
+ // Update search queries using the same logic as character input
919
+ this.updateSearchQueriesForActiveSelectors(
920
+ newInput,
921
+ newCursorPosition,
922
+ );
894
923
  });
895
924
  }
896
925
  return true;
897
926
  }
898
927
 
899
- // Arrow keys and Enter should be handled by selector components
900
- if (key.upArrow || key.downArrow || key.return) {
928
+ // Arrow keys, Enter and Tab should be handled by selector components
929
+ if (key.upArrow || key.downArrow || key.return || key.tab) {
901
930
  // Let selector component handle these keys, but prevent further processing
902
931
  // by returning true (indicating we've handled the input)
903
932
  return true;
@@ -909,6 +938,7 @@ export class InputManager {
909
938
  !("alt" in key && key.alt) &&
910
939
  !key.meta &&
911
940
  !key.return &&
941
+ !key.tab &&
912
942
  !key.escape &&
913
943
  !key.leftArrow &&
914
944
  !key.rightArrow &&
package/src/print-cli.ts CHANGED
@@ -1,15 +1,35 @@
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
+ }
10
+
11
+ function displayTimingInfo(startTime: Date, showStats: boolean): void {
12
+ // Skip timing info in test environment or if stats are disabled
13
+ if (process.env.NODE_ENV === "test" || process.env.VITEST || !showStats) {
14
+ return;
15
+ }
16
+
17
+ const endTime = new Date();
18
+ const duration = endTime.getTime() - startTime.getTime();
19
+
20
+ process.stdout.write(`\n\n📅 Start time: ${startTime.toISOString()}\n`);
21
+ process.stdout.write(`📅 End time: ${endTime.toISOString()}\n`);
22
+ process.stdout.write(`⏱️ Duration: ${duration}ms\n`);
9
23
  }
10
24
 
11
25
  export async function startPrintCli(options: PrintCliOptions): Promise<void> {
12
- const { restoreSessionId, continueLastSession, message } = options;
26
+ const startTime = new Date();
27
+ const {
28
+ restoreSessionId,
29
+ continueLastSession,
30
+ message,
31
+ showStats = false,
32
+ } = options;
13
33
 
14
34
  if (
15
35
  (!message || message.trim() === "") &&
@@ -26,12 +46,64 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
26
46
 
27
47
  // Setup callbacks for agent
28
48
  const callbacks: AgentCallbacks = {
29
- onAssistantMessageAdded: (content?: string) => {
30
- // Only output the content field, not tool calls
31
- if (content) {
32
- console.log(content);
49
+ onAssistantMessageAdded: () => {
50
+ // Assistant message started - no content to output yet
51
+ process.stdout.write("\n");
52
+ },
53
+ onAssistantContentUpdated: (chunk: string) => {
54
+ // FR-001: Stream content updates for real-time display - output only the new chunk
55
+ process.stdout.write(chunk);
56
+ },
57
+
58
+ // Tool block callback - display tool name when tool starts
59
+ onToolBlockUpdated: (params) => {
60
+ // Print tool name only during 'running' stage (happens once per tool call)
61
+ if (params.stage === "running" && params.name) {
62
+ process.stdout.write(`\n🔧 ${params.name}`);
63
+ if (params.compactParams) {
64
+ process.stdout.write(` ${params.compactParams}`);
65
+ }
66
+ process.stdout.write(`\n`);
33
67
  }
34
68
  },
69
+
70
+ // Subagent block callbacks
71
+ onSubAgentBlockAdded: (subagentId: string, parameters) => {
72
+ // Display subagent creation with indentation
73
+ process.stdout.write(
74
+ `\n🤖 Subagent [${parameters.subagent_type}]: ${parameters.description}\n`,
75
+ );
76
+ },
77
+ onSubAgentBlockUpdated: (subagentId: string, status) => {
78
+ // Display subagent status updates
79
+ const statusIconMap = {
80
+ active: "🔄",
81
+ completed: "✅",
82
+ error: "❌",
83
+ aborted: "⚠️",
84
+ } as const;
85
+
86
+ const statusIcon = statusIconMap[status] ?? "🔄";
87
+ process.stdout.write(` ${statusIcon} Subagent status: ${status}\n`);
88
+ },
89
+ // Subagent message callbacks
90
+ onSubagentAssistantMessageAdded: () => {
91
+ // Subagent assistant message started - add indentation
92
+ process.stdout.write("\n ");
93
+ },
94
+ onSubagentAssistantContentUpdated: (_subagentId: string, chunk: string) => {
95
+ // Stream subagent content with indentation - output only the new chunk
96
+ process.stdout.write(chunk);
97
+ },
98
+ onSubagentUserMessageAdded: (_subagentId: string, params) => {
99
+ // Display subagent user messages with indentation
100
+ process.stdout.write(`\n 👤 User: ${params.content}\n`);
101
+ },
102
+ // Error block callback
103
+ onErrorBlockAdded: (error: string) => {
104
+ // Display error blocks with distinct formatting
105
+ process.stdout.write(`\n❌ Error: ${error}\n`);
106
+ },
35
107
  };
36
108
 
37
109
  try {
@@ -40,7 +112,6 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
40
112
  callbacks,
41
113
  restoreSessionId,
42
114
  continueLastSession,
43
- logger,
44
115
  });
45
116
 
46
117
  // Send message if provided and not empty
@@ -49,29 +120,40 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
49
120
  }
50
121
 
51
122
  // 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
123
+ if (showStats) {
124
+ try {
125
+ const usages = agent.usages;
126
+ const sessionFilePath = agent.sessionFilePath;
127
+ displayUsageSummary(usages, sessionFilePath);
128
+ } catch {
129
+ // Silently ignore usage summary errors
130
+ }
58
131
  }
59
132
 
133
+ // Display timing information
134
+ displayTimingInfo(startTime, showStats);
135
+
60
136
  // Destroy agent and exit after sendMessage completes
61
- agent.destroy();
137
+ await agent.destroy();
62
138
  process.exit(0);
63
139
  } catch (error) {
64
140
  console.error("Failed to send message:", error);
65
141
  if (agent!) {
66
142
  // 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
143
+ if (showStats) {
144
+ try {
145
+ const usages = agent.usages;
146
+ const sessionFilePath = agent.sessionFilePath;
147
+ displayUsageSummary(usages, sessionFilePath);
148
+ } catch {
149
+ // Silently ignore usage summary errors
150
+ }
73
151
  }
74
- agent.destroy();
152
+
153
+ // Display timing information even on error
154
+ displayTimingInfo(startTime, showStats);
155
+
156
+ await agent.destroy();
75
157
  }
76
158
  process.exit(1);
77
159
  }
@@ -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
  );