@earendil-works/pi-coding-agent 0.80.9 → 0.81.0

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 (141) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +6 -3
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +13 -0
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +0 -3
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +45 -56
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/branch-summarization.d.ts +2 -1
  14. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  15. package/dist/core/compaction/branch-summarization.js +3 -4
  16. package/dist/core/compaction/branch-summarization.js.map +1 -1
  17. package/dist/core/compaction/compaction.d.ts +7 -0
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +47 -14
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/compaction/utils.d.ts +1 -1
  22. package/dist/core/compaction/utils.d.ts.map +1 -1
  23. package/dist/core/compaction/utils.js +6 -17
  24. package/dist/core/compaction/utils.js.map +1 -1
  25. package/dist/core/extensions/loader.d.ts.map +1 -1
  26. package/dist/core/extensions/loader.js +13 -2
  27. package/dist/core/extensions/loader.js.map +1 -1
  28. package/dist/core/extensions/runner.d.ts +2 -1
  29. package/dist/core/extensions/runner.d.ts.map +1 -1
  30. package/dist/core/extensions/runner.js +31 -0
  31. package/dist/core/extensions/runner.js.map +1 -1
  32. package/dist/core/extensions/types.d.ts +16 -2
  33. package/dist/core/extensions/types.d.ts.map +1 -1
  34. package/dist/core/extensions/types.js.map +1 -1
  35. package/dist/core/model-registry.d.ts +5 -1
  36. package/dist/core/model-registry.d.ts.map +1 -1
  37. package/dist/core/model-registry.js +17 -2
  38. package/dist/core/model-registry.js.map +1 -1
  39. package/dist/core/model-resolver.d.ts.map +1 -1
  40. package/dist/core/model-resolver.js +2 -0
  41. package/dist/core/model-resolver.js.map +1 -1
  42. package/dist/core/model-runtime.d.ts +7 -2
  43. package/dist/core/model-runtime.d.ts.map +1 -1
  44. package/dist/core/model-runtime.js +41 -18
  45. package/dist/core/model-runtime.js.map +1 -1
  46. package/dist/core/prompt-templates.d.ts +1 -0
  47. package/dist/core/prompt-templates.d.ts.map +1 -1
  48. package/dist/core/prompt-templates.js +4 -4
  49. package/dist/core/prompt-templates.js.map +1 -1
  50. package/dist/core/remote-catalog-provider.d.ts +1 -1
  51. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  52. package/dist/core/remote-catalog-provider.js +26 -7
  53. package/dist/core/remote-catalog-provider.js.map +1 -1
  54. package/dist/core/resource-loader.d.ts.map +1 -1
  55. package/dist/core/resource-loader.js +1 -0
  56. package/dist/core/resource-loader.js.map +1 -1
  57. package/dist/core/sdk.d.ts.map +1 -1
  58. package/dist/core/sdk.js +1 -1
  59. package/dist/core/sdk.js.map +1 -1
  60. package/dist/core/session-manager.d.ts +9 -4
  61. package/dist/core/session-manager.d.ts.map +1 -1
  62. package/dist/core/session-manager.js +99 -23
  63. package/dist/core/session-manager.js.map +1 -1
  64. package/dist/core/tools/read.d.ts.map +1 -1
  65. package/dist/core/tools/read.js +1 -1
  66. package/dist/core/tools/read.js.map +1 -1
  67. package/dist/core/usage-totals.d.ts +19 -0
  68. package/dist/core/usage-totals.d.ts.map +1 -0
  69. package/dist/core/usage-totals.js +52 -0
  70. package/dist/core/usage-totals.js.map +1 -0
  71. package/dist/extensions/index.d.ts +3 -0
  72. package/dist/extensions/index.d.ts.map +1 -0
  73. package/dist/extensions/index.js +3 -0
  74. package/dist/extensions/index.js.map +1 -0
  75. package/dist/extensions/llama/client.d.ts +61 -0
  76. package/dist/extensions/llama/client.d.ts.map +1 -0
  77. package/dist/extensions/llama/client.js +302 -0
  78. package/dist/extensions/llama/client.js.map +1 -0
  79. package/dist/extensions/llama/huggingface.d.ts +23 -0
  80. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  81. package/dist/extensions/llama/huggingface.js +141 -0
  82. package/dist/extensions/llama/huggingface.js.map +1 -0
  83. package/dist/extensions/llama/index.d.ts +3 -0
  84. package/dist/extensions/llama/index.d.ts.map +1 -0
  85. package/dist/extensions/llama/index.js +208 -0
  86. package/dist/extensions/llama/index.js.map +1 -0
  87. package/dist/extensions/llama/provider.d.ts +10 -0
  88. package/dist/extensions/llama/provider.d.ts.map +1 -0
  89. package/dist/extensions/llama/provider.js +102 -0
  90. package/dist/extensions/llama/provider.js.map +1 -0
  91. package/dist/extensions/llama/ui.d.ts +42 -0
  92. package/dist/extensions/llama/ui.d.ts.map +1 -0
  93. package/dist/extensions/llama/ui.js +416 -0
  94. package/dist/extensions/llama/ui.js.map +1 -0
  95. package/dist/index.d.ts +2 -2
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/main.d.ts.map +1 -1
  100. package/dist/main.js +9 -4
  101. package/dist/main.js.map +1 -1
  102. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/footer.js +24 -23
  104. package/dist/modes/interactive/components/footer.js.map +1 -1
  105. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  106. package/dist/modes/interactive/interactive-mode.js +14 -22
  107. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  108. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  109. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  110. package/dist/modes/rpc/rpc-client.js +7 -0
  111. package/dist/modes/rpc/rpc-client.js.map +1 -1
  112. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  113. package/dist/modes/rpc/rpc-mode.js +4 -0
  114. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  115. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  116. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  117. package/dist/modes/rpc/rpc-types.js.map +1 -1
  118. package/docs/compaction.md +8 -3
  119. package/docs/custom-provider.md +28 -0
  120. package/docs/extensions.md +51 -9
  121. package/docs/index.md +1 -0
  122. package/docs/llama-cpp.md +99 -0
  123. package/docs/prompt-templates.md +1 -0
  124. package/docs/providers.md +11 -0
  125. package/docs/rpc.md +48 -2
  126. package/docs/session-format.md +17 -3
  127. package/docs/tui.md +41 -26
  128. package/docs/usage.md +2 -1
  129. package/examples/extensions/README.md +1 -1
  130. package/examples/extensions/custom-compaction.ts +1 -0
  131. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  132. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  133. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  134. package/examples/extensions/gondolin/package-lock.json +2 -2
  135. package/examples/extensions/gondolin/package.json +1 -1
  136. package/examples/extensions/sandbox/package-lock.json +2 -2
  137. package/examples/extensions/sandbox/package.json +1 -1
  138. package/examples/extensions/with-deps/package-lock.json +2 -2
  139. package/examples/extensions/with-deps/package.json +1 -1
  140. package/npm-shrinkwrap.json +16 -31
  141. package/package.json +4 -4
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
+ import { contentText } from "@earendil-works/pi-ai";
17
18
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
18
19
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
19
20
  import { stripFrontmatter } from "../utils/frontmatter.js";
@@ -35,6 +36,7 @@ import { buildSystemPrompt } from "./system-prompt.js";
35
36
  import { createLocalBashOperations } from "./tools/bash.js";
36
37
  import { createAllToolDefinitions } from "./tools/index.js";
37
38
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
39
+ import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
38
40
  /**
39
41
  * Parse a skill block from message text.
40
42
  * Returns null if the text doesn't contain a skill block.
@@ -188,7 +190,7 @@ export class AgentSession {
188
190
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
189
191
  }
190
192
  async _getSummarizationRequestAuth(model) {
191
- if (this.agent.streamFn === streamSimple) {
193
+ if (this.agent.streamFunction === streamSimple) {
192
194
  return this._getRequiredRequestAuth(model);
193
195
  }
194
196
  try {
@@ -243,6 +245,7 @@ export class AgentSession {
243
245
  content: result.content,
244
246
  details: result.details,
245
247
  isError,
248
+ usage: result.usage,
246
249
  });
247
250
  if (!hookResult) {
248
251
  return undefined;
@@ -251,6 +254,7 @@ export class AgentSession {
251
254
  content: hookResult.content,
252
255
  details: hookResult.details,
253
256
  isError: hookResult.isError ?? isError,
257
+ usage: hookResult.usage,
254
258
  };
255
259
  };
256
260
  }
@@ -325,7 +329,7 @@ export class AgentSession {
325
329
  // This ensures the UI sees the updated queue state
326
330
  if (event.type === "message_start" && event.message.role === "user") {
327
331
  this._overflowRecoveryAttempted = false;
328
- const messageText = this._getUserMessageText(event.message);
332
+ const messageText = contentText(event.message.content, "");
329
333
  if (messageText) {
330
334
  // Check steering queue first
331
335
  const steeringIndex = this._steeringMessages.indexOf(messageText);
@@ -394,16 +398,6 @@ export class AgentSession {
394
398
  }
395
399
  return false;
396
400
  }
397
- /** Extract text content from a message */
398
- _getUserMessageText(message) {
399
- if (message.role !== "user")
400
- return "";
401
- const content = message.content;
402
- if (typeof content === "string")
403
- return content;
404
- const textBlocks = content.filter((c) => c.type === "text");
405
- return textBlocks.map((c) => c.text).join("");
406
- }
407
401
  /** Find the last assistant message in agent state (including aborted ones) */
408
402
  _findLastAssistantMessage() {
409
403
  const messages = this.agent.state.messages;
@@ -1414,26 +1408,29 @@ export class AgentSession {
1414
1408
  let summary;
1415
1409
  let firstKeptEntryId;
1416
1410
  let tokensBefore;
1411
+ let usage;
1417
1412
  let details;
1418
1413
  if (extensionCompaction) {
1419
1414
  // Extension provided compaction content
1420
1415
  summary = extensionCompaction.summary;
1421
1416
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
1422
1417
  tokensBefore = extensionCompaction.tokensBefore;
1418
+ usage = extensionCompaction.usage;
1423
1419
  details = extensionCompaction.details;
1424
1420
  }
1425
1421
  else {
1426
1422
  // Generate compaction result
1427
- const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
1423
+ const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env);
1428
1424
  summary = result.summary;
1429
1425
  firstKeptEntryId = result.firstKeptEntryId;
1430
1426
  tokensBefore = result.tokensBefore;
1427
+ usage = result.usage;
1431
1428
  details = result.details;
1432
1429
  }
1433
1430
  if (this._compactionAbortController.signal.aborted) {
1434
1431
  throw new Error("Compaction cancelled");
1435
1432
  }
1436
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
1433
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1437
1434
  const newEntries = this.sessionManager.getEntries();
1438
1435
  const sessionContext = this.sessionManager.buildSessionContext();
1439
1436
  this.agent.state.messages = sessionContext.messages;
@@ -1454,6 +1451,7 @@ export class AgentSession {
1454
1451
  firstKeptEntryId,
1455
1452
  tokensBefore,
1456
1453
  estimatedTokensAfter,
1454
+ usage,
1457
1455
  details,
1458
1456
  };
1459
1457
  this._emit({
@@ -1600,7 +1598,7 @@ export class AgentSession {
1600
1598
  let apiKey;
1601
1599
  let headers;
1602
1600
  let env;
1603
- if (this.agent.streamFn === streamSimple) {
1601
+ if (this.agent.streamFunction === streamSimple) {
1604
1602
  const authResult = await this._modelRuntime.getAuth(this.model);
1605
1603
  if (!authResult?.auth.apiKey)
1606
1604
  return false;
@@ -1649,20 +1647,23 @@ export class AgentSession {
1649
1647
  let summary;
1650
1648
  let firstKeptEntryId;
1651
1649
  let tokensBefore;
1650
+ let usage;
1652
1651
  let details;
1653
1652
  if (extensionCompaction) {
1654
1653
  // Extension provided compaction content
1655
1654
  summary = extensionCompaction.summary;
1656
1655
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
1657
1656
  tokensBefore = extensionCompaction.tokensBefore;
1657
+ usage = extensionCompaction.usage;
1658
1658
  details = extensionCompaction.details;
1659
1659
  }
1660
1660
  else {
1661
1661
  // Generate compaction result
1662
- const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
1662
+ const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env);
1663
1663
  summary = compactResult.summary;
1664
1664
  firstKeptEntryId = compactResult.firstKeptEntryId;
1665
1665
  tokensBefore = compactResult.tokensBefore;
1666
+ usage = compactResult.usage;
1666
1667
  details = compactResult.details;
1667
1668
  }
1668
1669
  if (this._autoCompactionAbortController.signal.aborted) {
@@ -1675,7 +1676,7 @@ export class AgentSession {
1675
1676
  });
1676
1677
  return false;
1677
1678
  }
1678
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
1679
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1679
1680
  const newEntries = this.sessionManager.getEntries();
1680
1681
  const sessionContext = this.sessionManager.buildSessionContext();
1681
1682
  this.agent.state.messages = sessionContext.messages;
@@ -1696,6 +1697,7 @@ export class AgentSession {
1696
1697
  firstKeptEntryId,
1697
1698
  tokensBefore,
1698
1699
  estimatedTokensAfter,
1700
+ usage,
1699
1701
  details,
1700
1702
  };
1701
1703
  this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
@@ -1929,6 +1931,10 @@ export class AgentSession {
1929
1931
  this._modelRuntime.registerProvider(name, config);
1930
1932
  this._refreshCurrentModelFromRegistry();
1931
1933
  },
1934
+ registerNativeProvider: (provider) => {
1935
+ this._modelRuntime.registerNativeProvider(provider);
1936
+ this._refreshCurrentModelFromRegistry();
1937
+ },
1932
1938
  unregisterProvider: (name) => {
1933
1939
  this._modelRuntime.unregisterProvider(name);
1934
1940
  this._refreshCurrentModelFromRegistry();
@@ -2327,6 +2333,7 @@ export class AgentSession {
2327
2333
  // Run default summarizer if needed
2328
2334
  let summaryText;
2329
2335
  let summaryDetails;
2336
+ let summaryUsage;
2330
2337
  if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
2331
2338
  const model = this.model;
2332
2339
  const { apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
@@ -2340,7 +2347,7 @@ export class AgentSession {
2340
2347
  customInstructions,
2341
2348
  replaceInstructions,
2342
2349
  reserveTokens: branchSummarySettings.reserveTokens,
2343
- streamFn: this.agent.streamFn,
2350
+ streamFn: this.agent.streamFunction,
2344
2351
  });
2345
2352
  if (result.aborted) {
2346
2353
  return { cancelled: true, aborted: true };
@@ -2349,6 +2356,7 @@ export class AgentSession {
2349
2356
  throw new Error(result.error);
2350
2357
  }
2351
2358
  summaryText = result.summary;
2359
+ summaryUsage = result.usage;
2352
2360
  summaryDetails = {
2353
2361
  readFiles: result.readFiles || [],
2354
2362
  modifiedFiles: result.modifiedFiles || [],
@@ -2357,6 +2365,7 @@ export class AgentSession {
2357
2365
  else if (extensionSummary) {
2358
2366
  summaryText = extensionSummary.summary;
2359
2367
  summaryDetails = extensionSummary.details;
2368
+ summaryUsage = extensionSummary.usage;
2360
2369
  }
2361
2370
  // Determine the new leaf position based on target type
2362
2371
  let newLeafId;
@@ -2364,18 +2373,12 @@ export class AgentSession {
2364
2373
  if (targetEntry.type === "message" && targetEntry.message.role === "user") {
2365
2374
  // User message: leaf = parent (null if root), text goes to editor
2366
2375
  newLeafId = targetEntry.parentId;
2367
- editorText = this._extractUserMessageText(targetEntry.message.content);
2376
+ editorText = contentText(targetEntry.message.content, "");
2368
2377
  }
2369
2378
  else if (targetEntry.type === "custom_message") {
2370
2379
  // Custom message: leaf = parent (null if root), text goes to editor
2371
2380
  newLeafId = targetEntry.parentId;
2372
- editorText =
2373
- typeof targetEntry.content === "string"
2374
- ? targetEntry.content
2375
- : targetEntry.content
2376
- .filter((c) => c.type === "text")
2377
- .map((c) => c.text)
2378
- .join("");
2381
+ editorText = contentText(targetEntry.content, "");
2379
2382
  }
2380
2383
  else {
2381
2384
  // Non-user message: leaf = selected node
@@ -2386,7 +2389,7 @@ export class AgentSession {
2386
2389
  let summaryEntry;
2387
2390
  if (summaryText) {
2388
2391
  // Create summary at target position (can be null for root)
2389
- const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
2392
+ const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension, summaryUsage);
2390
2393
  summaryEntry = this.sessionManager.getEntry(summaryId);
2391
2394
  // Attach label to the summary entry
2392
2395
  if (label) {
@@ -2434,24 +2437,13 @@ export class AgentSession {
2434
2437
  continue;
2435
2438
  if (entry.message.role !== "user")
2436
2439
  continue;
2437
- const text = this._extractUserMessageText(entry.message.content);
2440
+ const text = contentText(entry.message.content, "");
2438
2441
  if (text) {
2439
2442
  result.push({ entryId: entry.id, text });
2440
2443
  }
2441
2444
  }
2442
2445
  return result;
2443
2446
  }
2444
- _extractUserMessageText(content) {
2445
- if (typeof content === "string")
2446
- return content;
2447
- if (Array.isArray(content)) {
2448
- return content
2449
- .filter((c) => c.type === "text")
2450
- .map((c) => c.text)
2451
- .join("");
2452
- }
2453
- return "";
2454
- }
2455
2447
  /**
2456
2448
  * Get session statistics. Aggregates over ALL session entries (including
2457
2449
  * history that was compacted away), so token/cost totals reflect what was
@@ -2463,12 +2455,11 @@ export class AgentSession {
2463
2455
  let toolResults = 0;
2464
2456
  let totalMessages = 0;
2465
2457
  let toolCalls = 0;
2466
- let totalInput = 0;
2467
- let totalOutput = 0;
2468
- let totalCacheRead = 0;
2469
- let totalCacheWrite = 0;
2470
- let totalCost = 0;
2458
+ const usageTotals = createUsageTotals();
2471
2459
  for (const entry of this.sessionManager.getEntries()) {
2460
+ if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
2461
+ addUsageToTotals(usageTotals, entry.usage);
2462
+ }
2472
2463
  if (entry.type !== "message")
2473
2464
  continue;
2474
2465
  totalMessages++;
@@ -2478,6 +2469,9 @@ export class AgentSession {
2478
2469
  }
2479
2470
  else if (message.role === "toolResult") {
2480
2471
  toolResults++;
2472
+ if (message.usage) {
2473
+ addUsageToTotals(usageTotals, message.usage);
2474
+ }
2481
2475
  }
2482
2476
  else if (message.role === "assistant") {
2483
2477
  assistantMessages++;
@@ -2485,12 +2479,7 @@ export class AgentSession {
2485
2479
  if (Array.isArray(assistantMsg.content)) {
2486
2480
  toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
2487
2481
  }
2488
- const usage = assistantMsg.usage;
2489
- totalInput += usage.input;
2490
- totalOutput += usage.output;
2491
- totalCacheRead += usage.cacheRead;
2492
- totalCacheWrite += usage.cacheWrite;
2493
- totalCost += usage.cost.total;
2482
+ addUsageToTotals(usageTotals, assistantMsg.usage);
2494
2483
  }
2495
2484
  }
2496
2485
  return {
@@ -2502,13 +2491,13 @@ export class AgentSession {
2502
2491
  toolResults,
2503
2492
  totalMessages,
2504
2493
  tokens: {
2505
- input: totalInput,
2506
- output: totalOutput,
2507
- cacheRead: totalCacheRead,
2508
- cacheWrite: totalCacheWrite,
2509
- total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
2494
+ input: usageTotals.input,
2495
+ output: usageTotals.output,
2496
+ cacheRead: usageTotals.cacheRead,
2497
+ cacheWrite: usageTotals.cacheWrite,
2498
+ total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite,
2510
2499
  },
2511
- cost: totalCost,
2500
+ cost: usageTotals.cost,
2512
2501
  contextUsage: this.getContextUsage(),
2513
2502
  };
2514
2503
  }