@shareai-lab/kode-sdk 2.7.1 → 2.7.2

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 (97) hide show
  1. package/dist/core/agent/breakpoint-manager.js +36 -0
  2. package/dist/core/agent/message-queue.js +57 -0
  3. package/dist/core/agent/permission-manager.js +32 -0
  4. package/dist/core/agent/todo-manager.js +91 -0
  5. package/dist/core/agent/tool-runner.js +45 -0
  6. package/dist/core/agent.js +2035 -0
  7. package/dist/core/config.js +2 -0
  8. package/dist/core/context-manager.js +241 -0
  9. package/dist/core/errors.js +49 -0
  10. package/dist/core/events.js +329 -0
  11. package/dist/core/file-pool.d.ts +2 -0
  12. package/dist/core/file-pool.js +125 -0
  13. package/dist/core/hooks.js +71 -0
  14. package/dist/core/permission-modes.js +61 -0
  15. package/dist/core/pool.js +301 -0
  16. package/dist/core/room.js +57 -0
  17. package/dist/core/scheduler.js +58 -0
  18. package/dist/core/skills/index.js +20 -0
  19. package/dist/core/skills/management-manager.js +557 -0
  20. package/dist/core/skills/manager.js +243 -0
  21. package/dist/core/skills/operation-queue.js +113 -0
  22. package/dist/core/skills/sandbox-file-manager.js +183 -0
  23. package/dist/core/skills/types.js +9 -0
  24. package/dist/core/skills/xml-generator.js +70 -0
  25. package/dist/core/template.js +35 -0
  26. package/dist/core/time-bridge.js +100 -0
  27. package/dist/core/todo.js +89 -0
  28. package/dist/core/types.js +3 -0
  29. package/dist/index.js +148 -60461
  30. package/dist/infra/db/postgres/postgres-store.js +1073 -0
  31. package/dist/infra/db/sqlite/sqlite-store.js +800 -0
  32. package/dist/infra/e2b/e2b-fs.js +128 -0
  33. package/dist/infra/e2b/e2b-sandbox.js +156 -0
  34. package/dist/infra/e2b/e2b-template.js +105 -0
  35. package/dist/infra/e2b/index.js +9 -0
  36. package/dist/infra/e2b/types.js +2 -0
  37. package/dist/infra/provider.js +67 -0
  38. package/dist/infra/providers/anthropic.js +308 -0
  39. package/dist/infra/providers/core/errors.js +353 -0
  40. package/dist/infra/providers/core/fork.js +418 -0
  41. package/dist/infra/providers/core/index.js +76 -0
  42. package/dist/infra/providers/core/logger.js +191 -0
  43. package/dist/infra/providers/core/retry.js +189 -0
  44. package/dist/infra/providers/core/usage.js +376 -0
  45. package/dist/infra/providers/gemini.js +493 -0
  46. package/dist/infra/providers/index.js +83 -0
  47. package/dist/infra/providers/openai.js +662 -0
  48. package/dist/infra/providers/types.js +20 -0
  49. package/dist/infra/providers/utils.js +400 -0
  50. package/dist/infra/sandbox-factory.js +30 -0
  51. package/dist/infra/sandbox.js +243 -0
  52. package/dist/infra/store/factory.js +80 -0
  53. package/dist/infra/store/index.js +26 -0
  54. package/dist/infra/store/json-store.js +606 -0
  55. package/dist/infra/store/types.js +2 -0
  56. package/dist/infra/store.js +29 -0
  57. package/dist/tools/bash_kill/index.js +35 -0
  58. package/dist/tools/bash_kill/prompt.js +14 -0
  59. package/dist/tools/bash_logs/index.js +40 -0
  60. package/dist/tools/bash_logs/prompt.js +14 -0
  61. package/dist/tools/bash_run/index.js +61 -0
  62. package/dist/tools/bash_run/prompt.js +18 -0
  63. package/dist/tools/builtin.js +26 -0
  64. package/dist/tools/define.js +214 -0
  65. package/dist/tools/fs_edit/index.js +62 -0
  66. package/dist/tools/fs_edit/prompt.js +15 -0
  67. package/dist/tools/fs_glob/index.js +40 -0
  68. package/dist/tools/fs_glob/prompt.js +15 -0
  69. package/dist/tools/fs_grep/index.js +66 -0
  70. package/dist/tools/fs_grep/prompt.js +16 -0
  71. package/dist/tools/fs_multi_edit/index.js +106 -0
  72. package/dist/tools/fs_multi_edit/prompt.js +16 -0
  73. package/dist/tools/fs_read/index.js +40 -0
  74. package/dist/tools/fs_read/prompt.js +16 -0
  75. package/dist/tools/fs_write/index.js +40 -0
  76. package/dist/tools/fs_write/prompt.js +15 -0
  77. package/dist/tools/index.js +61 -0
  78. package/dist/tools/mcp.js +185 -0
  79. package/dist/tools/registry.js +26 -0
  80. package/dist/tools/scripts.js +205 -0
  81. package/dist/tools/skills.js +115 -0
  82. package/dist/tools/task_run/index.js +58 -0
  83. package/dist/tools/task_run/prompt.js +25 -0
  84. package/dist/tools/todo_read/index.js +29 -0
  85. package/dist/tools/todo_read/prompt.js +18 -0
  86. package/dist/tools/todo_write/index.js +42 -0
  87. package/dist/tools/todo_write/prompt.js +23 -0
  88. package/dist/tools/tool.js +211 -0
  89. package/dist/tools/toolkit.js +98 -0
  90. package/dist/tools/type-inference.js +207 -0
  91. package/dist/utils/agent-id.js +28 -0
  92. package/dist/utils/logger.js +44 -0
  93. package/dist/utils/session-id.js +64 -0
  94. package/package.json +7 -38
  95. package/dist/index.js.map +0 -7
  96. package/dist/index.mjs +0 -60385
  97. package/dist/index.mjs.map +0 -7
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Retry Strategy Module
4
+ *
5
+ * Exponential backoff with jitter for handling transient failures.
6
+ * Respects provider-specific retry-after headers.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AGGRESSIVE_RETRY_CONFIG = exports.DEFAULT_RETRY_CONFIG = void 0;
10
+ exports.withRetry = withRetry;
11
+ exports.createRetryWrapper = createRetryWrapper;
12
+ exports.withRetryAndTimeout = withRetryAndTimeout;
13
+ exports.shouldRetry = shouldRetry;
14
+ exports.getRetryDelay = getRetryDelay;
15
+ const errors_1 = require("./errors");
16
+ /**
17
+ * Default retry configuration.
18
+ * Suitable for most provider API calls.
19
+ */
20
+ exports.DEFAULT_RETRY_CONFIG = {
21
+ maxRetries: 3,
22
+ baseDelayMs: 1000,
23
+ maxDelayMs: 60000,
24
+ jitterFactor: 0.2,
25
+ };
26
+ /**
27
+ * Aggressive retry configuration for critical operations.
28
+ */
29
+ exports.AGGRESSIVE_RETRY_CONFIG = {
30
+ maxRetries: 5,
31
+ baseDelayMs: 500,
32
+ maxDelayMs: 120000,
33
+ jitterFactor: 0.3,
34
+ };
35
+ /**
36
+ * Execute a function with retry logic.
37
+ *
38
+ * @param fn - Async function to execute
39
+ * @param config - Retry configuration
40
+ * @param onRetry - Optional callback before each retry
41
+ * @returns Result of the function
42
+ * @throws Last error if all retries exhausted
43
+ */
44
+ async function withRetry(fn, config = exports.DEFAULT_RETRY_CONFIG, onRetry) {
45
+ let lastError;
46
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
47
+ try {
48
+ return await fn();
49
+ }
50
+ catch (error) {
51
+ // Convert to ProviderError if needed
52
+ const providerError = error instanceof errors_1.ProviderError
53
+ ? error
54
+ : (0, errors_1.parseProviderError)(error, config.provider || 'unknown');
55
+ lastError = providerError;
56
+ // Don't retry non-retryable errors
57
+ if (!providerError.retryable) {
58
+ throw providerError;
59
+ }
60
+ // Don't retry if we've exhausted attempts
61
+ if (attempt === config.maxRetries) {
62
+ throw providerError;
63
+ }
64
+ // Calculate delay with exponential backoff
65
+ let delay = calculateBackoffDelay(attempt, config);
66
+ // Respect retry-after header if available
67
+ if (providerError instanceof errors_1.RateLimitError && providerError.retryAfter) {
68
+ delay = Math.max(delay, providerError.retryAfter * 1000);
69
+ }
70
+ else if (providerError instanceof errors_1.ServiceUnavailableError && providerError.retryAfter) {
71
+ delay = Math.max(delay, providerError.retryAfter * 1000);
72
+ }
73
+ // Apply jitter
74
+ delay = applyJitter(delay, config.jitterFactor);
75
+ // Invoke callback
76
+ onRetry?.(providerError, attempt + 1, delay);
77
+ // Wait before retry
78
+ await sleep(delay);
79
+ }
80
+ }
81
+ // Should not reach here, but TypeScript needs this
82
+ throw lastError || new Error('Unexpected retry loop exit');
83
+ }
84
+ /**
85
+ * Calculate exponential backoff delay.
86
+ */
87
+ function calculateBackoffDelay(attempt, config) {
88
+ const delay = config.baseDelayMs * Math.pow(2, attempt);
89
+ return Math.min(delay, config.maxDelayMs);
90
+ }
91
+ /**
92
+ * Apply jitter to a delay value.
93
+ */
94
+ function applyJitter(delay, jitterFactor) {
95
+ const jitter = delay * jitterFactor * (Math.random() - 0.5) * 2;
96
+ return Math.max(0, Math.floor(delay + jitter));
97
+ }
98
+ /**
99
+ * Sleep for a specified duration.
100
+ */
101
+ function sleep(ms) {
102
+ return new Promise(resolve => setTimeout(resolve, ms));
103
+ }
104
+ /**
105
+ * Create a retry wrapper for a provider.
106
+ *
107
+ * @param provider - Provider name for error context
108
+ * @param config - Retry configuration
109
+ * @returns Configured retry function
110
+ */
111
+ function createRetryWrapper(provider, config = {}) {
112
+ const mergedConfig = {
113
+ ...exports.DEFAULT_RETRY_CONFIG,
114
+ ...config,
115
+ provider,
116
+ };
117
+ return (fn, onRetry) => withRetry(fn, mergedConfig, onRetry);
118
+ }
119
+ /**
120
+ * Retry with timeout.
121
+ * Aborts if total time exceeds timeout even if retries remain.
122
+ */
123
+ async function withRetryAndTimeout(fn, timeoutMs, config = exports.DEFAULT_RETRY_CONFIG, onRetry) {
124
+ const startTime = Date.now();
125
+ let lastError;
126
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
127
+ // Check if we've exceeded timeout
128
+ if (Date.now() - startTime > timeoutMs) {
129
+ throw lastError || new Error(`Operation timed out after ${timeoutMs}ms`);
130
+ }
131
+ try {
132
+ // Create a timeout promise
133
+ const remainingTime = timeoutMs - (Date.now() - startTime);
134
+ return await Promise.race([
135
+ fn(),
136
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), remainingTime)),
137
+ ]);
138
+ }
139
+ catch (error) {
140
+ const providerError = error instanceof errors_1.ProviderError
141
+ ? error
142
+ : (0, errors_1.parseProviderError)(error, config.provider || 'unknown');
143
+ lastError = providerError;
144
+ if (!providerError.retryable || attempt === config.maxRetries) {
145
+ throw providerError;
146
+ }
147
+ let delay = calculateBackoffDelay(attempt, config);
148
+ if (providerError instanceof errors_1.RateLimitError && providerError.retryAfter) {
149
+ delay = Math.max(delay, providerError.retryAfter * 1000);
150
+ }
151
+ delay = applyJitter(delay, config.jitterFactor);
152
+ // Don't wait longer than remaining timeout
153
+ const remainingTime = timeoutMs - (Date.now() - startTime);
154
+ if (delay > remainingTime) {
155
+ throw lastError;
156
+ }
157
+ onRetry?.(providerError, attempt + 1, delay);
158
+ await sleep(delay);
159
+ }
160
+ }
161
+ throw lastError;
162
+ }
163
+ /**
164
+ * Check if an operation should be retried.
165
+ * Useful for manual retry logic.
166
+ */
167
+ function shouldRetry(error, attempt, maxRetries) {
168
+ if (attempt >= maxRetries) {
169
+ return false;
170
+ }
171
+ if (error instanceof errors_1.ProviderError) {
172
+ return error.retryable;
173
+ }
174
+ // For unknown errors, be conservative
175
+ return false;
176
+ }
177
+ /**
178
+ * Get recommended delay for next retry.
179
+ */
180
+ function getRetryDelay(error, attempt, config = exports.DEFAULT_RETRY_CONFIG) {
181
+ let delay = calculateBackoffDelay(attempt, config);
182
+ if (error instanceof errors_1.RateLimitError && error.retryAfter) {
183
+ delay = Math.max(delay, error.retryAfter * 1000);
184
+ }
185
+ else if (error instanceof errors_1.ServiceUnavailableError && error.retryAfter) {
186
+ delay = Math.max(delay, error.retryAfter * 1000);
187
+ }
188
+ return applyJitter(delay, config.jitterFactor);
189
+ }
@@ -0,0 +1,376 @@
1
+ "use strict";
2
+ /**
3
+ * Usage Statistics Module
4
+ *
5
+ * Unified usage tracking, cache metrics, and cost calculation
6
+ * across all supported model providers.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PROVIDER_PRICING = void 0;
10
+ exports.createEmptyUsage = createEmptyUsage;
11
+ exports.calculateCost = calculateCost;
12
+ exports.normalizeAnthropicUsage = normalizeAnthropicUsage;
13
+ exports.normalizeOpenAIUsage = normalizeOpenAIUsage;
14
+ exports.normalizeGeminiUsage = normalizeGeminiUsage;
15
+ exports.normalizeDeepSeekUsage = normalizeDeepSeekUsage;
16
+ exports.aggregateUsage = aggregateUsage;
17
+ exports.formatUsageString = formatUsageString;
18
+ /**
19
+ * Provider pricing table (per 1M tokens).
20
+ */
21
+ exports.PROVIDER_PRICING = {
22
+ anthropic: {
23
+ 'claude-opus-4-5': {
24
+ input: 5.0,
25
+ output: 25.0,
26
+ cacheWrite: 6.25, // 5m TTL: 1.25x input
27
+ cacheRead: 0.5, // 10% of input
28
+ },
29
+ 'claude-opus-4-5-1h': {
30
+ input: 5.0,
31
+ output: 25.0,
32
+ cacheWrite: 10.0, // 1h TTL: 2x input
33
+ cacheRead: 0.5,
34
+ },
35
+ 'claude-sonnet-4-5': {
36
+ input: 3.0,
37
+ output: 15.0,
38
+ cacheWrite: 3.75,
39
+ cacheRead: 0.3,
40
+ },
41
+ 'claude-haiku-4-5': {
42
+ input: 1.0,
43
+ output: 5.0,
44
+ cacheWrite: 1.25,
45
+ cacheRead: 0.1,
46
+ },
47
+ },
48
+ openai: {
49
+ 'gpt-5.2': {
50
+ input: 5.0,
51
+ output: 15.0,
52
+ cacheRead: 1.25, // 75% discount
53
+ },
54
+ 'gpt-4.1': {
55
+ input: 2.0,
56
+ output: 8.0,
57
+ cacheRead: 0.5,
58
+ },
59
+ },
60
+ gemini: {
61
+ 'gemini-3-pro': {
62
+ input: 2.5,
63
+ output: 10.0,
64
+ cacheRead: 0.625, // 75% discount
65
+ },
66
+ 'gemini-3-flash': {
67
+ input: 0.075,
68
+ output: 0.3,
69
+ cacheRead: 0.01875,
70
+ },
71
+ },
72
+ deepseek: {
73
+ 'deepseek-reasoner': {
74
+ input: 0.28,
75
+ output: 1.10,
76
+ cacheRead: 0.028, // 90% discount
77
+ },
78
+ 'deepseek-chat': {
79
+ input: 0.14,
80
+ output: 0.28,
81
+ cacheRead: 0.014,
82
+ },
83
+ },
84
+ qwen: {
85
+ 'qwen3-max': {
86
+ input: 0.80,
87
+ output: 2.00,
88
+ },
89
+ 'qwen3-plus': {
90
+ input: 0.50,
91
+ output: 1.50,
92
+ },
93
+ },
94
+ };
95
+ /**
96
+ * Create empty usage statistics.
97
+ */
98
+ function createEmptyUsage() {
99
+ return {
100
+ inputTokens: 0,
101
+ outputTokens: 0,
102
+ totalTokens: 0,
103
+ cache: {
104
+ cacheCreationTokens: 0,
105
+ cacheReadTokens: 0,
106
+ provider: {},
107
+ },
108
+ cost: {
109
+ inputCost: 0,
110
+ outputCost: 0,
111
+ cacheWriteCost: 0,
112
+ totalCost: 0,
113
+ cacheSavings: 0,
114
+ currency: 'USD',
115
+ },
116
+ request: {
117
+ startTime: 0,
118
+ endTime: 0,
119
+ latencyMs: 0,
120
+ modelUsed: '',
121
+ },
122
+ };
123
+ }
124
+ /**
125
+ * Calculate cost based on usage and pricing.
126
+ */
127
+ function calculateCost(usage, pricing, cacheTtl) {
128
+ const perMillionFactor = 1000000;
129
+ // Calculate raw input cost (before cache)
130
+ const rawInputCost = (usage.inputTokens / perMillionFactor) * pricing.input;
131
+ // Calculate cache costs
132
+ const cacheReadCost = pricing.cacheRead
133
+ ? ((usage.cacheReadTokens || 0) / perMillionFactor) * pricing.cacheRead
134
+ : 0;
135
+ let cacheWriteCost = 0;
136
+ if (usage.cacheCreationTokens && pricing.cacheWrite) {
137
+ const multiplier = cacheTtl === '1h' ? 2.0 : 1.25;
138
+ cacheWriteCost = ((usage.cacheCreationTokens) / perMillionFactor) * pricing.input * multiplier;
139
+ }
140
+ // Actual input cost = raw - cached tokens + cache read cost
141
+ const cachedInputTokens = usage.cacheReadTokens || 0;
142
+ const nonCachedInputTokens = Math.max(0, usage.inputTokens - cachedInputTokens);
143
+ const inputCost = (nonCachedInputTokens / perMillionFactor) * pricing.input + cacheReadCost;
144
+ // Output cost
145
+ const outputCost = (usage.outputTokens / perMillionFactor) * pricing.output;
146
+ // Reasoning cost (if separate pricing)
147
+ const reasoningCost = pricing.reasoning && usage.reasoningTokens
148
+ ? (usage.reasoningTokens / perMillionFactor) * pricing.reasoning
149
+ : 0;
150
+ // Total cost
151
+ const totalCost = inputCost + outputCost + cacheWriteCost + reasoningCost;
152
+ // Cache savings = what we would have paid - what we actually paid
153
+ const cacheSavings = cachedInputTokens > 0
154
+ ? (cachedInputTokens / perMillionFactor) * pricing.input - cacheReadCost
155
+ : 0;
156
+ return {
157
+ inputCost: Math.round(inputCost * 100000) / 100000, // 5 decimal precision
158
+ outputCost: Math.round((outputCost + reasoningCost) * 100000) / 100000,
159
+ cacheWriteCost: Math.round(cacheWriteCost * 100000) / 100000,
160
+ totalCost: Math.round(totalCost * 100000) / 100000,
161
+ cacheSavings: Math.round(cacheSavings * 100000) / 100000,
162
+ currency: 'USD',
163
+ };
164
+ }
165
+ /**
166
+ * Normalize Anthropic usage to unified format.
167
+ */
168
+ function normalizeAnthropicUsage(raw, model, startTime, requestId, cacheTtl) {
169
+ const inputTokens = raw.input_tokens || 0;
170
+ const outputTokens = raw.output_tokens || 0;
171
+ const cacheCreationTokens = raw.cache_creation_input_tokens || 0;
172
+ const cacheReadTokens = raw.cache_read_input_tokens || 0;
173
+ // Determine model key for pricing
174
+ const modelKey = model.includes('opus') ? 'claude-opus-4-5'
175
+ : model.includes('sonnet') ? 'claude-sonnet-4-5'
176
+ : 'claude-haiku-4-5';
177
+ const pricing = cacheTtl === '1h'
178
+ ? exports.PROVIDER_PRICING.anthropic[`${modelKey}-1h`] || exports.PROVIDER_PRICING.anthropic[modelKey]
179
+ : exports.PROVIDER_PRICING.anthropic[modelKey];
180
+ const cost = pricing
181
+ ? calculateCost({ inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens }, pricing, cacheTtl)
182
+ : createEmptyUsage().cost;
183
+ const endTime = Date.now();
184
+ return {
185
+ inputTokens,
186
+ outputTokens,
187
+ totalTokens: inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens,
188
+ cache: {
189
+ cacheCreationTokens,
190
+ cacheReadTokens,
191
+ cacheSavingsEstimate: cost.cacheSavings,
192
+ provider: {
193
+ anthropic: {
194
+ breakpointsUsed: 0, // Inferred from request
195
+ ttlUsed: cacheTtl || '5m',
196
+ },
197
+ },
198
+ },
199
+ cost,
200
+ request: {
201
+ startTime,
202
+ endTime,
203
+ latencyMs: endTime - startTime,
204
+ requestId,
205
+ modelUsed: model,
206
+ },
207
+ raw,
208
+ };
209
+ }
210
+ /**
211
+ * Normalize OpenAI usage to unified format.
212
+ */
213
+ function normalizeOpenAIUsage(raw, model, api, startTime, requestId) {
214
+ const inputTokens = raw.prompt_tokens || 0;
215
+ const outputTokens = raw.completion_tokens || 0;
216
+ const cacheReadTokens = raw.prompt_tokens_details?.cached_tokens || 0;
217
+ const reasoningTokens = raw.completion_tokens_details?.reasoning_tokens || 0;
218
+ const modelKey = model.includes('gpt-5') ? 'gpt-5.2' : 'gpt-4.1';
219
+ const pricing = exports.PROVIDER_PRICING.openai[modelKey];
220
+ const cost = pricing
221
+ ? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
222
+ : createEmptyUsage().cost;
223
+ const endTime = Date.now();
224
+ return {
225
+ inputTokens,
226
+ outputTokens,
227
+ totalTokens: raw.total_tokens || (inputTokens + outputTokens),
228
+ reasoningTokens: reasoningTokens || undefined,
229
+ cache: {
230
+ cacheCreationTokens: 0,
231
+ cacheReadTokens,
232
+ cacheSavingsEstimate: cost.cacheSavings,
233
+ provider: {
234
+ openai: {
235
+ automaticCacheHit: cacheReadTokens > 0,
236
+ },
237
+ },
238
+ },
239
+ cost,
240
+ request: {
241
+ startTime,
242
+ endTime,
243
+ latencyMs: endTime - startTime,
244
+ requestId,
245
+ modelUsed: model,
246
+ },
247
+ raw,
248
+ };
249
+ }
250
+ /**
251
+ * Normalize Gemini usage to unified format.
252
+ */
253
+ function normalizeGeminiUsage(raw, model, startTime, cachedContentName) {
254
+ const inputTokens = raw.promptTokenCount || 0;
255
+ const outputTokens = raw.candidatesTokenCount || 0;
256
+ const cacheReadTokens = raw.cachedContentTokenCount || 0;
257
+ const reasoningTokens = raw.thoughtsTokenCount || 0;
258
+ const modelKey = model.includes('pro') ? 'gemini-3-pro' : 'gemini-3-flash';
259
+ const pricing = exports.PROVIDER_PRICING.gemini[modelKey];
260
+ const cost = pricing
261
+ ? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
262
+ : createEmptyUsage().cost;
263
+ const endTime = Date.now();
264
+ return {
265
+ inputTokens,
266
+ outputTokens,
267
+ totalTokens: raw.totalTokenCount || (inputTokens + outputTokens),
268
+ reasoningTokens: reasoningTokens || undefined,
269
+ cache: {
270
+ cacheCreationTokens: 0,
271
+ cacheReadTokens,
272
+ cacheSavingsEstimate: cost.cacheSavings,
273
+ provider: {
274
+ gemini: {
275
+ cachedContentName,
276
+ implicitCacheHit: cacheReadTokens > 0 && !cachedContentName,
277
+ },
278
+ },
279
+ },
280
+ cost,
281
+ request: {
282
+ startTime,
283
+ endTime,
284
+ latencyMs: endTime - startTime,
285
+ modelUsed: model,
286
+ },
287
+ raw,
288
+ };
289
+ }
290
+ /**
291
+ * Normalize DeepSeek usage to unified format.
292
+ */
293
+ function normalizeDeepSeekUsage(raw, model, startTime, requestId) {
294
+ const inputTokens = raw.prompt_tokens || 0;
295
+ const outputTokens = raw.completion_tokens || 0;
296
+ const cacheReadTokens = raw.prompt_cache_hit_tokens || 0;
297
+ const modelKey = model.includes('reasoner') ? 'deepseek-reasoner' : 'deepseek-chat';
298
+ const pricing = exports.PROVIDER_PRICING.deepseek[modelKey];
299
+ const cost = pricing
300
+ ? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
301
+ : createEmptyUsage().cost;
302
+ const endTime = Date.now();
303
+ return {
304
+ inputTokens,
305
+ outputTokens,
306
+ totalTokens: raw.total_tokens || (inputTokens + outputTokens),
307
+ cache: {
308
+ cacheCreationTokens: 0,
309
+ cacheReadTokens,
310
+ cacheSavingsEstimate: cost.cacheSavings,
311
+ provider: {
312
+ deepseek: {
313
+ prefixCacheHit: cacheReadTokens > 0,
314
+ },
315
+ },
316
+ },
317
+ cost,
318
+ request: {
319
+ startTime,
320
+ endTime,
321
+ latencyMs: endTime - startTime,
322
+ requestId,
323
+ modelUsed: model,
324
+ },
325
+ raw,
326
+ };
327
+ }
328
+ /**
329
+ * Aggregate multiple usage statistics.
330
+ */
331
+ function aggregateUsage(usages) {
332
+ const aggregated = createEmptyUsage();
333
+ for (const usage of usages) {
334
+ aggregated.inputTokens += usage.inputTokens;
335
+ aggregated.outputTokens += usage.outputTokens;
336
+ aggregated.totalTokens += usage.totalTokens;
337
+ aggregated.reasoningTokens = (aggregated.reasoningTokens || 0) + (usage.reasoningTokens || 0);
338
+ aggregated.cache.cacheCreationTokens += usage.cache.cacheCreationTokens;
339
+ aggregated.cache.cacheReadTokens += usage.cache.cacheReadTokens;
340
+ aggregated.cache.cacheSavingsEstimate = (aggregated.cache.cacheSavingsEstimate || 0) +
341
+ (usage.cache.cacheSavingsEstimate || 0);
342
+ aggregated.cost.inputCost += usage.cost.inputCost;
343
+ aggregated.cost.outputCost += usage.cost.outputCost;
344
+ aggregated.cost.cacheWriteCost += usage.cost.cacheWriteCost;
345
+ aggregated.cost.totalCost += usage.cost.totalCost;
346
+ aggregated.cost.cacheSavings += usage.cost.cacheSavings;
347
+ }
348
+ // Average latency
349
+ if (usages.length > 0) {
350
+ aggregated.request.latencyMs = usages.reduce((sum, u) => sum + u.request.latencyMs, 0) / usages.length;
351
+ }
352
+ return aggregated;
353
+ }
354
+ /**
355
+ * Format usage as human-readable string.
356
+ */
357
+ function formatUsageString(usage) {
358
+ const parts = [];
359
+ parts.push(`Tokens: ${usage.inputTokens} in / ${usage.outputTokens} out`);
360
+ if (usage.reasoningTokens) {
361
+ parts.push(`(${usage.reasoningTokens} reasoning)`);
362
+ }
363
+ if (usage.cache.cacheReadTokens > 0) {
364
+ parts.push(`Cache hit: ${usage.cache.cacheReadTokens} tokens`);
365
+ }
366
+ if (usage.cost.totalCost > 0) {
367
+ parts.push(`Cost: $${usage.cost.totalCost.toFixed(5)}`);
368
+ }
369
+ if (usage.cost.cacheSavings > 0) {
370
+ parts.push(`(saved: $${usage.cost.cacheSavings.toFixed(5)})`);
371
+ }
372
+ if (usage.request.latencyMs > 0) {
373
+ parts.push(`Latency: ${usage.request.latencyMs}ms`);
374
+ }
375
+ return parts.join(' | ');
376
+ }