@mariozechner/pi-coding-agent 0.61.1 → 0.63.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 (214) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +2 -2
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +4 -0
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +15 -6
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +94 -90
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/auth-storage.d.ts +3 -1
  11. package/dist/core/auth-storage.d.ts.map +1 -1
  12. package/dist/core/auth-storage.js +5 -2
  13. package/dist/core/auth-storage.js.map +1 -1
  14. package/dist/core/compaction/branch-summarization.d.ts +2 -0
  15. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  16. package/dist/core/compaction/branch-summarization.js +2 -2
  17. package/dist/core/compaction/branch-summarization.js.map +1 -1
  18. package/dist/core/compaction/compaction.d.ts +2 -2
  19. package/dist/core/compaction/compaction.d.ts.map +1 -1
  20. package/dist/core/compaction/compaction.js +9 -9
  21. package/dist/core/compaction/compaction.js.map +1 -1
  22. package/dist/core/export-html/index.d.ts +2 -2
  23. package/dist/core/export-html/index.d.ts.map +1 -1
  24. package/dist/core/export-html/index.js +7 -6
  25. package/dist/core/export-html/index.js.map +1 -1
  26. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  27. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  28. package/dist/core/export-html/tool-renderer.js +41 -16
  29. package/dist/core/export-html/tool-renderer.js.map +1 -1
  30. package/dist/core/extensions/index.d.ts +3 -2
  31. package/dist/core/extensions/index.d.ts.map +1 -1
  32. package/dist/core/extensions/index.js.map +1 -1
  33. package/dist/core/extensions/loader.d.ts.map +1 -1
  34. package/dist/core/extensions/loader.js +12 -2
  35. package/dist/core/extensions/loader.js.map +1 -1
  36. package/dist/core/extensions/runner.d.ts +4 -7
  37. package/dist/core/extensions/runner.d.ts.map +1 -1
  38. package/dist/core/extensions/runner.js +27 -38
  39. package/dist/core/extensions/runner.js.map +1 -1
  40. package/dist/core/extensions/types.d.ts +44 -9
  41. package/dist/core/extensions/types.d.ts.map +1 -1
  42. package/dist/core/extensions/types.js.map +1 -1
  43. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  44. package/dist/core/extensions/wrapper.js +2 -8
  45. package/dist/core/extensions/wrapper.js.map +1 -1
  46. package/dist/core/index.d.ts +1 -0
  47. package/dist/core/index.d.ts.map +1 -1
  48. package/dist/core/index.js +1 -0
  49. package/dist/core/index.js.map +1 -1
  50. package/dist/core/model-registry.d.ts +18 -2
  51. package/dist/core/model-registry.d.ts.map +1 -1
  52. package/dist/core/model-registry.js +83 -69
  53. package/dist/core/model-registry.js.map +1 -1
  54. package/dist/core/model-resolver.d.ts.map +1 -1
  55. package/dist/core/model-resolver.js +4 -4
  56. package/dist/core/model-resolver.js.map +1 -1
  57. package/dist/core/output-guard.d.ts +6 -0
  58. package/dist/core/output-guard.d.ts.map +1 -0
  59. package/dist/core/output-guard.js +59 -0
  60. package/dist/core/output-guard.js.map +1 -0
  61. package/dist/core/package-manager.d.ts +1 -0
  62. package/dist/core/package-manager.d.ts.map +1 -1
  63. package/dist/core/package-manager.js +77 -10
  64. package/dist/core/package-manager.js.map +1 -1
  65. package/dist/core/prompt-templates.d.ts +2 -1
  66. package/dist/core/prompt-templates.d.ts.map +1 -1
  67. package/dist/core/prompt-templates.js +30 -32
  68. package/dist/core/prompt-templates.js.map +1 -1
  69. package/dist/core/resolve-config-value.d.ts +6 -0
  70. package/dist/core/resolve-config-value.d.ts.map +1 -1
  71. package/dist/core/resolve-config-value.js +37 -5
  72. package/dist/core/resolve-config-value.js.map +1 -1
  73. package/dist/core/resource-loader.d.ts +6 -5
  74. package/dist/core/resource-loader.d.ts.map +1 -1
  75. package/dist/core/resource-loader.js +136 -108
  76. package/dist/core/resource-loader.js.map +1 -1
  77. package/dist/core/sdk.d.ts +2 -2
  78. package/dist/core/sdk.d.ts.map +1 -1
  79. package/dist/core/sdk.js +13 -22
  80. package/dist/core/sdk.js.map +1 -1
  81. package/dist/core/settings-manager.d.ts +2 -0
  82. package/dist/core/settings-manager.d.ts.map +1 -1
  83. package/dist/core/settings-manager.js +3 -0
  84. package/dist/core/settings-manager.js.map +1 -1
  85. package/dist/core/skills.d.ts +2 -1
  86. package/dist/core/skills.d.ts.map +1 -1
  87. package/dist/core/skills.js +25 -1
  88. package/dist/core/skills.js.map +1 -1
  89. package/dist/core/slash-commands.d.ts +2 -3
  90. package/dist/core/slash-commands.d.ts.map +1 -1
  91. package/dist/core/slash-commands.js.map +1 -1
  92. package/dist/core/source-info.d.ts +18 -0
  93. package/dist/core/source-info.d.ts.map +1 -0
  94. package/dist/core/source-info.js +19 -0
  95. package/dist/core/source-info.js.map +1 -0
  96. package/dist/core/system-prompt.d.ts.map +1 -1
  97. package/dist/core/system-prompt.js +3 -38
  98. package/dist/core/system-prompt.js.map +1 -1
  99. package/dist/core/timings.d.ts +1 -0
  100. package/dist/core/timings.d.ts.map +1 -1
  101. package/dist/core/timings.js +6 -0
  102. package/dist/core/timings.js.map +1 -1
  103. package/dist/core/tools/bash.d.ts +19 -9
  104. package/dist/core/tools/bash.d.ts.map +1 -1
  105. package/dist/core/tools/bash.js +151 -59
  106. package/dist/core/tools/bash.js.map +1 -1
  107. package/dist/core/tools/edit-diff.d.ts +23 -1
  108. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  109. package/dist/core/tools/edit-diff.js +100 -32
  110. package/dist/core/tools/edit-diff.js.map +1 -1
  111. package/dist/core/tools/edit.d.ts +30 -6
  112. package/dist/core/tools/edit.d.ts.map +1 -1
  113. package/dist/core/tools/edit.js +172 -59
  114. package/dist/core/tools/edit.js.map +1 -1
  115. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  116. package/dist/core/tools/file-mutation-queue.js +4 -4
  117. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  118. package/dist/core/tools/find.d.ts +11 -4
  119. package/dist/core/tools/find.d.ts.map +1 -1
  120. package/dist/core/tools/find.js +76 -27
  121. package/dist/core/tools/find.js.map +1 -1
  122. package/dist/core/tools/grep.d.ts +15 -4
  123. package/dist/core/tools/grep.d.ts.map +1 -1
  124. package/dist/core/tools/grep.js +83 -29
  125. package/dist/core/tools/grep.js.map +1 -1
  126. package/dist/core/tools/index.d.ts +67 -21
  127. package/dist/core/tools/index.d.ts.map +1 -1
  128. package/dist/core/tools/index.js +50 -26
  129. package/dist/core/tools/index.js.map +1 -1
  130. package/dist/core/tools/ls.d.ts +9 -3
  131. package/dist/core/tools/ls.d.ts.map +1 -1
  132. package/dist/core/tools/ls.js +67 -13
  133. package/dist/core/tools/ls.js.map +1 -1
  134. package/dist/core/tools/read.d.ts +10 -3
  135. package/dist/core/tools/read.d.ts.map +1 -1
  136. package/dist/core/tools/read.js +110 -51
  137. package/dist/core/tools/read.js.map +1 -1
  138. package/dist/core/tools/render-utils.d.ts +21 -0
  139. package/dist/core/tools/render-utils.d.ts.map +1 -0
  140. package/dist/core/tools/render-utils.js +49 -0
  141. package/dist/core/tools/render-utils.js.map +1 -0
  142. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  143. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  144. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  145. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  146. package/dist/core/tools/write.d.ts +9 -3
  147. package/dist/core/tools/write.d.ts.map +1 -1
  148. package/dist/core/tools/write.js +162 -27
  149. package/dist/core/tools/write.js.map +1 -1
  150. package/dist/index.d.ts +3 -2
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +2 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/main.d.ts.map +1 -1
  155. package/dist/main.js +56 -18
  156. package/dist/main.js.map +1 -1
  157. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  158. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/bash-execution.js +18 -5
  160. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/tool-execution.d.ts +15 -40
  162. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/tool-execution.js +126 -679
  164. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  165. package/dist/modes/interactive/interactive-mode.d.ts +4 -11
  166. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  167. package/dist/modes/interactive/interactive-mode.js +146 -93
  168. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  169. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  170. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  171. package/dist/modes/interactive/theme/theme.js +14 -0
  172. package/dist/modes/interactive/theme/theme.js.map +1 -1
  173. package/dist/modes/print-mode.d.ts +1 -1
  174. package/dist/modes/print-mode.d.ts.map +1 -1
  175. package/dist/modes/print-mode.js +84 -78
  176. package/dist/modes/print-mode.js.map +1 -1
  177. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  178. package/dist/modes/rpc/rpc-mode.js +27 -20
  179. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  180. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  181. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  182. package/dist/modes/rpc/rpc-types.js.map +1 -1
  183. package/dist/utils/image-resize.d.ts +5 -5
  184. package/dist/utils/image-resize.d.ts.map +1 -1
  185. package/dist/utils/image-resize.js +45 -94
  186. package/dist/utils/image-resize.js.map +1 -1
  187. package/docs/development.md +3 -1
  188. package/docs/extensions.md +74 -33
  189. package/docs/models.md +6 -0
  190. package/docs/rpc.md +11 -2
  191. package/docs/settings.md +12 -0
  192. package/docs/tui.md +2 -2
  193. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  194. package/examples/extensions/commands.ts +3 -3
  195. package/examples/extensions/custom-compaction.ts +17 -4
  196. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  197. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  198. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  199. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  200. package/examples/extensions/handoff.ts +5 -2
  201. package/examples/extensions/minimal-mode.ts +14 -14
  202. package/examples/extensions/qna.ts +5 -2
  203. package/examples/extensions/question.ts +2 -2
  204. package/examples/extensions/questionnaire.ts +2 -2
  205. package/examples/extensions/subagent/index.ts +2 -2
  206. package/examples/extensions/summarize.ts +15 -4
  207. package/examples/extensions/todo.ts +2 -2
  208. package/examples/extensions/truncated-tool.ts +2 -2
  209. package/examples/extensions/with-deps/package-lock.json +2 -2
  210. package/examples/extensions/with-deps/package.json +1 -1
  211. package/examples/sdk/04-skills.ts +8 -2
  212. package/examples/sdk/08-prompt-templates.ts +2 -1
  213. package/examples/sdk/12-full-control.ts +0 -1
  214. package/package.json +5 -4
@@ -27,9 +27,10 @@ import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
27
27
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
28
28
  import { expandPromptTemplate } from "./prompt-templates.js";
29
29
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
30
- import { BUILTIN_SLASH_COMMANDS } from "./slash-commands.js";
30
+ import { createSyntheticSourceInfo } from "./source-info.js";
31
31
  import { buildSystemPrompt } from "./system-prompt.js";
32
- import { createAllTools } from "./tools/index.js";
32
+ import { createAllToolDefinitions } from "./tools/index.js";
33
+ import { createToolDefinitionFromAgentTool, wrapToolDefinition } from "./tools/tool-definition-wrapper.js";
33
34
  /**
34
35
  * Parse a skill block from message text.
35
36
  * Returns null if the text doesn't contain a skill block.
@@ -89,7 +90,7 @@ export class AgentSession {
89
90
  _turnIndex = 0;
90
91
  _resourceLoader;
91
92
  _customTools;
92
- _baseToolRegistry = new Map();
93
+ _baseToolDefinitions = new Map();
93
94
  _cwd;
94
95
  _extensionRunnerRef;
95
96
  _initialActiveToolNames;
@@ -103,6 +104,7 @@ export class AgentSession {
103
104
  _modelRegistry;
104
105
  // Tool registry for extension getTools/setTools
105
106
  _toolRegistry = new Map();
107
+ _toolDefinitions = new Map();
106
108
  _toolPromptSnippets = new Map();
107
109
  _toolPromptGuidelines = new Map();
108
110
  // Base system prompt (without extension appends) - used to apply fresh appends each turn
@@ -132,6 +134,23 @@ export class AgentSession {
132
134
  get modelRegistry() {
133
135
  return this._modelRegistry;
134
136
  }
137
+ async _getRequiredRequestAuth(model) {
138
+ const result = await this._modelRegistry.getApiKeyAndHeaders(model);
139
+ if (!result.ok) {
140
+ throw new Error(result.error);
141
+ }
142
+ if (result.apiKey) {
143
+ return { apiKey: result.apiKey, headers: result.headers };
144
+ }
145
+ const isOAuth = this._modelRegistry.isUsingOAuth(model);
146
+ if (isOAuth) {
147
+ throw new Error(`Authentication failed for "${model.provider}". ` +
148
+ `Credentials may have expired or network is unavailable. ` +
149
+ `Run '/login ${model.provider}' to re-authenticate.`);
150
+ }
151
+ throw new Error(`No API key found for ${model.provider}.\n\n` +
152
+ `Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}`);
153
+ }
135
154
  /**
136
155
  * Install tool hooks once on the Agent instance.
137
156
  *
@@ -493,15 +512,19 @@ export class AgentSession {
493
512
  return this.agent.state.tools.map((t) => t.name);
494
513
  }
495
514
  /**
496
- * Get all configured tools with name, description, and parameter schema.
515
+ * Get all configured tools with name, description, parameter schema, and source metadata.
497
516
  */
498
517
  getAllTools() {
499
- return Array.from(this._toolRegistry.values()).map((t) => ({
500
- name: t.name,
501
- description: t.description,
502
- parameters: t.parameters,
518
+ return Array.from(this._toolDefinitions.values()).map(({ definition, sourceInfo }) => ({
519
+ name: definition.name,
520
+ description: definition.description,
521
+ parameters: definition.parameters,
522
+ sourceInfo,
503
523
  }));
504
524
  }
525
+ getToolDefinition(name) {
526
+ return this._toolDefinitions.get(name)?.definition;
527
+ }
505
528
  /**
506
529
  * Set active tools by name.
507
530
  * Only tools in the registry can be enabled. Unknown tool names are ignored.
@@ -680,9 +703,7 @@ export class AgentSession {
680
703
  `Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}\n\n` +
681
704
  "Then use /model to select a model.");
682
705
  }
683
- // Validate API key
684
- const apiKey = await this._modelRegistry.getApiKey(this.model);
685
- if (!apiKey) {
706
+ if (!this._modelRegistry.hasConfiguredAuth(this.model)) {
686
707
  const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
687
708
  if (isOAuth) {
688
709
  throw new Error(`Authentication failed for "${this.model.provider}". ` +
@@ -1059,12 +1080,11 @@ export class AgentSession {
1059
1080
  }
1060
1081
  /**
1061
1082
  * Set model directly.
1062
- * Validates API key, saves to session and settings.
1063
- * @throws Error if no API key available for the model
1083
+ * Validates that auth is configured, saves to session and settings.
1084
+ * @throws Error if no auth is configured for the model
1064
1085
  */
1065
1086
  async setModel(model) {
1066
- const apiKey = await this._modelRegistry.getApiKey(model);
1067
- if (!apiKey) {
1087
+ if (!this._modelRegistry.hasConfiguredAuth(model)) {
1068
1088
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1069
1089
  }
1070
1090
  const previousModel = this.model;
@@ -1088,27 +1108,11 @@ export class AgentSession {
1088
1108
  }
1089
1109
  return this._cycleAvailableModel(direction);
1090
1110
  }
1091
- async _getScopedModelsWithApiKey() {
1092
- const apiKeysByProvider = new Map();
1093
- const result = [];
1094
- for (const scoped of this._scopedModels) {
1095
- const provider = scoped.model.provider;
1096
- let apiKey;
1097
- if (apiKeysByProvider.has(provider)) {
1098
- apiKey = apiKeysByProvider.get(provider);
1099
- }
1100
- else {
1101
- apiKey = await this._modelRegistry.getApiKeyForProvider(provider);
1102
- apiKeysByProvider.set(provider, apiKey);
1103
- }
1104
- if (apiKey) {
1105
- result.push(scoped);
1106
- }
1107
- }
1108
- return result;
1111
+ _getScopedModelsWithAuth() {
1112
+ return this._scopedModels.filter((scoped) => this._modelRegistry.hasConfiguredAuth(scoped.model));
1109
1113
  }
1110
1114
  async _cycleScopedModel(direction) {
1111
- const scopedModels = await this._getScopedModelsWithApiKey();
1115
+ const scopedModels = this._getScopedModelsWithAuth();
1112
1116
  if (scopedModels.length <= 1)
1113
1117
  return undefined;
1114
1118
  const currentModel = this.model;
@@ -1142,10 +1146,6 @@ export class AgentSession {
1142
1146
  const len = availableModels.length;
1143
1147
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1144
1148
  const nextModel = availableModels[nextIndex];
1145
- const apiKey = await this._modelRegistry.getApiKey(nextModel);
1146
- if (!apiKey) {
1147
- throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
1148
- }
1149
1149
  const thinkingLevel = this._getThinkingLevelForModelSwitch();
1150
1150
  this.agent.setModel(nextModel);
1151
1151
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
@@ -1274,10 +1274,7 @@ export class AgentSession {
1274
1274
  if (!this.model) {
1275
1275
  throw new Error("No model selected");
1276
1276
  }
1277
- const apiKey = await this._modelRegistry.getApiKey(this.model);
1278
- if (!apiKey) {
1279
- throw new Error(`No API key for ${this.model.provider}`);
1280
- }
1277
+ const { apiKey, headers } = await this._getRequiredRequestAuth(this.model);
1281
1278
  const pathEntries = this.sessionManager.getBranch();
1282
1279
  const settings = this.settingsManager.getCompactionSettings();
1283
1280
  const preparation = prepareCompaction(pathEntries, settings);
@@ -1320,7 +1317,7 @@ export class AgentSession {
1320
1317
  }
1321
1318
  else {
1322
1319
  // Generate compaction result
1323
- const result = await compact(preparation, this.model, apiKey, customInstructions, this._compactionAbortController.signal);
1320
+ const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal);
1324
1321
  summary = result.summary;
1325
1322
  firstKeptEntryId = result.firstKeptEntryId;
1326
1323
  tokensBefore = result.tokensBefore;
@@ -1460,11 +1457,12 @@ export class AgentSession {
1460
1457
  this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
1461
1458
  return;
1462
1459
  }
1463
- const apiKey = await this._modelRegistry.getApiKey(this.model);
1464
- if (!apiKey) {
1460
+ const authResult = await this._modelRegistry.getApiKeyAndHeaders(this.model);
1461
+ if (!authResult.ok || !authResult.apiKey) {
1465
1462
  this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
1466
1463
  return;
1467
1464
  }
1465
+ const { apiKey, headers } = authResult;
1468
1466
  const pathEntries = this.sessionManager.getBranch();
1469
1467
  const preparation = prepareCompaction(pathEntries, settings);
1470
1468
  if (!preparation) {
@@ -1503,7 +1501,7 @@ export class AgentSession {
1503
1501
  }
1504
1502
  else {
1505
1503
  // Generate compaction result
1506
- const compactResult = await compact(preparation, this.model, apiKey, undefined, this._autoCompactionAbortController.signal);
1504
+ const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal);
1507
1505
  summary = compactResult.summary;
1508
1506
  firstKeptEntryId = compactResult.firstKeptEntryId;
1509
1507
  tokensBefore = compactResult.tokensBefore;
@@ -1656,36 +1654,24 @@ export class AgentSession {
1656
1654
  this.agent.setModel(refreshedModel);
1657
1655
  }
1658
1656
  _bindExtensionCore(runner) {
1659
- const normalizeLocation = (source) => {
1660
- if (source === "user" || source === "project" || source === "path") {
1661
- return source;
1662
- }
1663
- return undefined;
1664
- };
1665
- const reservedBuiltins = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
1666
1657
  const getCommands = () => {
1667
- const extensionCommands = runner
1668
- .getRegisteredCommandsWithPaths()
1669
- .filter(({ command }) => !reservedBuiltins.has(command.name))
1670
- .map(({ command, extensionPath }) => ({
1671
- name: command.name,
1658
+ const extensionCommands = runner.getRegisteredCommands().map((command) => ({
1659
+ name: command.invocationName,
1672
1660
  description: command.description,
1673
1661
  source: "extension",
1674
- path: extensionPath,
1662
+ sourceInfo: command.sourceInfo,
1675
1663
  }));
1676
1664
  const templates = this.promptTemplates.map((template) => ({
1677
1665
  name: template.name,
1678
1666
  description: template.description,
1679
1667
  source: "prompt",
1680
- location: normalizeLocation(template.source),
1681
- path: template.filePath,
1668
+ sourceInfo: template.sourceInfo,
1682
1669
  }));
1683
1670
  const skills = this._resourceLoader.getSkills().skills.map((skill) => ({
1684
1671
  name: `skill:${skill.name}`,
1685
1672
  description: skill.description,
1686
1673
  source: "skill",
1687
- location: normalizeLocation(skill.source),
1688
- path: skill.filePath,
1674
+ sourceInfo: skill.sourceInfo,
1689
1675
  }));
1690
1676
  return [...extensionCommands, ...templates, ...skills];
1691
1677
  };
@@ -1726,8 +1712,7 @@ export class AgentSession {
1726
1712
  refreshTools: () => this._refreshToolRegistry(),
1727
1713
  getCommands,
1728
1714
  setModel: async (model) => {
1729
- const key = await this.modelRegistry.getApiKey(model);
1730
- if (!key)
1715
+ if (!this.modelRegistry.hasConfiguredAuth(model))
1731
1716
  return false;
1732
1717
  await this.setModel(model);
1733
1718
  return true;
@@ -1773,24 +1758,44 @@ export class AgentSession {
1773
1758
  const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
1774
1759
  const allCustomTools = [
1775
1760
  ...registeredTools,
1776
- ...this._customTools.map((def) => ({ definition: def, extensionPath: "<sdk>" })),
1761
+ ...this._customTools.map((definition) => ({
1762
+ definition,
1763
+ sourceInfo: createSyntheticSourceInfo(`<sdk:${definition.name}>`, { source: "sdk" }),
1764
+ })),
1777
1765
  ];
1778
- this._toolPromptSnippets = new Map(allCustomTools
1779
- .map((registeredTool) => {
1780
- const snippet = this._normalizePromptSnippet(registeredTool.definition.promptSnippet);
1781
- return snippet ? [registeredTool.definition.name, snippet] : undefined;
1766
+ const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries()).map(([name, definition]) => [
1767
+ name,
1768
+ {
1769
+ definition,
1770
+ sourceInfo: createSyntheticSourceInfo(`<builtin:${name}>`, { source: "builtin" }),
1771
+ },
1772
+ ]));
1773
+ for (const tool of allCustomTools) {
1774
+ definitionRegistry.set(tool.definition.name, {
1775
+ definition: tool.definition,
1776
+ sourceInfo: tool.sourceInfo,
1777
+ });
1778
+ }
1779
+ this._toolDefinitions = definitionRegistry;
1780
+ this._toolPromptSnippets = new Map(Array.from(definitionRegistry.values())
1781
+ .map(({ definition }) => {
1782
+ const snippet = this._normalizePromptSnippet(definition.promptSnippet);
1783
+ return snippet ? [definition.name, snippet] : undefined;
1782
1784
  })
1783
1785
  .filter((entry) => entry !== undefined));
1784
- this._toolPromptGuidelines = new Map(allCustomTools
1785
- .map((registeredTool) => {
1786
- const guidelines = this._normalizePromptGuidelines(registeredTool.definition.promptGuidelines);
1787
- return guidelines.length > 0 ? [registeredTool.definition.name, guidelines] : undefined;
1786
+ this._toolPromptGuidelines = new Map(Array.from(definitionRegistry.values())
1787
+ .map(({ definition }) => {
1788
+ const guidelines = this._normalizePromptGuidelines(definition.promptGuidelines);
1789
+ return guidelines.length > 0 ? [definition.name, guidelines] : undefined;
1788
1790
  })
1789
1791
  .filter((entry) => entry !== undefined));
1790
1792
  const wrappedExtensionTools = this._extensionRunner
1791
1793
  ? wrapRegisteredTools(allCustomTools, this._extensionRunner)
1792
1794
  : [];
1793
- const toolRegistry = new Map(this._baseToolRegistry);
1795
+ const toolRegistry = new Map(Array.from(this._baseToolDefinitions.values()).map((definition) => [
1796
+ definition.name,
1797
+ wrapToolDefinition(definition),
1798
+ ]));
1794
1799
  for (const tool of wrappedExtensionTools) {
1795
1800
  toolRegistry.set(tool.name, tool);
1796
1801
  }
@@ -1815,13 +1820,16 @@ export class AgentSession {
1815
1820
  _buildRuntime(options) {
1816
1821
  const autoResizeImages = this.settingsManager.getImageAutoResize();
1817
1822
  const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
1818
- const baseTools = this._baseToolsOverride
1819
- ? this._baseToolsOverride
1820
- : createAllTools(this._cwd, {
1823
+ const baseToolDefinitions = this._baseToolsOverride
1824
+ ? Object.fromEntries(Object.entries(this._baseToolsOverride).map(([name, tool]) => [
1825
+ name,
1826
+ createToolDefinitionFromAgentTool(tool),
1827
+ ]))
1828
+ : createAllToolDefinitions(this._cwd, {
1821
1829
  read: { autoResizeImages },
1822
1830
  bash: { commandPrefix: shellCommandPrefix },
1823
1831
  });
1824
- this._baseToolRegistry = new Map(Object.entries(baseTools).map(([name, tool]) => [name, tool]));
1832
+ this._baseToolDefinitions = new Map(Object.entries(baseToolDefinitions).map(([name, tool]) => [name, tool]));
1825
1833
  const extensionsResult = this._resourceLoader.getExtensions();
1826
1834
  if (options.flagValues) {
1827
1835
  for (const [name, value] of options.flagValues) {
@@ -2285,14 +2293,12 @@ export class AgentSession {
2285
2293
  let summaryDetails;
2286
2294
  if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
2287
2295
  const model = this.model;
2288
- const apiKey = await this._modelRegistry.getApiKey(model);
2289
- if (!apiKey) {
2290
- throw new Error(`No API key for ${model.provider}`);
2291
- }
2296
+ const { apiKey, headers } = await this._getRequiredRequestAuth(model);
2292
2297
  const branchSummarySettings = this.settingsManager.getBranchSummarySettings();
2293
2298
  const result = await generateBranchSummary(entriesToSummarize, {
2294
2299
  model,
2295
2300
  apiKey,
2301
+ headers,
2296
2302
  signal: this._branchSummaryAbortController.signal,
2297
2303
  customInstructions,
2298
2304
  replaceInstructions,
@@ -2449,6 +2455,7 @@ export class AgentSession {
2449
2455
  total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
2450
2456
  },
2451
2457
  cost: totalCost,
2458
+ contextUsage: this.getContextUsage(),
2452
2459
  };
2453
2460
  }
2454
2461
  getContextUsage() {
@@ -2500,13 +2507,10 @@ export class AgentSession {
2500
2507
  async exportToHtml(outputPath) {
2501
2508
  const themeName = this.settingsManager.getTheme();
2502
2509
  // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
2503
- let toolRenderer;
2504
- if (this._extensionRunner) {
2505
- toolRenderer = createToolHtmlRenderer({
2506
- getToolDefinition: (name) => this._extensionRunner.getToolDefinition(name),
2507
- theme,
2508
- });
2509
- }
2510
+ const toolRenderer = createToolHtmlRenderer({
2511
+ getToolDefinition: (name) => this.getToolDefinition(name),
2512
+ theme,
2513
+ });
2510
2514
  return await exportSessionToHtml(this.sessionManager, this.state, {
2511
2515
  outputPath,
2512
2516
  themeName,