@averyyy/pi-coding-agent 0.80.10-piclient.2 → 0.82.0-piclient.1

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 (185) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +19 -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/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/startup-ui.d.ts.map +1 -1
  10. package/dist/cli/startup-ui.js +1 -1
  11. package/dist/cli/startup-ui.js.map +1 -1
  12. package/dist/core/agent-session-services.d.ts.map +1 -1
  13. package/dist/core/agent-session-services.js +13 -0
  14. package/dist/core/agent-session-services.js.map +1 -1
  15. package/dist/core/agent-session.d.ts +33 -3
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +91 -59
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/compaction/branch-summarization.d.ts +7 -1
  20. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  21. package/dist/core/compaction/branch-summarization.js +6 -2
  22. package/dist/core/compaction/branch-summarization.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts +23 -4
  24. package/dist/core/compaction/compaction.d.ts.map +1 -1
  25. package/dist/core/compaction/compaction.js +83 -29
  26. package/dist/core/compaction/compaction.js.map +1 -1
  27. package/dist/core/compaction/utils.d.ts +1 -1
  28. package/dist/core/compaction/utils.d.ts.map +1 -1
  29. package/dist/core/compaction/utils.js +6 -17
  30. package/dist/core/compaction/utils.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +13 -2
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/extensions/runner.d.ts +2 -1
  35. package/dist/core/extensions/runner.d.ts.map +1 -1
  36. package/dist/core/extensions/runner.js +35 -0
  37. package/dist/core/extensions/runner.js.map +1 -1
  38. package/dist/core/extensions/types.d.ts +20 -2
  39. package/dist/core/extensions/types.d.ts.map +1 -1
  40. package/dist/core/extensions/types.js.map +1 -1
  41. package/dist/core/model-config.d.ts +30 -0
  42. package/dist/core/model-config.d.ts.map +1 -1
  43. package/dist/core/model-config.js +6 -0
  44. package/dist/core/model-config.js.map +1 -1
  45. package/dist/core/model-registry.d.ts +5 -1
  46. package/dist/core/model-registry.d.ts.map +1 -1
  47. package/dist/core/model-registry.js +19 -4
  48. package/dist/core/model-registry.js.map +1 -1
  49. package/dist/core/model-resolver.d.ts.map +1 -1
  50. package/dist/core/model-resolver.js +9 -0
  51. package/dist/core/model-resolver.js.map +1 -1
  52. package/dist/core/model-runtime.d.ts +7 -3
  53. package/dist/core/model-runtime.d.ts.map +1 -1
  54. package/dist/core/model-runtime.js +42 -21
  55. package/dist/core/model-runtime.js.map +1 -1
  56. package/dist/core/prompt-templates.d.ts +1 -0
  57. package/dist/core/prompt-templates.d.ts.map +1 -1
  58. package/dist/core/prompt-templates.js +4 -4
  59. package/dist/core/prompt-templates.js.map +1 -1
  60. package/dist/core/remote-catalog-provider.d.ts +1 -1
  61. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  62. package/dist/core/remote-catalog-provider.js +21 -7
  63. package/dist/core/remote-catalog-provider.js.map +1 -1
  64. package/dist/core/resource-loader.d.ts.map +1 -1
  65. package/dist/core/resource-loader.js +1 -0
  66. package/dist/core/resource-loader.js.map +1 -1
  67. package/dist/core/sdk.d.ts.map +1 -1
  68. package/dist/core/sdk.js +6 -2
  69. package/dist/core/sdk.js.map +1 -1
  70. package/dist/core/session-manager.d.ts +9 -4
  71. package/dist/core/session-manager.d.ts.map +1 -1
  72. package/dist/core/session-manager.js +99 -23
  73. package/dist/core/session-manager.js.map +1 -1
  74. package/dist/core/settings-manager.d.ts +1 -1
  75. package/dist/core/settings-manager.d.ts.map +1 -1
  76. package/dist/core/settings-manager.js.map +1 -1
  77. package/dist/core/system-prompt.d.ts.map +1 -1
  78. package/dist/core/system-prompt.js +1 -1
  79. package/dist/core/system-prompt.js.map +1 -1
  80. package/dist/core/tools/bash.d.ts +2 -0
  81. package/dist/core/tools/bash.d.ts.map +1 -1
  82. package/dist/core/tools/bash.js +34 -5
  83. package/dist/core/tools/bash.js.map +1 -1
  84. package/dist/core/tools/read.d.ts.map +1 -1
  85. package/dist/core/tools/read.js +1 -1
  86. package/dist/core/tools/read.js.map +1 -1
  87. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  88. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  89. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  90. package/dist/core/usage-totals.d.ts +19 -0
  91. package/dist/core/usage-totals.d.ts.map +1 -0
  92. package/dist/core/usage-totals.js +52 -0
  93. package/dist/core/usage-totals.js.map +1 -0
  94. package/dist/extensions/index.d.ts +3 -0
  95. package/dist/extensions/index.d.ts.map +1 -0
  96. package/dist/extensions/index.js +3 -0
  97. package/dist/extensions/index.js.map +1 -0
  98. package/dist/extensions/llama/client.d.ts +61 -0
  99. package/dist/extensions/llama/client.d.ts.map +1 -0
  100. package/dist/extensions/llama/client.js +302 -0
  101. package/dist/extensions/llama/client.js.map +1 -0
  102. package/dist/extensions/llama/huggingface.d.ts +23 -0
  103. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  104. package/dist/extensions/llama/huggingface.js +141 -0
  105. package/dist/extensions/llama/huggingface.js.map +1 -0
  106. package/dist/extensions/llama/index.d.ts +3 -0
  107. package/dist/extensions/llama/index.d.ts.map +1 -0
  108. package/dist/extensions/llama/index.js +208 -0
  109. package/dist/extensions/llama/index.js.map +1 -0
  110. package/dist/extensions/llama/provider.d.ts +10 -0
  111. package/dist/extensions/llama/provider.d.ts.map +1 -0
  112. package/dist/extensions/llama/provider.js +101 -0
  113. package/dist/extensions/llama/provider.js.map +1 -0
  114. package/dist/extensions/llama/ui.d.ts +42 -0
  115. package/dist/extensions/llama/ui.d.ts.map +1 -0
  116. package/dist/extensions/llama/ui.js +416 -0
  117. package/dist/extensions/llama/ui.js.map +1 -0
  118. package/dist/index.d.ts +2 -2
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +1 -1
  121. package/dist/index.js.map +1 -1
  122. package/dist/main.d.ts.map +1 -1
  123. package/dist/main.js +10 -4
  124. package/dist/main.js.map +1 -1
  125. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  126. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  127. package/dist/modes/interactive/components/extension-editor.js +16 -46
  128. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  129. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/footer.js +24 -23
  131. package/dist/modes/interactive/components/footer.js.map +1 -1
  132. package/dist/modes/interactive/external-editor.d.ts +12 -0
  133. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  134. package/dist/modes/interactive/external-editor.js +37 -0
  135. package/dist/modes/interactive/external-editor.js.map +1 -0
  136. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  137. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  138. package/dist/modes/interactive/interactive-mode.js +72 -68
  139. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  140. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  141. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  142. package/dist/modes/rpc/rpc-client.js +7 -0
  143. package/dist/modes/rpc/rpc-client.js.map +1 -1
  144. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.js +5 -0
  146. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  147. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  148. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  149. package/dist/modes/rpc/rpc-types.js.map +1 -1
  150. package/dist/utils/clipboard.d.ts.map +1 -1
  151. package/dist/utils/clipboard.js +19 -8
  152. package/dist/utils/clipboard.js.map +1 -1
  153. package/dist/utils/version-check.d.ts.map +1 -1
  154. package/dist/utils/version-check.js +3 -1
  155. package/dist/utils/version-check.js.map +1 -1
  156. package/docs/compaction.md +9 -4
  157. package/docs/custom-provider.md +33 -2
  158. package/docs/environment-variables.md +88 -0
  159. package/docs/extensions.md +61 -11
  160. package/docs/index.md +2 -0
  161. package/docs/json.md +5 -1
  162. package/docs/llama-cpp.md +99 -0
  163. package/docs/models.md +6 -2
  164. package/docs/prompt-templates.md +1 -0
  165. package/docs/providers.md +19 -1
  166. package/docs/rpc.md +104 -8
  167. package/docs/sdk.md +3 -0
  168. package/docs/session-format.md +17 -3
  169. package/docs/settings.md +1 -1
  170. package/docs/tui.md +41 -26
  171. package/docs/usage.md +2 -14
  172. package/examples/extensions/README.md +1 -1
  173. package/examples/extensions/custom-compaction.ts +4 -0
  174. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  175. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  176. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  177. package/examples/extensions/gondolin/package-lock.json +2 -2
  178. package/examples/extensions/gondolin/package.json +1 -1
  179. package/examples/extensions/handoff.ts +9 -1
  180. package/examples/extensions/sandbox/package-lock.json +2 -2
  181. package/examples/extensions/sandbox/package.json +1 -1
  182. package/examples/extensions/summarize.ts +3 -0
  183. package/examples/extensions/with-deps/package-lock.json +2 -2
  184. package/examples/extensions/with-deps/package.json +1 -1
  185. package/package.json +5 -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";
@@ -36,6 +37,7 @@ import { buildSystemPrompt } from "./system-prompt.js";
36
37
  import { createLocalBashOperations } from "./tools/bash.js";
37
38
  import { createAllToolDefinitions } from "./tools/index.js";
38
39
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
40
+ import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
39
41
  /**
40
42
  * Parse a skill block from message text.
41
43
  * Returns null if the text doesn't contain a skill block.
@@ -286,7 +288,7 @@ export class AgentSession {
286
288
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
287
289
  }
288
290
  async _getSummarizationRequestAuth(model) {
289
- if (this.agent.streamFn === streamSimple) {
291
+ if (this.agent.streamFunction === streamSimple) {
290
292
  return this._getRequiredRequestAuth(model);
291
293
  }
292
294
  try {
@@ -404,6 +406,7 @@ export class AgentSession {
404
406
  content: result.content,
405
407
  details: result.details,
406
408
  isError,
409
+ usage: result.usage,
407
410
  })
408
411
  : undefined;
409
412
  const finalContent = hookResult?.content ?? result.content;
@@ -416,6 +419,7 @@ export class AgentSession {
416
419
  content: hookResult.content,
417
420
  details: hookResult.details,
418
421
  isError: hookResult.isError ?? isError,
422
+ usage: hookResult.usage,
419
423
  };
420
424
  };
421
425
  }
@@ -492,7 +496,7 @@ export class AgentSession {
492
496
  // This ensures the UI sees the updated queue state
493
497
  if (event.type === "message_start" && event.message.role === "user") {
494
498
  this._overflowRecoveryAttempted = false;
495
- const messageText = this._getUserMessageText(event.message);
499
+ const messageText = contentText(event.message.content, "");
496
500
  if (messageText) {
497
501
  // Check steering queue first
498
502
  const steeringIndex = this._steeringMessages.indexOf(messageText);
@@ -568,16 +572,6 @@ export class AgentSession {
568
572
  }
569
573
  return false;
570
574
  }
571
- /** Extract text content from a message */
572
- _getUserMessageText(message) {
573
- if (message.role !== "user")
574
- return "";
575
- const content = message.content;
576
- if (typeof content === "string")
577
- return content;
578
- const textBlocks = content.filter((c) => c.type === "text");
579
- return textBlocks.map((c) => c.text).join("");
580
- }
581
575
  /** Find the last assistant message in agent state (including aborted ones) */
582
576
  _findLastAssistantMessage() {
583
577
  const messages = this.agent.state.messages;
@@ -1042,7 +1036,7 @@ export class AgentSession {
1042
1036
  if (!model || !transcript)
1043
1037
  return;
1044
1038
  try {
1045
- const stream = await this.agent.streamFn(model, {
1039
+ const stream = await this.agent.streamFunction(model, {
1046
1040
  systemPrompt: this.systemPrompt,
1047
1041
  messages: [
1048
1042
  {
@@ -1691,8 +1685,17 @@ export class AgentSession {
1691
1685
  this.sessionManager.replaceTree(result.entries, result.leafId);
1692
1686
  const sessionContext = this.sessionManager.buildSessionContext();
1693
1687
  this.agent.state.messages = sessionContext.messages;
1688
+ const firstKeptEntryId = result.compaction.firstKeptEntryId;
1689
+ if (!firstKeptEntryId) {
1690
+ throw new Error("Pi-server compaction result is missing firstKeptEntryId");
1691
+ }
1692
+ const compaction = result.compaction;
1694
1693
  const compactionResult = {
1695
- ...result.compaction,
1694
+ summary: compaction.summary,
1695
+ firstKeptEntryId,
1696
+ tokensBefore: compaction.tokensBefore,
1697
+ usage: compaction.usage,
1698
+ details: compaction.details,
1696
1699
  estimatedTokensAfter: estimateMessagesTokens(sessionContext.messages),
1697
1700
  };
1698
1701
  const savedCompactionEntry = this.sessionManager.getEntry(result.compactionEntry.id);
@@ -1784,26 +1787,29 @@ export class AgentSession {
1784
1787
  let summary;
1785
1788
  let firstKeptEntryId;
1786
1789
  let tokensBefore;
1790
+ let usage;
1787
1791
  let details;
1788
1792
  if (extensionCompaction) {
1789
1793
  // Extension provided compaction content
1790
1794
  summary = extensionCompaction.summary;
1791
1795
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
1792
1796
  tokensBefore = extensionCompaction.tokensBefore;
1797
+ usage = extensionCompaction.usage;
1793
1798
  details = extensionCompaction.details;
1794
1799
  }
1795
1800
  else {
1796
1801
  // Generate compaction result
1797
- const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
1802
+ const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
1798
1803
  summary = result.summary;
1799
1804
  firstKeptEntryId = result.firstKeptEntryId;
1800
1805
  tokensBefore = result.tokensBefore;
1806
+ usage = result.usage;
1801
1807
  details = result.details;
1802
1808
  }
1803
1809
  if (this._compactionAbortController.signal.aborted) {
1804
1810
  throw new Error("Compaction cancelled");
1805
1811
  }
1806
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
1812
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1807
1813
  const newEntries = this.sessionManager.getEntries();
1808
1814
  const sessionContext = this.sessionManager.buildSessionContext();
1809
1815
  this.agent.state.messages = sessionContext.messages;
@@ -1824,6 +1830,7 @@ export class AgentSession {
1824
1830
  firstKeptEntryId,
1825
1831
  tokensBefore,
1826
1832
  estimatedTokensAfter,
1833
+ usage,
1827
1834
  details,
1828
1835
  };
1829
1836
  this._emit({
@@ -1980,7 +1987,7 @@ export class AgentSession {
1980
1987
  let apiKey;
1981
1988
  let headers;
1982
1989
  let env;
1983
- if (this.agent.streamFn === streamSimple) {
1990
+ if (this.agent.streamFunction === streamSimple) {
1984
1991
  const authResult = await this._modelRuntime.getAuth(this.model);
1985
1992
  if (!authResult?.auth.apiKey)
1986
1993
  return false;
@@ -2054,20 +2061,23 @@ export class AgentSession {
2054
2061
  let summary;
2055
2062
  let firstKeptEntryId;
2056
2063
  let tokensBefore;
2064
+ let usage;
2057
2065
  let details;
2058
2066
  if (extensionCompaction) {
2059
2067
  // Extension provided compaction content
2060
2068
  summary = extensionCompaction.summary;
2061
2069
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
2062
2070
  tokensBefore = extensionCompaction.tokensBefore;
2071
+ usage = extensionCompaction.usage;
2063
2072
  details = extensionCompaction.details;
2064
2073
  }
2065
2074
  else {
2066
2075
  // Generate compaction result
2067
- const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
2076
+ const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
2068
2077
  summary = compactResult.summary;
2069
2078
  firstKeptEntryId = compactResult.firstKeptEntryId;
2070
2079
  tokensBefore = compactResult.tokensBefore;
2080
+ usage = compactResult.usage;
2071
2081
  details = compactResult.details;
2072
2082
  }
2073
2083
  if (this._autoCompactionAbortController.signal.aborted) {
@@ -2080,7 +2090,7 @@ export class AgentSession {
2080
2090
  });
2081
2091
  return false;
2082
2092
  }
2083
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
2093
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
2084
2094
  const newEntries = this.sessionManager.getEntries();
2085
2095
  const sessionContext = this.sessionManager.buildSessionContext();
2086
2096
  this.agent.state.messages = sessionContext.messages;
@@ -2101,6 +2111,7 @@ export class AgentSession {
2101
2111
  firstKeptEntryId,
2102
2112
  tokensBefore,
2103
2113
  estimatedTokensAfter,
2114
+ usage,
2104
2115
  details,
2105
2116
  };
2106
2117
  this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
@@ -2388,6 +2399,10 @@ export class AgentSession {
2388
2399
  this._modelRuntime.registerProvider(name, config);
2389
2400
  this._refreshCurrentModelFromRegistry();
2390
2401
  },
2402
+ registerNativeProvider: (provider) => {
2403
+ this._modelRuntime.registerNativeProvider(provider);
2404
+ this._refreshCurrentModelFromRegistry();
2405
+ },
2391
2406
  unregisterProvider: (name) => {
2392
2407
  this._modelRuntime.unregisterProvider(name);
2393
2408
  this._refreshCurrentModelFromRegistry();
@@ -2551,6 +2566,34 @@ export class AgentSession {
2551
2566
  return false;
2552
2567
  return isRetryableAssistantError(message);
2553
2568
  }
2569
+ /**
2570
+ * Retry policy + callbacks shared by compaction and branch-summary summarization calls.
2571
+ * Uses the same `settings.retry` budget/backoff as agent-turn retries so a single transient
2572
+ * stream drop no longer fails the whole operation. `source` carries the context
2573
+ * the TUI needs to render the retry and recreate the underlying indicator.
2574
+ */
2575
+ _summarizationRetryCallbacks(source) {
2576
+ return {
2577
+ onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => {
2578
+ this._emit({
2579
+ type: "summarization_retry_scheduled",
2580
+ attempt,
2581
+ maxAttempts,
2582
+ delayMs,
2583
+ errorMessage,
2584
+ });
2585
+ },
2586
+ onRetryAttemptStart: () => {
2587
+ this._emit({
2588
+ type: "summarization_retry_attempt_start",
2589
+ ...source,
2590
+ });
2591
+ },
2592
+ onRetryFinished: () => {
2593
+ this._emit({ type: "summarization_retry_finished" });
2594
+ },
2595
+ };
2596
+ }
2554
2597
  /**
2555
2598
  * Prepare a retryable error for continuation with exponential backoff.
2556
2599
  * @returns true if the caller should continue the agent, false otherwise
@@ -2647,6 +2690,7 @@ export class AgentSession {
2647
2690
  * @param command The bash command to execute
2648
2691
  * @param onChunk Optional streaming callback for output
2649
2692
  * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
2693
+ * @param options.id Optional identifier included in bash execution update events
2650
2694
  * @param options.operations Custom BashOperations for remote execution
2651
2695
  */
2652
2696
  async executeBash(command, onChunk, options) {
@@ -2657,7 +2701,10 @@ export class AgentSession {
2657
2701
  const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
2658
2702
  try {
2659
2703
  const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
2660
- onChunk,
2704
+ onChunk: (delta) => {
2705
+ onChunk?.(delta);
2706
+ this._emit({ type: "bash_execution_update", id: options?.id, delta });
2707
+ },
2661
2708
  signal: this._bashAbortController.signal,
2662
2709
  });
2663
2710
  this.recordBashResult(command, result, options);
@@ -2813,6 +2860,7 @@ export class AgentSession {
2813
2860
  // Run default summarizer if needed
2814
2861
  let summaryText;
2815
2862
  let summaryDetails;
2863
+ let summaryUsage;
2816
2864
  if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
2817
2865
  const model = this.model;
2818
2866
  const { apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
@@ -2826,7 +2874,9 @@ export class AgentSession {
2826
2874
  customInstructions,
2827
2875
  replaceInstructions,
2828
2876
  reserveTokens: branchSummarySettings.reserveTokens,
2829
- streamFn: this.agent.streamFn,
2877
+ streamFn: this.agent.streamFunction,
2878
+ retry: this.settingsManager.getRetrySettings(),
2879
+ callbacks: this._summarizationRetryCallbacks({ source: "branchSummary" }),
2830
2880
  });
2831
2881
  if (result.aborted) {
2832
2882
  return { cancelled: true, aborted: true };
@@ -2835,6 +2885,7 @@ export class AgentSession {
2835
2885
  throw new Error(result.error);
2836
2886
  }
2837
2887
  summaryText = result.summary;
2888
+ summaryUsage = result.usage;
2838
2889
  summaryDetails = {
2839
2890
  readFiles: result.readFiles || [],
2840
2891
  modifiedFiles: result.modifiedFiles || [],
@@ -2843,6 +2894,7 @@ export class AgentSession {
2843
2894
  else if (extensionSummary) {
2844
2895
  summaryText = extensionSummary.summary;
2845
2896
  summaryDetails = extensionSummary.details;
2897
+ summaryUsage = extensionSummary.usage;
2846
2898
  }
2847
2899
  // Determine the new leaf position based on target type
2848
2900
  let newLeafId;
@@ -2850,18 +2902,12 @@ export class AgentSession {
2850
2902
  if (targetEntry.type === "message" && targetEntry.message.role === "user") {
2851
2903
  // User message: leaf = parent (null if root), text goes to editor
2852
2904
  newLeafId = targetEntry.parentId;
2853
- editorText = this._extractUserMessageText(targetEntry.message.content);
2905
+ editorText = contentText(targetEntry.message.content, "");
2854
2906
  }
2855
2907
  else if (targetEntry.type === "custom_message") {
2856
2908
  // Custom message: leaf = parent (null if root), text goes to editor
2857
2909
  newLeafId = targetEntry.parentId;
2858
- editorText =
2859
- typeof targetEntry.content === "string"
2860
- ? targetEntry.content
2861
- : targetEntry.content
2862
- .filter((c) => c.type === "text")
2863
- .map((c) => c.text)
2864
- .join("");
2910
+ editorText = contentText(targetEntry.content, "");
2865
2911
  }
2866
2912
  else {
2867
2913
  // Non-user message: leaf = selected node
@@ -2872,7 +2918,7 @@ export class AgentSession {
2872
2918
  let summaryEntry;
2873
2919
  if (summaryText) {
2874
2920
  // Create summary at target position (can be null for root)
2875
- const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
2921
+ const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension, summaryUsage);
2876
2922
  summaryEntry = this.sessionManager.getEntry(summaryId);
2877
2923
  // Attach label to the summary entry
2878
2924
  if (label) {
@@ -2927,24 +2973,13 @@ export class AgentSession {
2927
2973
  continue;
2928
2974
  if (entry.message.role !== "user")
2929
2975
  continue;
2930
- const text = this._extractUserMessageText(entry.message.content);
2976
+ const text = contentText(entry.message.content, "");
2931
2977
  if (text) {
2932
2978
  result.push({ entryId: entry.id, text });
2933
2979
  }
2934
2980
  }
2935
2981
  return result;
2936
2982
  }
2937
- _extractUserMessageText(content) {
2938
- if (typeof content === "string")
2939
- return content;
2940
- if (Array.isArray(content)) {
2941
- return content
2942
- .filter((c) => c.type === "text")
2943
- .map((c) => c.text)
2944
- .join("");
2945
- }
2946
- return "";
2947
- }
2948
2983
  /**
2949
2984
  * Get session statistics. Aggregates over ALL session entries (including
2950
2985
  * history that was compacted away), so token/cost totals reflect what was
@@ -2956,12 +2991,11 @@ export class AgentSession {
2956
2991
  let toolResults = 0;
2957
2992
  let totalMessages = 0;
2958
2993
  let toolCalls = 0;
2959
- let totalInput = 0;
2960
- let totalOutput = 0;
2961
- let totalCacheRead = 0;
2962
- let totalCacheWrite = 0;
2963
- let totalCost = 0;
2994
+ const usageTotals = createUsageTotals();
2964
2995
  for (const entry of this.sessionManager.getEntries()) {
2996
+ if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
2997
+ addUsageToTotals(usageTotals, entry.usage);
2998
+ }
2965
2999
  if (entry.type !== "message")
2966
3000
  continue;
2967
3001
  totalMessages++;
@@ -2971,6 +3005,9 @@ export class AgentSession {
2971
3005
  }
2972
3006
  else if (message.role === "toolResult") {
2973
3007
  toolResults++;
3008
+ if (message.usage) {
3009
+ addUsageToTotals(usageTotals, message.usage);
3010
+ }
2974
3011
  }
2975
3012
  else if (message.role === "assistant") {
2976
3013
  assistantMessages++;
@@ -2978,12 +3015,7 @@ export class AgentSession {
2978
3015
  if (Array.isArray(assistantMsg.content)) {
2979
3016
  toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
2980
3017
  }
2981
- const usage = assistantMsg.usage;
2982
- totalInput += usage.input;
2983
- totalOutput += usage.output;
2984
- totalCacheRead += usage.cacheRead;
2985
- totalCacheWrite += usage.cacheWrite;
2986
- totalCost += usage.cost.total;
3018
+ addUsageToTotals(usageTotals, assistantMsg.usage);
2987
3019
  }
2988
3020
  }
2989
3021
  return {
@@ -2995,13 +3027,13 @@ export class AgentSession {
2995
3027
  toolResults,
2996
3028
  totalMessages,
2997
3029
  tokens: {
2998
- input: totalInput,
2999
- output: totalOutput,
3000
- cacheRead: totalCacheRead,
3001
- cacheWrite: totalCacheWrite,
3002
- total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
3030
+ input: usageTotals.input,
3031
+ output: usageTotals.output,
3032
+ cacheRead: usageTotals.cacheRead,
3033
+ cacheWrite: usageTotals.cacheWrite,
3034
+ total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite,
3003
3035
  },
3004
- cost: totalCost,
3036
+ cost: usageTotals.cost,
3005
3037
  contextUsage: this.getContextUsage(),
3006
3038
  };
3007
3039
  }