@selesai/code 0.5.13 → 0.5.15

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 (297) hide show
  1. package/README.md +186 -0
  2. package/dist/bun/cli.d.ts.map +1 -1
  3. package/dist/bun/cli.js +2 -0
  4. package/dist/bun/cli.js.map +1 -1
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +3 -1
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/list-models.d.ts +2 -2
  9. package/dist/cli/list-models.d.ts.map +1 -1
  10. package/dist/cli/list-models.js +3 -3
  11. package/dist/cli/list-models.js.map +1 -1
  12. package/dist/config.d.ts +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +2 -2
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session-auto-handoff.test.d.ts.map +1 -1
  17. package/dist/core/agent-session-auto-handoff.test.js +2 -2
  18. package/dist/core/agent-session-auto-handoff.test.js.map +1 -1
  19. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  20. package/dist/core/agent-session-runtime.js +3 -0
  21. package/dist/core/agent-session-runtime.js.map +1 -1
  22. package/dist/core/agent-session-services.d.ts +3 -6
  23. package/dist/core/agent-session-services.d.ts.map +1 -1
  24. package/dist/core/agent-session-services.js +23 -9
  25. package/dist/core/agent-session-services.js.map +1 -1
  26. package/dist/core/agent-session.d.ts +28 -10
  27. package/dist/core/agent-session.d.ts.map +1 -1
  28. package/dist/core/agent-session.js +136 -90
  29. package/dist/core/agent-session.js.map +1 -1
  30. package/dist/core/auth-storage.d.ts +20 -98
  31. package/dist/core/auth-storage.d.ts.map +1 -1
  32. package/dist/core/auth-storage.js +59 -251
  33. package/dist/core/auth-storage.js.map +1 -1
  34. package/dist/core/built-in-extensions.d.ts +3 -0
  35. package/dist/core/built-in-extensions.d.ts.map +1 -0
  36. package/dist/core/built-in-extensions.js +3 -0
  37. package/dist/core/built-in-extensions.js.map +1 -0
  38. package/dist/core/cache-stats.d.ts +2 -2
  39. package/dist/core/cache-stats.d.ts.map +1 -1
  40. package/dist/core/cache-stats.js +1 -1
  41. package/dist/core/cache-stats.js.map +1 -1
  42. package/dist/core/compaction/branch-summarization.d.ts +8 -2
  43. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  44. package/dist/core/compaction/branch-summarization.js +8 -11
  45. package/dist/core/compaction/branch-summarization.js.map +1 -1
  46. package/dist/core/compaction/compaction.d.ts +19 -3
  47. package/dist/core/compaction/compaction.d.ts.map +1 -1
  48. package/dist/core/compaction/compaction.js +63 -26
  49. package/dist/core/compaction/compaction.js.map +1 -1
  50. package/dist/core/compaction/utils.d.ts +1 -1
  51. package/dist/core/compaction/utils.d.ts.map +1 -1
  52. package/dist/core/compaction/utils.js +6 -17
  53. package/dist/core/compaction/utils.js.map +1 -1
  54. package/dist/core/extensions/loader.d.ts.map +1 -1
  55. package/dist/core/extensions/loader.js +21 -4
  56. package/dist/core/extensions/loader.js.map +1 -1
  57. package/dist/core/extensions/runner.d.ts +4 -1
  58. package/dist/core/extensions/runner.d.ts.map +1 -1
  59. package/dist/core/extensions/runner.js +39 -0
  60. package/dist/core/extensions/runner.js.map +1 -1
  61. package/dist/core/extensions/types.d.ts +24 -3
  62. package/dist/core/extensions/types.d.ts.map +1 -1
  63. package/dist/core/extensions/types.js.map +1 -1
  64. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  65. package/dist/core/extensions/wrapper.js +22 -3
  66. package/dist/core/extensions/wrapper.js.map +1 -1
  67. package/dist/core/keybindings.d.ts +8 -3
  68. package/dist/core/keybindings.d.ts.map +1 -1
  69. package/dist/core/keybindings.js +7 -3
  70. package/dist/core/keybindings.js.map +1 -1
  71. package/dist/core/llama/client.d.ts +61 -0
  72. package/dist/core/llama/client.d.ts.map +1 -0
  73. package/dist/core/llama/client.js +302 -0
  74. package/dist/core/llama/client.js.map +1 -0
  75. package/dist/core/llama/huggingface.d.ts +23 -0
  76. package/dist/core/llama/huggingface.d.ts.map +1 -0
  77. package/dist/core/llama/huggingface.js +141 -0
  78. package/dist/core/llama/huggingface.js.map +1 -0
  79. package/dist/core/llama/index.d.ts +3 -0
  80. package/dist/core/llama/index.d.ts.map +1 -0
  81. package/dist/core/llama/index.js +208 -0
  82. package/dist/core/llama/index.js.map +1 -0
  83. package/dist/core/llama/provider.d.ts +10 -0
  84. package/dist/core/llama/provider.d.ts.map +1 -0
  85. package/dist/core/llama/provider.js +102 -0
  86. package/dist/core/llama/provider.js.map +1 -0
  87. package/dist/core/llama/ui.d.ts +42 -0
  88. package/dist/core/llama/ui.d.ts.map +1 -0
  89. package/dist/core/llama/ui.js +416 -0
  90. package/dist/core/llama/ui.js.map +1 -0
  91. package/dist/core/model-config.d.ts +513 -0
  92. package/dist/core/model-config.d.ts.map +1 -0
  93. package/dist/core/model-config.js +272 -0
  94. package/dist/core/model-config.js.map +1 -0
  95. package/dist/core/model-registry.d.ts +20 -126
  96. package/dist/core/model-registry.d.ts.map +1 -1
  97. package/dist/core/model-registry.js +62 -780
  98. package/dist/core/model-registry.js.map +1 -1
  99. package/dist/core/model-resolver.d.ts +6 -6
  100. package/dist/core/model-resolver.d.ts.map +1 -1
  101. package/dist/core/model-resolver.js +21 -18
  102. package/dist/core/model-resolver.js.map +1 -1
  103. package/dist/core/model-runtime.d.ts +83 -0
  104. package/dist/core/model-runtime.d.ts.map +1 -0
  105. package/dist/core/model-runtime.js +451 -0
  106. package/dist/core/model-runtime.js.map +1 -0
  107. package/dist/core/models-store.d.ts +17 -0
  108. package/dist/core/models-store.d.ts.map +1 -0
  109. package/dist/core/models-store.js +45 -0
  110. package/dist/core/models-store.js.map +1 -0
  111. package/dist/core/package-manager.d.ts.map +1 -1
  112. package/dist/core/package-manager.js +7 -2
  113. package/dist/core/package-manager.js.map +1 -1
  114. package/dist/core/prompt-templates.d.ts +1 -0
  115. package/dist/core/prompt-templates.d.ts.map +1 -1
  116. package/dist/core/prompt-templates.js +4 -4
  117. package/dist/core/prompt-templates.js.map +1 -1
  118. package/dist/core/provider-composer.d.ts +55 -0
  119. package/dist/core/provider-composer.d.ts.map +1 -0
  120. package/dist/core/provider-composer.js +375 -0
  121. package/dist/core/provider-composer.js.map +1 -0
  122. package/dist/core/radius.d.ts +2 -0
  123. package/dist/core/radius.d.ts.map +1 -0
  124. package/dist/core/radius.js +2 -0
  125. package/dist/core/radius.js.map +1 -0
  126. package/dist/core/remote-catalog-provider.d.ts +5 -0
  127. package/dist/core/remote-catalog-provider.d.ts.map +1 -0
  128. package/dist/core/remote-catalog-provider.js +102 -0
  129. package/dist/core/remote-catalog-provider.js.map +1 -0
  130. package/dist/core/resource-loader.d.ts.map +1 -1
  131. package/dist/core/resource-loader.js +1 -0
  132. package/dist/core/resource-loader.js.map +1 -1
  133. package/dist/core/runtime-credentials.d.ts +17 -0
  134. package/dist/core/runtime-credentials.d.ts.map +1 -0
  135. package/dist/core/runtime-credentials.js +42 -0
  136. package/dist/core/runtime-credentials.js.map +1 -0
  137. package/dist/core/sdk.d.ts +4 -7
  138. package/dist/core/sdk.d.ts.map +1 -1
  139. package/dist/core/sdk.js +18 -25
  140. package/dist/core/sdk.js.map +1 -1
  141. package/dist/core/session-manager.d.ts +12 -7
  142. package/dist/core/session-manager.d.ts.map +1 -1
  143. package/dist/core/session-manager.js +103 -27
  144. package/dist/core/session-manager.js.map +1 -1
  145. package/dist/core/system-prompt.d.ts.map +1 -1
  146. package/dist/core/system-prompt.js +2 -12
  147. package/dist/core/system-prompt.js.map +1 -1
  148. package/dist/core/tools/read.d.ts.map +1 -1
  149. package/dist/core/tools/read.js +1 -1
  150. package/dist/core/tools/read.js.map +1 -1
  151. package/dist/core/usage-totals.d.ts +19 -0
  152. package/dist/core/usage-totals.d.ts.map +1 -0
  153. package/dist/core/usage-totals.js +52 -0
  154. package/dist/core/usage-totals.js.map +1 -0
  155. package/dist/extensions/index.d.ts +3 -0
  156. package/dist/extensions/index.d.ts.map +1 -0
  157. package/dist/extensions/index.js +9 -0
  158. package/dist/extensions/index.js.map +1 -0
  159. package/dist/extensions/llama/client.d.ts +61 -0
  160. package/dist/extensions/llama/client.d.ts.map +1 -0
  161. package/dist/extensions/llama/client.js +309 -0
  162. package/dist/extensions/llama/client.js.map +1 -0
  163. package/dist/extensions/llama/huggingface.d.ts +23 -0
  164. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  165. package/dist/extensions/llama/huggingface.js +146 -0
  166. package/dist/extensions/llama/huggingface.js.map +1 -0
  167. package/dist/extensions/llama/index.d.ts +3 -0
  168. package/dist/extensions/llama/index.d.ts.map +1 -0
  169. package/dist/extensions/llama/index.js +211 -0
  170. package/dist/extensions/llama/index.js.map +1 -0
  171. package/dist/extensions/llama/provider.d.ts +10 -0
  172. package/dist/extensions/llama/provider.d.ts.map +1 -0
  173. package/dist/extensions/llama/provider.js +106 -0
  174. package/dist/extensions/llama/provider.js.map +1 -0
  175. package/dist/extensions/llama/ui.d.ts +42 -0
  176. package/dist/extensions/llama/ui.d.ts.map +1 -0
  177. package/dist/extensions/llama/ui.js +420 -0
  178. package/dist/extensions/llama/ui.js.map +1 -0
  179. package/dist/extensions/package.json +4 -3
  180. package/dist/extensions/pi-powerline-footer/README.md +1 -1
  181. package/dist/extensions/pi-powerline-footer/guide.ts +227 -0
  182. package/dist/extensions/pi-powerline-footer/index.ts +88 -55
  183. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -78
  184. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +102 -0
  185. package/dist/extensions/pi-powerline-footer/welcome.ts +65 -30
  186. package/dist/extensions/tokenin-onboarding.ts +3 -3
  187. package/dist/index.d.ts +4 -3
  188. package/dist/index.d.ts.map +1 -1
  189. package/dist/index.js +3 -3
  190. package/dist/index.js.map +1 -1
  191. package/dist/main.d.ts.map +1 -1
  192. package/dist/main.js +21 -19
  193. package/dist/main.js.map +1 -1
  194. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/assistant-message.js +22 -10
  196. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  197. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/custom-editor.js +1 -1
  199. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  200. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/footer.js +24 -23
  202. package/dist/modes/interactive/components/footer.js.map +1 -1
  203. package/dist/modes/interactive/components/login-dialog.d.ts +5 -5
  204. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  205. package/dist/modes/interactive/components/login-dialog.js +17 -8
  206. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  207. package/dist/modes/interactive/components/model-selector.d.ts +11 -4
  208. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  209. package/dist/modes/interactive/components/model-selector.js +72 -41
  210. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  211. package/dist/modes/interactive/components/oauth-selector.d.ts +4 -4
  212. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  213. package/dist/modes/interactive/components/oauth-selector.js +14 -27
  214. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  215. package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
  216. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/tree-selector.js +49 -12
  218. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  219. package/dist/modes/interactive/interactive-mode.d.ts +8 -5
  220. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  221. package/dist/modes/interactive/interactive-mode.js +253 -225
  222. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  223. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  224. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  225. package/dist/modes/rpc/rpc-client.js +7 -0
  226. package/dist/modes/rpc/rpc-client.js.map +1 -1
  227. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  228. package/dist/modes/rpc/rpc-mode.js +6 -2
  229. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  230. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  231. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  232. package/dist/modes/rpc/rpc-types.js.map +1 -1
  233. package/dist/package-manager-cli.d.ts.map +1 -1
  234. package/dist/package-manager-cli.js +67 -5
  235. package/dist/package-manager-cli.js.map +1 -1
  236. package/dist/skills/caveman/SKILL.md +1 -0
  237. package/dist/skills/handoff/SKILL.md +1 -0
  238. package/dist/skills/handoff-text/SKILL.md +1 -0
  239. package/dist/skills/implanger/SKILL.md +1 -0
  240. package/dist/skills/improve-codebase/SKILL.md +1 -0
  241. package/dist/skills/planger/SKILL.md +1 -0
  242. package/dist/skills/ponytail/SKILL.md +1 -0
  243. package/dist/skills/ponytail-audit/SKILL.md +1 -0
  244. package/dist/skills/ponytail-debt/SKILL.md +1 -0
  245. package/dist/skills/ponytail-gain/SKILL.md +1 -0
  246. package/dist/skills/ponytail-help/SKILL.md +1 -0
  247. package/dist/skills/ponytail-review/SKILL.md +1 -0
  248. package/dist/skills/workflow-creation/SKILL.md +1 -0
  249. package/dist/utils/clipboard-native.d.ts +1 -0
  250. package/dist/utils/clipboard-native.d.ts.map +1 -1
  251. package/dist/utils/clipboard-native.js.map +1 -1
  252. package/dist/utils/clipboard.d.ts +2 -0
  253. package/dist/utils/clipboard.d.ts.map +1 -1
  254. package/dist/utils/clipboard.js +13 -0
  255. package/dist/utils/clipboard.js.map +1 -1
  256. package/package.json +26 -5
  257. package/dist/agents/architect.md +0 -217
  258. package/dist/agents/builder.md +0 -120
  259. package/dist/agents/commentator.md +0 -130
  260. package/dist/agents/explorer.md +0 -51
  261. package/dist/agents/recapper.md +0 -26
  262. package/dist/extensions/gitignore-guard.ts +0 -132
  263. package/dist/extensions/hooks/claude-codex-hooks.json +0 -44
  264. package/dist/extensions/hooks/copilot-hooks.json +0 -21
  265. package/dist/extensions/hooks/ponytail-activate.js +0 -91
  266. package/dist/extensions/hooks/ponytail-config.js +0 -122
  267. package/dist/extensions/hooks/ponytail-instructions.js +0 -94
  268. package/dist/extensions/hooks/ponytail-mode-tracker.js +0 -55
  269. package/dist/extensions/hooks/ponytail-runtime.js +0 -68
  270. package/dist/extensions/hooks/ponytail-statusline.ps1 +0 -21
  271. package/dist/extensions/hooks/ponytail-statusline.sh +0 -12
  272. package/dist/extensions/hooks/ponytail-subagent.js +0 -22
  273. package/dist/extensions/pi-extension/index.js +0 -189
  274. package/dist/extensions/pi-extension/package.json +0 -8
  275. package/dist/extensions/pi-extension/test/extension.test.js +0 -167
  276. package/dist/extensions/pi-extension/test/helpers.test.js +0 -92
  277. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  278. package/dist/extensions/pi-subagents/agents/context-builder.md +0 -46
  279. package/dist/extensions/pi-subagents/agents/delegate.md +0 -12
  280. package/dist/extensions/pi-subagents/agents/oracle.md +0 -73
  281. package/dist/extensions/pi-subagents/agents/planner.md +0 -55
  282. package/dist/extensions/pi-subagents/agents/reviewer.md +0 -79
  283. package/dist/extensions/pi-subagents/agents/scout.md +0 -50
  284. package/dist/extensions/pi-subagents/agents/worker.md +0 -55
  285. package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +0 -46
  286. package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +0 -45
  287. package/dist/extensions/pi-subagents/tsconfig.runner.json +0 -17
  288. package/dist/extensions/ponytail-pi-extension/index.js +0 -189
  289. package/dist/extensions/ponytail-pi-extension/package.json +0 -8
  290. package/dist/extensions/ponytail-pi-extension/test/extension.test.js +0 -167
  291. package/dist/extensions/ponytail-pi-extension/test/helpers.test.js +0 -92
  292. package/dist/extensions/prototype.ts +0 -713
  293. package/dist/extensions/question.ts +0 -350
  294. package/dist/extensions/quick.ts +0 -630
  295. package/dist/extensions/tps-tracker.ts +0 -280
  296. package/dist/extensions/workflow/task-validators.ts +0 -69
  297. package/dist/skills/selesai-default/SKILL.md +0 -16
@@ -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";
@@ -28,6 +29,7 @@ import { exportSessionToHtml } from "./export-html/index.js";
28
29
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
29
30
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
30
31
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
32
+ import { ModelRegistry } from "./model-registry.js";
31
33
  import { expandPromptTemplate } from "./prompt-templates.js";
32
34
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
33
35
  import { createSyntheticSourceInfo } from "./source-info.js";
@@ -35,6 +37,7 @@ import { buildSystemPrompt } from "./system-prompt.js";
35
37
  import { createLocalBashOperations } from "./tools/bash.js";
36
38
  import { createAllToolDefinitions } from "./tools/index.js";
37
39
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
40
+ import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
38
41
  /**
39
42
  * Parse a skill block from message text.
40
43
  * Returns null if the text doesn't contain a skill block.
@@ -50,6 +53,14 @@ export function parseSkillBlock(text) {
50
53
  userMessage: match[4]?.trim() || undefined,
51
54
  };
52
55
  }
56
+ // ============================================================================
57
+ // Types
58
+ // ============================================================================
59
+ function withoutDeletedHeaders(headers) {
60
+ return headers
61
+ ? Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] !== null))
62
+ : undefined;
63
+ }
53
64
  function estimateMessagesTokens(messages) {
54
65
  let tokens = 0;
55
66
  for (const message of messages) {
@@ -114,8 +125,7 @@ export class AgentSession {
114
125
  _extensionShutdownHandler;
115
126
  _extensionErrorListener;
116
127
  _extensionErrorUnsubscriber;
117
- // Model registry for API key resolution
118
- _modelRegistry;
128
+ _modelRuntime;
119
129
  // Tool registry for extension getTools/setTools
120
130
  _toolRegistry = new Map();
121
131
  _toolDefinitions = new Map();
@@ -133,7 +143,7 @@ export class AgentSession {
133
143
  this._resourceLoader = config.resourceLoader;
134
144
  this._customTools = config.customTools ?? [];
135
145
  this._cwd = config.cwd;
136
- this._modelRegistry = config.modelRegistry;
146
+ this._modelRuntime = config.modelRuntime;
137
147
  this._extensionRunnerRef = config.extensionRunnerRef;
138
148
  this._initialActiveToolNames = config.initialActiveToolNames;
139
149
  this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
@@ -150,22 +160,29 @@ export class AgentSession {
150
160
  includeAllExtensionTools: true,
151
161
  });
152
162
  }
153
- /** Model registry for API key resolution and model discovery */
154
- get modelRegistry() {
155
- return this._modelRegistry;
163
+ get modelRuntime() {
164
+ return this._modelRuntime;
156
165
  }
157
166
  async _getRequiredRequestAuth(model) {
158
- const result = await this._modelRegistry.getApiKeyAndHeaders(model);
159
- if (!result.ok) {
160
- if (result.error.startsWith("No API key found")) {
167
+ let result;
168
+ try {
169
+ result = await this._modelRuntime.getAuth(model);
170
+ }
171
+ catch (error) {
172
+ const cause = error instanceof Error ? error.cause : undefined;
173
+ if (cause instanceof Error && cause.message === "authHeader requires a resolved API key") {
161
174
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
162
175
  }
163
- throw new Error(result.error);
176
+ throw error;
164
177
  }
165
- if (result.apiKey) {
166
- return { apiKey: result.apiKey, headers: result.headers, env: result.env };
178
+ if (result?.auth.apiKey) {
179
+ return {
180
+ apiKey: result.auth.apiKey,
181
+ headers: withoutDeletedHeaders(result.auth.headers),
182
+ env: result.env,
183
+ };
167
184
  }
168
- const isOAuth = this._modelRegistry.isUsingOAuth(model);
185
+ const isOAuth = this._modelRuntime.isUsingOAuth(model.provider);
169
186
  if (isOAuth) {
170
187
  throw new Error(`Authentication failed for "${model.provider}". ` +
171
188
  `Credentials may have expired or network is unavailable. ` +
@@ -173,12 +190,19 @@ export class AgentSession {
173
190
  }
174
191
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
175
192
  }
176
- async _getCompactionRequestAuth(model) {
177
- if (this.agent.streamFn === streamSimple) {
193
+ async _getSummarizationRequestAuth(model) {
194
+ if (this.agent.streamFunction === streamSimple) {
178
195
  return this._getRequiredRequestAuth(model);
179
196
  }
180
- const result = await this._modelRegistry.getApiKeyAndHeaders(model);
181
- return result.ok ? { apiKey: result.apiKey, headers: result.headers, env: result.env } : {};
197
+ try {
198
+ const result = await this._modelRuntime.getAuth(model);
199
+ return result
200
+ ? { apiKey: result.auth.apiKey, headers: withoutDeletedHeaders(result.auth.headers), env: result.env }
201
+ : {};
202
+ }
203
+ catch {
204
+ return {};
205
+ }
182
206
  }
183
207
  /**
184
208
  * Install tool hooks once on the Agent instance.
@@ -222,6 +246,7 @@ export class AgentSession {
222
246
  content: result.content,
223
247
  details: result.details,
224
248
  isError,
249
+ usage: result.usage,
225
250
  });
226
251
  if (!hookResult) {
227
252
  return undefined;
@@ -230,6 +255,7 @@ export class AgentSession {
230
255
  content: hookResult.content,
231
256
  details: hookResult.details,
232
257
  isError: hookResult.isError ?? isError,
258
+ usage: hookResult.usage,
233
259
  };
234
260
  };
235
261
  }
@@ -341,7 +367,7 @@ export class AgentSession {
341
367
  // This ensures the UI sees the updated queue state
342
368
  if (event.type === "message_start" && event.message.role === "user") {
343
369
  this._overflowRecoveryAttempted = false;
344
- const messageText = this._getUserMessageText(event.message);
370
+ const messageText = contentText(event.message.content, "");
345
371
  if (messageText) {
346
372
  // Check steering queue first
347
373
  const steeringIndex = this._steeringMessages.indexOf(messageText);
@@ -410,16 +436,6 @@ export class AgentSession {
410
436
  }
411
437
  return false;
412
438
  }
413
- /** Extract text content from a message */
414
- _getUserMessageText(message) {
415
- if (message.role !== "user")
416
- return "";
417
- const content = message.content;
418
- if (typeof content === "string")
419
- return content;
420
- const textBlocks = content.filter((c) => c.type === "text");
421
- return textBlocks.map((c) => c.text).join("");
422
- }
423
439
  /** Find the last assistant message in agent state (including aborted ones) */
424
440
  _findLastAssistantMessage() {
425
441
  const messages = this.agent.state.messages;
@@ -875,8 +891,10 @@ export class AgentSession {
875
891
  if (!this.model) {
876
892
  throw new Error(formatNoModelSelectedMessage());
877
893
  }
878
- if (!this._modelRegistry.hasConfiguredAuth(this.model)) {
879
- const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
894
+ const hasConfiguredAuth = this._modelRuntime.hasConfiguredAuth(this.model.provider) ||
895
+ (await this._modelRuntime.checkAuth(this.model.provider)) !== undefined;
896
+ if (!hasConfiguredAuth) {
897
+ const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
880
898
  if (isOAuth) {
881
899
  throw new Error(`Authentication failed for "${this.model.provider}". ` +
882
900
  `Credentials may have expired or network is unavailable. ` +
@@ -1221,7 +1239,7 @@ export class AgentSession {
1221
1239
  * @throws Error if no auth is configured for the model
1222
1240
  */
1223
1241
  async setModel(model) {
1224
- if (!this._modelRegistry.hasConfiguredAuth(model)) {
1242
+ if (!(await this._modelRuntime.checkAuth(model.provider))) {
1225
1243
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1226
1244
  }
1227
1245
  const previousModel = this.model;
@@ -1246,7 +1264,11 @@ export class AgentSession {
1246
1264
  return this._cycleAvailableModel(direction);
1247
1265
  }
1248
1266
  async _cycleScopedModel(direction) {
1249
- const scopedModels = this._scopedModels.filter((scoped) => this._modelRegistry.hasConfiguredAuth(scoped.model));
1267
+ const checks = await Promise.all(this._scopedModels.map(async (scoped) => ({
1268
+ scoped,
1269
+ auth: await this._modelRuntime.checkAuth(scoped.model.provider),
1270
+ })));
1271
+ const scopedModels = checks.filter(({ auth }) => auth !== undefined).map(({ scoped }) => scoped);
1250
1272
  if (scopedModels.length <= 1)
1251
1273
  return undefined;
1252
1274
  const currentModel = this.model;
@@ -1270,7 +1292,7 @@ export class AgentSession {
1270
1292
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1271
1293
  }
1272
1294
  async _cycleAvailableModel(direction) {
1273
- const availableModels = await this._modelRegistry.getAvailable();
1295
+ const availableModels = await this._modelRuntime.getAvailable();
1274
1296
  if (availableModels.length <= 1)
1275
1297
  return undefined;
1276
1298
  const currentModel = this.model;
@@ -1398,7 +1420,7 @@ export class AgentSession {
1398
1420
  if (!this.model) {
1399
1421
  throw new Error(formatNoModelSelectedMessage());
1400
1422
  }
1401
- const { apiKey, headers, env } = await this._getCompactionRequestAuth(this.model);
1423
+ const { apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model);
1402
1424
  const pathEntries = this.sessionManager.getBranch();
1403
1425
  const settings = this.settingsManager.getCompactionSettings();
1404
1426
  const preparation = prepareCompaction(pathEntries, settings);
@@ -1433,26 +1455,29 @@ export class AgentSession {
1433
1455
  let summary;
1434
1456
  let firstKeptEntryId;
1435
1457
  let tokensBefore;
1458
+ let usage;
1436
1459
  let details;
1437
1460
  if (extensionCompaction) {
1438
1461
  // Extension provided compaction content
1439
1462
  summary = extensionCompaction.summary;
1440
1463
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
1441
1464
  tokensBefore = extensionCompaction.tokensBefore;
1465
+ usage = extensionCompaction.usage;
1442
1466
  details = extensionCompaction.details;
1443
1467
  }
1444
1468
  else {
1445
1469
  // Generate compaction result
1446
- const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
1470
+ 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" }));
1447
1471
  summary = result.summary;
1448
1472
  firstKeptEntryId = result.firstKeptEntryId;
1449
1473
  tokensBefore = result.tokensBefore;
1474
+ usage = result.usage;
1450
1475
  details = result.details;
1451
1476
  }
1452
1477
  if (this._compactionAbortController.signal.aborted) {
1453
1478
  throw new Error("Compaction cancelled");
1454
1479
  }
1455
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
1480
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1456
1481
  const newEntries = this.sessionManager.getEntries();
1457
1482
  const sessionContext = this.sessionManager.buildSessionContext();
1458
1483
  this.agent.state.messages = sessionContext.messages;
@@ -1473,6 +1498,7 @@ export class AgentSession {
1473
1498
  firstKeptEntryId,
1474
1499
  tokensBefore,
1475
1500
  estimatedTokensAfter,
1501
+ usage,
1476
1502
  details,
1477
1503
  };
1478
1504
  this._emit({
@@ -1619,17 +1645,16 @@ export class AgentSession {
1619
1645
  let apiKey;
1620
1646
  let headers;
1621
1647
  let env;
1622
- if (this.agent.streamFn === streamSimple) {
1623
- const authResult = await this._modelRegistry.getApiKeyAndHeaders(this.model);
1624
- if (!authResult.ok || !authResult.apiKey) {
1648
+ if (this.agent.streamFunction === streamSimple) {
1649
+ const authResult = await this._modelRuntime.getAuth(this.model);
1650
+ if (!authResult?.auth.apiKey)
1625
1651
  return false;
1626
- }
1627
- apiKey = authResult.apiKey;
1628
- headers = authResult.headers;
1652
+ apiKey = authResult.auth.apiKey;
1653
+ headers = withoutDeletedHeaders(authResult.auth.headers);
1629
1654
  env = authResult.env;
1630
1655
  }
1631
1656
  else {
1632
- ({ apiKey, headers, env } = await this._getCompactionRequestAuth(this.model));
1657
+ ({ apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model));
1633
1658
  }
1634
1659
  const pathEntries = this.sessionManager.getBranch();
1635
1660
  const preparation = prepareCompaction(pathEntries, settings);
@@ -1669,20 +1694,23 @@ export class AgentSession {
1669
1694
  let summary;
1670
1695
  let firstKeptEntryId;
1671
1696
  let tokensBefore;
1697
+ let usage;
1672
1698
  let details;
1673
1699
  if (extensionCompaction) {
1674
1700
  // Extension provided compaction content
1675
1701
  summary = extensionCompaction.summary;
1676
1702
  firstKeptEntryId = extensionCompaction.firstKeptEntryId;
1677
1703
  tokensBefore = extensionCompaction.tokensBefore;
1704
+ usage = extensionCompaction.usage;
1678
1705
  details = extensionCompaction.details;
1679
1706
  }
1680
1707
  else {
1681
1708
  // Generate compaction result
1682
- const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, env);
1709
+ 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 }));
1683
1710
  summary = compactResult.summary;
1684
1711
  firstKeptEntryId = compactResult.firstKeptEntryId;
1685
1712
  tokensBefore = compactResult.tokensBefore;
1713
+ usage = compactResult.usage;
1686
1714
  details = compactResult.details;
1687
1715
  }
1688
1716
  if (this._autoCompactionAbortController.signal.aborted) {
@@ -1695,7 +1723,7 @@ export class AgentSession {
1695
1723
  });
1696
1724
  return false;
1697
1725
  }
1698
- this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
1726
+ this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1699
1727
  const newEntries = this.sessionManager.getEntries();
1700
1728
  const sessionContext = this.sessionManager.buildSessionContext();
1701
1729
  this.agent.state.messages = sessionContext.messages;
@@ -1716,6 +1744,7 @@ export class AgentSession {
1716
1744
  firstKeptEntryId,
1717
1745
  tokensBefore,
1718
1746
  estimatedTokensAfter,
1747
+ usage,
1719
1748
  details,
1720
1749
  };
1721
1750
  this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
@@ -1843,7 +1872,7 @@ export class AgentSession {
1843
1872
  if (!currentModel) {
1844
1873
  return;
1845
1874
  }
1846
- const refreshedModel = this._modelRegistry.find(currentModel.provider, currentModel.id);
1875
+ const refreshedModel = this._modelRuntime.getModel(currentModel.provider, currentModel.id);
1847
1876
  if (!refreshedModel || refreshedModel === currentModel) {
1848
1877
  return;
1849
1878
  }
@@ -1912,7 +1941,7 @@ export class AgentSession {
1912
1941
  refreshTools: () => this._refreshToolRegistry(),
1913
1942
  getCommands,
1914
1943
  setModel: async (model) => {
1915
- if (!this.modelRegistry.hasConfiguredAuth(model))
1944
+ if (!this._modelRuntime.hasConfiguredAuth(model.provider))
1916
1945
  return false;
1917
1946
  await this.setModel(model);
1918
1947
  return true;
@@ -1952,11 +1981,15 @@ export class AgentSession {
1952
1981
  getSystemPromptOptions: () => this._baseSystemPromptOptions,
1953
1982
  }, {
1954
1983
  registerProvider: (name, config) => {
1955
- this._modelRegistry.registerProvider(name, config);
1984
+ this._modelRuntime.registerProvider(name, config);
1985
+ this._refreshCurrentModelFromRegistry();
1986
+ },
1987
+ registerNativeProvider: (provider) => {
1988
+ this._modelRuntime.registerNativeProvider(provider);
1956
1989
  this._refreshCurrentModelFromRegistry();
1957
1990
  },
1958
1991
  unregisterProvider: (name) => {
1959
- this._modelRegistry.unregisterProvider(name);
1992
+ this._modelRuntime.unregisterProvider(name);
1960
1993
  this._refreshCurrentModelFromRegistry();
1961
1994
  },
1962
1995
  });
@@ -2058,7 +2091,7 @@ export class AgentSession {
2058
2091
  extensionsResult.runtime.flagValues.set(name, value);
2059
2092
  }
2060
2093
  }
2061
- this._extensionRunner = new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, this._modelRegistry);
2094
+ this._extensionRunner = new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, new ModelRegistry(this._modelRuntime));
2062
2095
  if (this._extensionRunnerRef) {
2063
2096
  this._extensionRunnerRef.current = this._extensionRunner;
2064
2097
  }
@@ -2108,6 +2141,34 @@ export class AgentSession {
2108
2141
  return false;
2109
2142
  return isRetryableAssistantError(message);
2110
2143
  }
2144
+ /**
2145
+ * Retry policy + callbacks shared by compaction and branch-summary summarization calls.
2146
+ * Uses the same `settings.retry` budget/backoff as agent-turn retries so a single transient
2147
+ * stream drop no longer fails the whole operation. `source` carries the context
2148
+ * the TUI needs to render the retry and recreate the underlying indicator.
2149
+ */
2150
+ _summarizationRetryCallbacks(source) {
2151
+ return {
2152
+ onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => {
2153
+ this._emit({
2154
+ type: "summarization_retry_scheduled",
2155
+ attempt,
2156
+ maxAttempts,
2157
+ delayMs,
2158
+ errorMessage,
2159
+ });
2160
+ },
2161
+ onRetryAttemptStart: () => {
2162
+ this._emit({
2163
+ type: "summarization_retry_attempt_start",
2164
+ ...source,
2165
+ });
2166
+ },
2167
+ onRetryFinished: () => {
2168
+ this._emit({ type: "summarization_retry_finished" });
2169
+ },
2170
+ };
2171
+ }
2111
2172
  /**
2112
2173
  * Prepare a retryable error for continuation with exponential backoff.
2113
2174
  * @returns true if the caller should continue the agent, false otherwise
@@ -2353,9 +2414,10 @@ export class AgentSession {
2353
2414
  // Run default summarizer if needed
2354
2415
  let summaryText;
2355
2416
  let summaryDetails;
2417
+ let summaryUsage;
2356
2418
  if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
2357
2419
  const model = this.model;
2358
- const { apiKey, headers, env } = await this._getRequiredRequestAuth(model);
2420
+ const { apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
2359
2421
  const branchSummarySettings = this.settingsManager.getBranchSummarySettings();
2360
2422
  const result = await generateBranchSummary(entriesToSummarize, {
2361
2423
  model,
@@ -2366,7 +2428,9 @@ export class AgentSession {
2366
2428
  customInstructions,
2367
2429
  replaceInstructions,
2368
2430
  reserveTokens: branchSummarySettings.reserveTokens,
2369
- streamFn: this.agent.streamFn,
2431
+ streamFn: this.agent.streamFunction,
2432
+ retry: this.settingsManager.getRetrySettings(),
2433
+ callbacks: this._summarizationRetryCallbacks({ source: "branchSummary" }),
2370
2434
  });
2371
2435
  if (result.aborted) {
2372
2436
  return { cancelled: true, aborted: true };
@@ -2375,6 +2439,7 @@ export class AgentSession {
2375
2439
  throw new Error(result.error);
2376
2440
  }
2377
2441
  summaryText = result.summary;
2442
+ summaryUsage = result.usage;
2378
2443
  summaryDetails = {
2379
2444
  readFiles: result.readFiles || [],
2380
2445
  modifiedFiles: result.modifiedFiles || [],
@@ -2383,6 +2448,7 @@ export class AgentSession {
2383
2448
  else if (extensionSummary) {
2384
2449
  summaryText = extensionSummary.summary;
2385
2450
  summaryDetails = extensionSummary.details;
2451
+ summaryUsage = extensionSummary.usage;
2386
2452
  }
2387
2453
  // Determine the new leaf position based on target type
2388
2454
  let newLeafId;
@@ -2390,18 +2456,12 @@ export class AgentSession {
2390
2456
  if (targetEntry.type === "message" && targetEntry.message.role === "user") {
2391
2457
  // User message: leaf = parent (null if root), text goes to editor
2392
2458
  newLeafId = targetEntry.parentId;
2393
- editorText = this._extractUserMessageText(targetEntry.message.content);
2459
+ editorText = contentText(targetEntry.message.content, "");
2394
2460
  }
2395
2461
  else if (targetEntry.type === "custom_message") {
2396
2462
  // Custom message: leaf = parent (null if root), text goes to editor
2397
2463
  newLeafId = targetEntry.parentId;
2398
- editorText =
2399
- typeof targetEntry.content === "string"
2400
- ? targetEntry.content
2401
- : targetEntry.content
2402
- .filter((c) => c.type === "text")
2403
- .map((c) => c.text)
2404
- .join("");
2464
+ editorText = contentText(targetEntry.content, "");
2405
2465
  }
2406
2466
  else {
2407
2467
  // Non-user message: leaf = selected node
@@ -2412,7 +2472,7 @@ export class AgentSession {
2412
2472
  let summaryEntry;
2413
2473
  if (summaryText) {
2414
2474
  // Create summary at target position (can be null for root)
2415
- const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
2475
+ const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension, summaryUsage);
2416
2476
  summaryEntry = this.sessionManager.getEntry(summaryId);
2417
2477
  // Attach label to the summary entry
2418
2478
  if (label) {
@@ -2460,24 +2520,13 @@ export class AgentSession {
2460
2520
  continue;
2461
2521
  if (entry.message.role !== "user")
2462
2522
  continue;
2463
- const text = this._extractUserMessageText(entry.message.content);
2523
+ const text = contentText(entry.message.content, "");
2464
2524
  if (text) {
2465
2525
  result.push({ entryId: entry.id, text });
2466
2526
  }
2467
2527
  }
2468
2528
  return result;
2469
2529
  }
2470
- _extractUserMessageText(content) {
2471
- if (typeof content === "string")
2472
- return content;
2473
- if (Array.isArray(content)) {
2474
- return content
2475
- .filter((c) => c.type === "text")
2476
- .map((c) => c.text)
2477
- .join("");
2478
- }
2479
- return "";
2480
- }
2481
2530
  /**
2482
2531
  * Get session statistics. Aggregates over ALL session entries (including
2483
2532
  * history that was compacted away), so token/cost totals reflect what was
@@ -2489,12 +2538,11 @@ export class AgentSession {
2489
2538
  let toolResults = 0;
2490
2539
  let totalMessages = 0;
2491
2540
  let toolCalls = 0;
2492
- let totalInput = 0;
2493
- let totalOutput = 0;
2494
- let totalCacheRead = 0;
2495
- let totalCacheWrite = 0;
2496
- let totalCost = 0;
2541
+ const usageTotals = createUsageTotals();
2497
2542
  for (const entry of this.sessionManager.getEntries()) {
2543
+ if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
2544
+ addUsageToTotals(usageTotals, entry.usage);
2545
+ }
2498
2546
  if (entry.type !== "message")
2499
2547
  continue;
2500
2548
  totalMessages++;
@@ -2504,6 +2552,9 @@ export class AgentSession {
2504
2552
  }
2505
2553
  else if (message.role === "toolResult") {
2506
2554
  toolResults++;
2555
+ if (message.usage) {
2556
+ addUsageToTotals(usageTotals, message.usage);
2557
+ }
2507
2558
  }
2508
2559
  else if (message.role === "assistant") {
2509
2560
  assistantMessages++;
@@ -2511,12 +2562,7 @@ export class AgentSession {
2511
2562
  if (Array.isArray(assistantMsg.content)) {
2512
2563
  toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
2513
2564
  }
2514
- const usage = assistantMsg.usage;
2515
- totalInput += usage.input;
2516
- totalOutput += usage.output;
2517
- totalCacheRead += usage.cacheRead;
2518
- totalCacheWrite += usage.cacheWrite;
2519
- totalCost += usage.cost.total;
2565
+ addUsageToTotals(usageTotals, assistantMsg.usage);
2520
2566
  }
2521
2567
  }
2522
2568
  return {
@@ -2528,13 +2574,13 @@ export class AgentSession {
2528
2574
  toolResults,
2529
2575
  totalMessages,
2530
2576
  tokens: {
2531
- input: totalInput,
2532
- output: totalOutput,
2533
- cacheRead: totalCacheRead,
2534
- cacheWrite: totalCacheWrite,
2535
- total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
2577
+ input: usageTotals.input,
2578
+ output: usageTotals.output,
2579
+ cacheRead: usageTotals.cacheRead,
2580
+ cacheWrite: usageTotals.cacheWrite,
2581
+ total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite,
2536
2582
  },
2537
- cost: totalCost,
2583
+ cost: usageTotals.cost,
2538
2584
  contextUsage: this.getContextUsage(),
2539
2585
  };
2540
2586
  }