@averyyy/pi-coding-agent 0.99.1-piclient.1 → 1.0.0-piclient.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/bundle/chunks/anthropic-messages-5Z4I6I54.js +6 -0
  3. package/dist/bundle/chunks/anthropic.js +2 -2
  4. package/dist/bundle/chunks/{azure-openai-responses-YZ3Q2WVP.js → azure-openai-responses-TXH53UDQ.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  6. package/dist/bundle/chunks/{chunk-7BINYQ2V.js → chunk-43NAB75E.js} +54 -49
  7. package/dist/bundle/chunks/{chunk-V7V43FNB.js → chunk-4ILNOXGR.js} +2 -2
  8. package/dist/bundle/chunks/chunk-AV6LYFGH.js +2 -0
  9. package/dist/bundle/chunks/{chunk-7JGR3GZN.js → chunk-F42GKQ7R.js} +1 -1
  10. package/dist/bundle/chunks/chunk-GIUN2MS4.js +16 -0
  11. package/dist/bundle/chunks/{chunk-W2DOMZSC.js → chunk-JTLRGL3E.js} +2 -2
  12. package/dist/bundle/chunks/{chunk-F6ORSP6L.js → chunk-KYK7L7MP.js} +18 -78
  13. package/dist/bundle/chunks/chunk-LD2P6SFZ.js +2 -0
  14. package/dist/bundle/chunks/chunk-LDPPAHPJ.js +4 -0
  15. package/dist/bundle/chunks/{chunk-3YXHSP6S.js → chunk-NEUJZQL3.js} +7 -9
  16. package/dist/bundle/chunks/chunk-OI6YFHHK.js +82 -0
  17. package/dist/bundle/chunks/{chunk-J4CWPC4N.js → chunk-PXGUCLIP.js} +1 -1
  18. package/dist/bundle/chunks/chunk-VIV3CWXG.js +2 -0
  19. package/dist/bundle/chunks/chunk-WGS2XTDX.js +2 -0
  20. package/dist/bundle/chunks/{chunk-FJTXMIZI.js → chunk-Y5O5BZGX.js} +15 -29
  21. package/dist/bundle/chunks/{cli-WF6HDHBO.js → cli-JE36AHMI.js} +8 -6
  22. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-XJQQYGGU.js → cloudflare-workers-ai-system-one-TF36V7KG.js} +1 -1
  23. package/dist/bundle/chunks/codemode-worker.js +67 -8
  24. package/dist/bundle/chunks/execute-UX73LR4F.js +367 -0
  25. package/dist/bundle/chunks/{google-generative-ai-CQAKKJTA.js → google-generative-ai-XQU2LTWM.js} +1 -1
  26. package/dist/bundle/chunks/{google-vertex-AE36YXJU.js → google-vertex-F6Q2CN55.js} +1 -1
  27. package/dist/bundle/chunks/{https-proxy-agent-2VXB7436.js → https-proxy-agent-UDEHOBGS.js} +1 -1
  28. package/dist/bundle/chunks/{llama-cpp-classify-PJJYKPLD.js → llama-cpp-classify-XT44PI3C.js} +1 -1
  29. package/dist/bundle/chunks/{mistral-conversations-KEYTXJM7.js → mistral-conversations-XAJWHQFR.js} +1 -1
  30. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  31. package/dist/bundle/chunks/{openai-codex-responses-FUE7WESF.js → openai-codex-responses-3EHOMZYM.js} +1 -1
  32. package/dist/bundle/chunks/openai-codex.js +1 -1
  33. package/dist/bundle/chunks/{openai-completions-XW2Q5HVC.js → openai-completions-QC2IQHSC.js} +1 -1
  34. package/dist/bundle/chunks/{openai-responses-ZUXFVYPI.js → openai-responses-JPLD2GJS.js} +1 -1
  35. package/dist/bundle/chunks/{openrouter-images-TPQ4V6OR.js → openrouter-images-ZVXWCSIM.js} +1 -1
  36. package/dist/bundle/chunks/openrouter.js +1 -1
  37. package/dist/bundle/chunks/pi-logo-animation-APS4JS6B.js +2 -0
  38. package/dist/bundle/chunks/radius.js +1 -1
  39. package/dist/bundle/chunks/runtime-MPXPM7VL.js +2 -0
  40. package/dist/bundle/chunks/{typesafe-system-one-E7A4HNJI.js → typesafe-system-one-7DJTDUKY.js} +1 -1
  41. package/dist/bundle/chunks/virtual-modules-X5AB6TM6.js +2 -0
  42. package/dist/bundle/cli-runtime.js +1 -1
  43. package/dist/bundle/index.js +1 -1
  44. package/dist/bundle/rpc-entry.js +1 -1
  45. package/dist/cli/args.js +2 -2
  46. package/dist/cli/args.js.map +1 -1
  47. package/dist/config.d.ts +5 -8
  48. package/dist/config.d.ts.map +1 -1
  49. package/dist/config.js +16 -13
  50. package/dist/config.js.map +1 -1
  51. package/dist/core/agent-session.d.ts +15 -1
  52. package/dist/core/agent-session.d.ts.map +1 -1
  53. package/dist/core/agent-session.js +80 -12
  54. package/dist/core/agent-session.js.map +1 -1
  55. package/dist/core/cache-warmer.d.ts.map +1 -1
  56. package/dist/core/cache-warmer.js +11 -3
  57. package/dist/core/cache-warmer.js.map +1 -1
  58. package/dist/core/extensions/loader.d.ts.map +1 -1
  59. package/dist/core/extensions/loader.js +13 -1
  60. package/dist/core/extensions/loader.js.map +1 -1
  61. package/dist/core/extensions/types.d.ts +6 -1
  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/mcp-servers.d.ts +34 -7
  65. package/dist/core/mcp-servers.d.ts.map +1 -1
  66. package/dist/core/mcp-servers.js +52 -11
  67. package/dist/core/mcp-servers.js.map +1 -1
  68. package/dist/core/model-registry.d.ts +3 -1
  69. package/dist/core/model-registry.d.ts.map +1 -1
  70. package/dist/core/model-registry.js +4 -0
  71. package/dist/core/model-registry.js.map +1 -1
  72. package/dist/core/model-resolver.js +1 -1
  73. package/dist/core/model-resolver.js.map +1 -1
  74. package/dist/core/model-runtime.d.ts +22 -0
  75. package/dist/core/model-runtime.d.ts.map +1 -1
  76. package/dist/core/model-runtime.js +347 -49
  77. package/dist/core/model-runtime.js.map +1 -1
  78. package/dist/core/pi-server-client.d.ts +21 -1
  79. package/dist/core/pi-server-client.d.ts.map +1 -1
  80. package/dist/core/pi-server-client.js +308 -22
  81. package/dist/core/pi-server-client.js.map +1 -1
  82. package/dist/core/pi-server-protocol.d.ts +12 -1
  83. package/dist/core/pi-server-protocol.d.ts.map +1 -1
  84. package/dist/core/pi-server-protocol.js +1 -0
  85. package/dist/core/pi-server-protocol.js.map +1 -1
  86. package/dist/core/radius.d.ts +2 -0
  87. package/dist/core/radius.d.ts.map +1 -1
  88. package/dist/core/radius.js +2 -0
  89. package/dist/core/radius.js.map +1 -1
  90. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  91. package/dist/core/remote-catalog-provider.js +4 -9
  92. package/dist/core/remote-catalog-provider.js.map +1 -1
  93. package/dist/core/sdk.d.ts.map +1 -1
  94. package/dist/core/sdk.js +52 -18
  95. package/dist/core/sdk.js.map +1 -1
  96. package/dist/core/settings-manager.d.ts +7 -5
  97. package/dist/core/settings-manager.d.ts.map +1 -1
  98. package/dist/core/settings-manager.js +3 -2
  99. package/dist/core/settings-manager.js.map +1 -1
  100. package/dist/core/system-prompt.js +1 -1
  101. package/dist/core/system-prompt.js.map +1 -1
  102. package/dist/core/tools/bash.d.ts.map +1 -1
  103. package/dist/core/tools/bash.js +3 -5
  104. package/dist/core/tools/bash.js.map +1 -1
  105. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  106. package/dist/core/tools/renderers/bash.js +11 -31
  107. package/dist/core/tools/renderers/bash.js.map +1 -1
  108. package/dist/core/virtual-models.d.ts +2 -0
  109. package/dist/core/virtual-models.d.ts.map +1 -1
  110. package/dist/core/virtual-models.js +19 -12
  111. package/dist/core/virtual-models.js.map +1 -1
  112. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  113. package/dist/extensions/codemode/execute.js +197 -51
  114. package/dist/extensions/codemode/execute.js.map +1 -1
  115. package/dist/extensions/codemode/renderer.d.ts.map +1 -1
  116. package/dist/extensions/codemode/renderer.js +43 -25
  117. package/dist/extensions/codemode/renderer.js.map +1 -1
  118. package/dist/extensions/codemode/tool.d.ts +12 -11
  119. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  120. package/dist/extensions/codemode/tool.js +73 -129
  121. package/dist/extensions/codemode/tool.js.map +1 -1
  122. package/dist/extensions/codemode/worker.d.ts +1 -1
  123. package/dist/extensions/codemode/worker.js +1 -1
  124. package/dist/extensions/codemode/worker.js.map +1 -1
  125. package/dist/extensions/mcp/cli.d.ts.map +1 -1
  126. package/dist/extensions/mcp/cli.js +20 -5
  127. package/dist/extensions/mcp/cli.js.map +1 -1
  128. package/dist/extensions/mcp/config.d.ts +4 -2
  129. package/dist/extensions/mcp/config.d.ts.map +1 -1
  130. package/dist/extensions/mcp/config.js +14 -2
  131. package/dist/extensions/mcp/config.js.map +1 -1
  132. package/dist/extensions/mcp/index.d.ts +32 -8
  133. package/dist/extensions/mcp/index.d.ts.map +1 -1
  134. package/dist/extensions/mcp/index.js +220 -56
  135. package/dist/extensions/mcp/index.js.map +1 -1
  136. package/dist/extensions/mcp/oauth.d.ts +10 -7
  137. package/dist/extensions/mcp/oauth.d.ts.map +1 -1
  138. package/dist/extensions/mcp/oauth.js +56 -31
  139. package/dist/extensions/mcp/oauth.js.map +1 -1
  140. package/dist/extensions/mcp/runtime.d.ts +2 -0
  141. package/dist/extensions/mcp/runtime.d.ts.map +1 -1
  142. package/dist/extensions/mcp/runtime.js +16 -9
  143. package/dist/extensions/mcp/runtime.js.map +1 -1
  144. package/dist/extensions/mcp/tools.d.ts +5 -4
  145. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  146. package/dist/extensions/mcp/tools.js +33 -15
  147. package/dist/extensions/mcp/tools.js.map +1 -1
  148. package/dist/extensions/mcp/ui.d.ts.map +1 -1
  149. package/dist/extensions/mcp/ui.js +4 -2
  150. package/dist/extensions/mcp/ui.js.map +1 -1
  151. package/dist/extensions/tool-search/tool.d.ts +5 -4
  152. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  153. package/dist/extensions/tool-search/tool.js +7 -28
  154. package/dist/extensions/tool-search/tool.js.map +1 -1
  155. package/dist/index.d.ts +1 -1
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js.map +1 -1
  158. package/dist/main.d.ts.map +1 -1
  159. package/dist/main.js +24 -11
  160. package/dist/main.js.map +1 -1
  161. package/dist/modes/interactive/components/oauth-selector.d.ts +8 -2
  162. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/oauth-selector.js +24 -21
  164. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  165. package/dist/modes/interactive/components/pi-logo-animation.d.ts +68 -0
  166. package/dist/modes/interactive/components/pi-logo-animation.d.ts.map +1 -0
  167. package/dist/modes/interactive/components/pi-logo-animation.js +1086 -0
  168. package/dist/modes/interactive/components/pi-logo-animation.js.map +1 -0
  169. package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts +7 -0
  170. package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts.map +1 -0
  171. package/dist/modes/interactive/components/pi-logo-animation.lazy.js +12 -0
  172. package/dist/modes/interactive/components/pi-logo-animation.lazy.js.map +1 -0
  173. package/dist/modes/interactive/components/pi-logo.d.ts +7 -0
  174. package/dist/modes/interactive/components/pi-logo.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/pi-logo.js +13 -1
  176. package/dist/modes/interactive/components/pi-logo.js.map +1 -1
  177. package/dist/modes/interactive/components/radius-login-selector.d.ts +15 -0
  178. package/dist/modes/interactive/components/radius-login-selector.d.ts.map +1 -0
  179. package/dist/modes/interactive/components/radius-login-selector.js +85 -0
  180. package/dist/modes/interactive/components/radius-login-selector.js.map +1 -0
  181. package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
  182. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  183. package/dist/modes/interactive/components/settings-selector.js +5 -5
  184. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  185. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  186. package/dist/modes/interactive/components/user-message.js +5 -4
  187. package/dist/modes/interactive/components/user-message.js.map +1 -1
  188. package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
  189. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  190. package/dist/modes/interactive/components/visual-truncate.js +39 -6
  191. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  192. package/dist/modes/interactive/interactive-mode.d.ts +10 -0
  193. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  194. package/dist/modes/interactive/interactive-mode.js +153 -40
  195. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  196. package/dist/modes/interactive/theme/system-theme.d.ts +2 -1
  197. package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -1
  198. package/dist/modes/interactive/theme/system-theme.js +16 -5
  199. package/dist/modes/interactive/theme/system-theme.js.map +1 -1
  200. package/docs/cli.md +8 -18
  201. package/docs/codemode.md +207 -0
  202. package/docs/docs.json +5 -1
  203. package/docs/environment-variables.md +2 -2
  204. package/docs/extensions.md +3 -3
  205. package/docs/index.md +1 -1
  206. package/docs/mcp.md +142 -82
  207. package/docs/models.md +22 -1
  208. package/docs/providers.md +18 -6
  209. package/docs/sdk.md +2 -2
  210. package/docs/settings.md +5 -3
  211. package/docs/usage.md +1 -1
  212. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  213. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  214. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  215. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  216. package/examples/extensions/gondolin/package-lock.json +2 -2
  217. package/examples/extensions/gondolin/package.json +1 -1
  218. package/examples/extensions/minimal-mode.ts +24 -96
  219. package/examples/extensions/sandbox/package-lock.json +2 -2
  220. package/examples/extensions/sandbox/package.json +1 -1
  221. package/examples/extensions/with-deps/package-lock.json +2 -2
  222. package/examples/extensions/with-deps/package.json +1 -1
  223. package/examples/sdk/14-codemode-mcp.ts +2 -2
  224. package/package.json +10 -10
  225. package/dist/bundle/chunks/anthropic-messages-A6EXUBG6.js +0 -6
  226. package/dist/bundle/chunks/chunk-HSPCVFST.js +0 -2
  227. package/dist/bundle/chunks/chunk-KYJKN5BJ.js +0 -2
  228. package/dist/bundle/chunks/chunk-RO6I2QFQ.js +0 -4
  229. package/dist/bundle/chunks/chunk-WVKBMRQX.js +0 -82
  230. package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
  231. package/dist/bundle/chunks/execute-AVGAUHMT.js +0 -308
  232. package/dist/bundle/chunks/runtime-6ZC4MGWD.js +0 -2
  233. package/dist/bundle/chunks/virtual-modules-H7OCXN6W.js +0 -2
@@ -26,6 +26,7 @@ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScop
26
26
  import { CredentialSynchronizationError } from "../../core/model-runtime.js";
27
27
  import { DefaultPackageManager } from "../../core/package-manager.js";
28
28
  import { getCliBinName } from "../../core/pi-client-cli-adapter.js";
29
+ import { RADIUS_MCP_URL, RADIUS_PROVIDER_ID } from "../../core/radius.js";
29
30
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
30
31
  import { SessionManager, sessionEntryToContextMessages, } from "../../core/session-manager.js";
31
32
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
@@ -33,6 +34,7 @@ import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
33
34
  import { withBuiltInRenderers } from "../../core/tools/renderers/index.js";
34
35
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
35
36
  import { getUsageCostBreakdown } from "../../core/usage-totals.js";
37
+ import { addMcpServerConfig, loadMcpConfig } from "../../extensions/mcp/config.js";
36
38
  import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
37
39
  import { copyToClipboard, readClipboardFilePaths, readClipboardText } from "../../utils/clipboard.js";
38
40
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
@@ -64,8 +66,10 @@ import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./c
64
66
  import { LoginDialogComponent } from "./components/login-dialog.js";
65
67
  import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
66
68
  import { ModelSelectorComponent } from "./components/model-selector.js";
67
- import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
68
- import { piLogoLines } from "./components/pi-logo.js";
69
+ import { formatAuthSelectorProviderStatus, formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
70
+ import { piLogoLines, piWordmark, supportsPiLogo } from "./components/pi-logo.js";
71
+ import { playPiLogoAnimation } from "./components/pi-logo-animation.lazy.js";
72
+ import { createLoginMenuSelector } from "./components/radius-login-selector.js";
69
73
  import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
70
74
  import { SessionSelectorComponent } from "./components/session-selector.js";
71
75
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
@@ -107,6 +111,16 @@ class ExpandableText extends ThemedText {
107
111
  this.invalidate();
108
112
  }
109
113
  }
114
+ /** The built-in header. Clicking its logo (the first two lines, after one column of padding) plays an easter egg. */
115
+ class BuiltInHeader extends ExpandableText {
116
+ onLogoClick;
117
+ handleMouse(event) {
118
+ if (event.type !== "click" || event.y > 1 || event.x < 1 || event.x > 4 || !this.onLogoClick)
119
+ return undefined;
120
+ this.onLogoClick(event.screenX - event.x + 1, event.screenY - event.y);
121
+ return { handled: true };
122
+ }
123
+ }
110
124
  function isCustomSessionEntry(item) {
111
125
  return "type" in item && item.type === "custom";
112
126
  }
@@ -176,6 +190,7 @@ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
176
190
  ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
177
191
  : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
178
192
  }
193
+ const RADIUS_LOGIN_INTRO = "Radius is a service crafted for Pi by the builders of Pi, Earendil Works";
179
194
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
180
195
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
181
196
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -198,18 +213,21 @@ function getLoginProviderCompletionOptions(providerOptions) {
198
213
  id: provider.id,
199
214
  name: provider.name,
200
215
  authTypes: [provider.authType],
216
+ subscription: provider.subscription,
201
217
  });
202
218
  }
203
219
  return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name));
204
220
  }
205
221
  function getLoginProviderSearchText(provider) {
206
222
  const authTypes = provider.authTypes
207
- .map((authType) => `${authType} ${formatAuthSelectorProviderType(authType)}`)
223
+ .map((authType) => `${authType} ${formatAuthSelectorProviderType(authType, provider.subscription)}`)
208
224
  .join(" ");
209
225
  return `${provider.id} ${provider.name} ${authTypes}`;
210
226
  }
211
227
  function formatLoginProviderCompletionDescription(provider) {
212
- const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
228
+ const authTypes = provider.authTypes
229
+ .map((authType) => formatAuthSelectorProviderType(authType, provider.subscription))
230
+ .join("/");
213
231
  return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
214
232
  }
215
233
  export class InteractiveMode {
@@ -642,7 +660,7 @@ export class InteractiveMode {
642
660
  this.registerSignalHandlers();
643
661
  // Load changelog (only show new entries, skip for resumed sessions)
644
662
  this.changelogMarkdown = this.getChangelogForDisplay();
645
- if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
663
+ if (this.session.scopedModels.length > 0 && this.shouldShowStartupDetails()) {
646
664
  const modelList = this.session.scopedModels
647
665
  .map((sm) => {
648
666
  const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : "";
@@ -694,10 +712,15 @@ export class InteractiveMode {
694
712
  // reported its colors. This ends at the terminal's DA1 reply, or after 100 ms if it answers nothing.
695
713
  await this.themeController.waitForTerminalColors();
696
714
  // Add header with keybindings from config (unless silenced)
697
- if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
715
+ if (this.shouldShowStartupHeader()) {
716
+ const showDetails = this.shouldShowStartupDetails();
698
717
  // Built on demand so the header follows theme changes. The logo's first line carries the version,
699
- // its second line the first line of key hints.
718
+ // its second line the first line of key hints. Terminals that cannot render the logo get a
719
+ // "Pi vX" line instead, with the key hints below it.
720
+ const showLogo = supportsPiLogo();
700
721
  const withLogo = (hints) => {
722
+ if (!showLogo)
723
+ return `${piWordmark()} ${theme.fg("dim", `v${this.version}`)}\n${hints}`;
701
724
  const [top, bottom] = piLogoLines();
702
725
  return `${top} ${theme.fg("dim", `v${this.version}`)}\n${bottom} ${hints}`;
703
726
  };
@@ -731,9 +754,12 @@ export class InteractiveMode {
731
754
  rawKeyHint("!", "bash"),
732
755
  hint("app.tools.expand", "more"),
733
756
  ].join(theme.fg("muted", " · "));
734
- const compactOnboarding = () => theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
757
+ const compactOnboarding = () => theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help${showDetails ? " and loaded resources" : ""}.`);
735
758
  const onboarding = () => theme.fg("dim", `Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.`);
736
- this.builtInHeader = new ExpandableText(() => `${withLogo(compactInstructions())}\n${compactOnboarding()}\n\n${onboarding()}`, () => `${withLogo(expandedInstructions())}\n\n${onboarding()}`, this.getStartupExpansionState(), 1, 0);
759
+ const header = new BuiltInHeader(() => `${withLogo(compactInstructions())}\n${compactOnboarding()}\n\n${onboarding()}`, () => `${withLogo(expandedInstructions())}\n\n${onboarding()}`, this.getStartupExpansionState(), 1, 0);
760
+ if (showLogo)
761
+ header.onLogoClick = (column, row) => playPiLogoAnimation(this.renderer, column, row);
762
+ this.builtInHeader = header;
737
763
  // Setup UI layout
738
764
  this.headerContainer.addChild(new Spacer(1));
739
765
  this.headerContainer.addChild(this.builtInHeader);
@@ -1046,6 +1072,14 @@ export class InteractiveMode {
1046
1072
  getStartupExpansionState() {
1047
1073
  return this.options.verbose || this.toolOutputExpanded;
1048
1074
  }
1075
+ /** Startup header (logo, version, key hints). Hidden only by quietStartup: true. */
1076
+ shouldShowStartupHeader() {
1077
+ return this.options.verbose === true || this.settingsManager.getQuietStartup() !== true;
1078
+ }
1079
+ /** Startup details (model scope, loaded resources). Hidden by quietStartup: true or "header". */
1080
+ shouldShowStartupDetails() {
1081
+ return this.options.verbose === true || this.settingsManager.getQuietStartup() === false;
1082
+ }
1049
1083
  /**
1050
1084
  * Get a short path relative to the package root for display.
1051
1085
  */
@@ -1307,7 +1341,7 @@ export class InteractiveMode {
1307
1341
  showLoadedResources(options) {
1308
1342
  // Resource rendering is idempotent; chat clears no longer clear this separate container.
1309
1343
  this.loadedResourcesContainer.clear();
1310
- const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
1344
+ const showListing = options?.force || this.shouldShowStartupDetails();
1311
1345
  const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
1312
1346
  if (!showListing && !showDiagnostics) {
1313
1347
  return;
@@ -4299,8 +4333,8 @@ export class InteractiveMode {
4299
4333
  onEnableInstallTelemetryChange: (enabled) => {
4300
4334
  this.settingsManager.setEnableInstallTelemetry(enabled);
4301
4335
  },
4302
- onQuietStartupChange: (enabled) => {
4303
- this.settingsManager.setQuietStartup(enabled);
4336
+ onQuietStartupChange: (quiet) => {
4337
+ this.settingsManager.setQuietStartup(quiet);
4304
4338
  },
4305
4339
  onDefaultProjectTrustChange: (defaultProjectTrust) => {
4306
4340
  this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
@@ -4952,6 +4986,7 @@ export class InteractiveMode {
4952
4986
  source: authStatus.label ?? authStatus.source,
4953
4987
  }
4954
4988
  : undefined;
4989
+ const subscription = provider.auth.oauth?.isSubscription === true;
4955
4990
  if ((!authType || authType === "oauth") && provider.auth.oauth) {
4956
4991
  options.push({
4957
4992
  id: provider.id,
@@ -4959,6 +4994,7 @@ export class InteractiveMode {
4959
4994
  authType: "oauth",
4960
4995
  method: provider.auth.oauth,
4961
4996
  status,
4997
+ subscription,
4962
4998
  });
4963
4999
  }
4964
5000
  if ((!authType || authType === "api_key") && provider.auth.apiKey) {
@@ -4968,6 +5004,7 @@ export class InteractiveMode {
4968
5004
  authType: "api_key",
4969
5005
  method: provider.auth.apiKey,
4970
5006
  status,
5007
+ subscription,
4971
5008
  });
4972
5009
  }
4973
5010
  }
@@ -4975,12 +5012,16 @@ export class InteractiveMode {
4975
5012
  }
4976
5013
  async getLogoutProviderOptions() {
4977
5014
  return (await this.session.modelRuntime.listCredentials({ signal: AbortSignal.timeout(15_000) }))
4978
- .map(({ providerId, type }) => ({
4979
- id: providerId,
4980
- name: this.session.modelRuntime.getProvider(providerId)?.name ?? providerId,
4981
- authType: type,
4982
- status: { type, source: "stored credential" },
4983
- }))
5015
+ .map(({ providerId, type }) => {
5016
+ const provider = this.session.modelRuntime.getProvider(providerId);
5017
+ return {
5018
+ id: providerId,
5019
+ name: provider?.name ?? providerId,
5020
+ authType: type,
5021
+ status: { type, source: "stored credential" },
5022
+ subscription: provider?.auth.oauth?.isSubscription === true,
5023
+ };
5024
+ })
4984
5025
  .sort((a, b) => a.name.localeCompare(b.name));
4985
5026
  }
4986
5027
  findLoginProviderOptions(providerRef) {
@@ -5010,18 +5051,25 @@ export class InteractiveMode {
5010
5051
  }
5011
5052
  this.showLoginProviderSelector(undefined, providerRef);
5012
5053
  }
5013
- async startProviderLogin(providerOption) {
5054
+ /** `onBack` reopens the selector the login was started from when the user cancels it. */
5055
+ async startProviderLogin(providerOption, onBack) {
5014
5056
  if (providerOption.authType === "oauth") {
5015
- await this.showLoginDialog(providerOption.id, providerOption.name);
5057
+ await this.showLoginDialog(providerOption.id, providerOption.name, onBack);
5016
5058
  }
5017
5059
  else if (providerOption.method?.login) {
5018
- await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
5060
+ await this.showApiKeyLoginDialog(providerOption.id, providerOption.name, onBack);
5019
5061
  }
5020
5062
  else {
5021
- this.showAmbientAuthDialog(providerOption);
5063
+ this.showAmbientAuthDialog(providerOption, onBack);
5022
5064
  }
5023
5065
  }
5024
5066
  showLoginAuthTypeSelector(providerOptions) {
5067
+ // The top-level selector offers Radius directly, as its last option.
5068
+ const radiusOption = providerOptions
5069
+ ? undefined
5070
+ : this.getLoginProviderOptions("oauth").find((provider) => provider.id === RADIUS_PROVIDER_ID);
5071
+ const radiusText = radiusOption ? `Sign in with ${radiusOption.name}` : undefined;
5072
+ const radiusLabel = radiusOption ? `${radiusText}${formatAuthSelectorProviderStatus(radiusOption)}` : undefined;
5025
5073
  const oauthProvider = providerOptions?.find((provider) => provider.authType === "oauth");
5026
5074
  const oauthLoginLabel = oauthProvider?.method && "loginLabel" in oauthProvider.method ? oauthProvider.method.loginLabel : undefined;
5027
5075
  const subscriptionLabel = oauthLoginLabel ?? "Sign in with an account";
@@ -5036,6 +5084,8 @@ export class InteractiveMode {
5036
5084
  if (availableAuthTypes.has("api_key")) {
5037
5085
  options.push(apiKeyLabel);
5038
5086
  }
5087
+ if (radiusLabel)
5088
+ options.push(radiusLabel);
5039
5089
  if (options.length === 0) {
5040
5090
  this.showStatus("No login methods available.");
5041
5091
  return;
@@ -5051,29 +5101,37 @@ export class InteractiveMode {
5051
5101
  ? `Select authentication method for ${providerOptions[0].name}:`
5052
5102
  : "Select authentication method:";
5053
5103
  this.showSelector((done) => {
5054
- const selector = new ExtensionSelectorComponent(title, options, (option) => {
5104
+ const onSelect = (option) => {
5055
5105
  done();
5106
+ if (radiusOption && option === radiusLabel) {
5107
+ void this.startProviderLogin(radiusOption, () => this.showLoginAuthTypeSelector());
5108
+ return;
5109
+ }
5056
5110
  const authType = option === subscriptionLabel ? "oauth" : "api_key";
5057
5111
  if (providerOptions) {
5058
5112
  const providerOption = providerOptions.find((provider) => provider.authType === authType);
5059
5113
  if (providerOption) {
5060
- void this.startProviderLogin(providerOption);
5114
+ void this.startProviderLogin(providerOption, () => this.showLoginAuthTypeSelector(providerOptions));
5061
5115
  }
5062
5116
  return;
5063
5117
  }
5064
5118
  this.showLoginProviderSelector(authType);
5065
- }, () => {
5119
+ };
5120
+ const onCancel = () => {
5066
5121
  done();
5067
5122
  this.ui.requestRender();
5068
- });
5069
- return { component: selector, focus: selector };
5123
+ };
5124
+ const selector = radiusLabel && radiusText
5125
+ ? createLoginMenuSelector(this.ui, title, options, { label: radiusLabel, text: radiusText }, onSelect, onCancel)
5126
+ : new ExtensionSelectorComponent(title, options, onSelect, onCancel);
5127
+ return { component: selector, focus: selector, dispose: () => selector.dispose() };
5070
5128
  });
5071
5129
  }
5072
5130
  showLoginProviderSelector(authType, initialSearchInput) {
5073
5131
  const providerOptions = this.getLoginProviderOptions(authType);
5074
5132
  if (providerOptions.length === 0) {
5075
5133
  const message = authType === "oauth"
5076
- ? "No subscription providers available."
5134
+ ? "No account providers available."
5077
5135
  : authType === "api_key"
5078
5136
  ? "No API key providers available."
5079
5137
  : "No login providers available.";
@@ -5087,7 +5145,7 @@ export class InteractiveMode {
5087
5145
  if (!providerOption) {
5088
5146
  return;
5089
5147
  }
5090
- await this.startProviderLogin(providerOption);
5148
+ await this.startProviderLogin(providerOption, () => this.showLoginProviderSelector(authType, initialSearchInput));
5091
5149
  }, () => {
5092
5150
  done();
5093
5151
  if (authType) {
@@ -5241,21 +5299,24 @@ export class InteractiveMode {
5241
5299
  })
5242
5300
  .finally(() => clearTimeout(timeout));
5243
5301
  }
5244
- showAmbientAuthDialog(providerOption) {
5302
+ showAmbientAuthDialog(providerOption, onBack) {
5245
5303
  const restoreEditor = () => {
5246
5304
  this.editorContainer.clear();
5247
5305
  this.editorContainer.addChild(this.editor);
5248
5306
  this.ui.setFocus(this.editor);
5249
5307
  this.ui.requestRender();
5250
5308
  };
5251
- const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
5309
+ const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => {
5310
+ restoreEditor();
5311
+ onBack?.();
5312
+ }, providerOption.name, `${providerOption.name} setup`);
5252
5313
  dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside ${APP_NAME}.`, [], true);
5253
5314
  this.editorContainer.clear();
5254
5315
  this.editorContainer.addChild(dialog);
5255
5316
  this.ui.setFocus(dialog);
5256
5317
  this.ui.requestRender();
5257
5318
  }
5258
- async showApiKeyLoginDialog(providerId, providerName) {
5319
+ async showApiKeyLoginDialog(providerId, providerName, onBack) {
5259
5320
  const previousModel = this.session.model;
5260
5321
  const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
5261
5322
  // Completion handled below
@@ -5288,12 +5349,15 @@ export class InteractiveMode {
5288
5349
  if (error instanceof CredentialSynchronizationError) {
5289
5350
  this.showError(`Saved API key for ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
5290
5351
  }
5291
- else if (errorMsg !== "Login cancelled") {
5352
+ else if (errorMsg === "Login cancelled") {
5353
+ onBack?.();
5354
+ }
5355
+ else {
5292
5356
  this.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);
5293
5357
  }
5294
5358
  }
5295
5359
  }
5296
- showAuthSelect(dialog, prompt) {
5360
+ showAuthSelect(dialog, prompt, providerId) {
5297
5361
  return new Promise((resolve, reject) => {
5298
5362
  const restoreDialog = () => {
5299
5363
  this.editorContainer.clear();
@@ -5312,17 +5376,17 @@ export class InteractiveMode {
5312
5376
  }, () => {
5313
5377
  restoreDialog();
5314
5378
  reject(new Error("Login cancelled"));
5315
- });
5379
+ }, { description: providerId === RADIUS_PROVIDER_ID ? RADIUS_LOGIN_INTRO : undefined });
5316
5380
  this.editorContainer.clear();
5317
5381
  this.editorContainer.addChild(selector);
5318
5382
  this.ui.setFocus(selector);
5319
5383
  this.ui.requestRender();
5320
5384
  });
5321
5385
  }
5322
- async showAuthPrompt(dialog, prompt) {
5386
+ async showAuthPrompt(dialog, prompt, providerId) {
5323
5387
  let response;
5324
5388
  if (prompt.type === "select") {
5325
- response = this.showAuthSelect(dialog, prompt);
5389
+ response = this.showAuthSelect(dialog, prompt, providerId);
5326
5390
  }
5327
5391
  else if (prompt.type === "manual_code") {
5328
5392
  response = dialog.showManualInput(prompt.message);
@@ -5366,11 +5430,11 @@ export class InteractiveMode {
5366
5430
  async loginProvider(dialog, providerId, method) {
5367
5431
  await this.session.modelRuntime.login(providerId, method, {
5368
5432
  signal: dialog.signal,
5369
- prompt: (prompt) => this.showAuthPrompt(dialog, prompt),
5433
+ prompt: (prompt) => this.showAuthPrompt(dialog, prompt, providerId),
5370
5434
  notify: (event) => this.notifyAuthDialog(dialog, event),
5371
5435
  }, { getDeviceId: () => this.settingsManager.getOrCreateDeviceId() });
5372
5436
  }
5373
- async showLoginDialog(providerId, providerName) {
5437
+ async showLoginDialog(providerId, providerName, onBack) {
5374
5438
  const previousModel = this.session.model;
5375
5439
  const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => { }, providerName);
5376
5440
  this.editorContainer.clear();
@@ -5387,6 +5451,8 @@ export class InteractiveMode {
5387
5451
  await this.loginProvider(dialog, providerId, "oauth");
5388
5452
  restoreEditor();
5389
5453
  await this.completeProviderAuthentication(providerId, providerName, "oauth", previousModel);
5454
+ if (providerId === RADIUS_PROVIDER_ID)
5455
+ this.offerRadiusMcpServer(providerId, providerName);
5390
5456
  }
5391
5457
  catch (error) {
5392
5458
  restoreEditor();
@@ -5394,11 +5460,58 @@ export class InteractiveMode {
5394
5460
  if (error instanceof CredentialSynchronizationError) {
5395
5461
  this.showError(`Logged in to ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
5396
5462
  }
5397
- else if (errorMsg !== "Login cancelled") {
5463
+ else if (errorMsg === "Login cancelled") {
5464
+ onBack?.();
5465
+ }
5466
+ else {
5398
5467
  this.showError(`Failed to login to ${providerName}: ${errorMsg}`);
5399
5468
  }
5400
5469
  }
5401
5470
  }
5471
+ /**
5472
+ * Offer to point the Radius MCP server in the global mcp.json at the Radius login, adding the server
5473
+ * when missing. Nothing is asked when a global server already uses this login.
5474
+ */
5475
+ offerRadiusMcpServer(providerId, providerName) {
5476
+ const mcpPath = path.join(getAgentDir(), "mcp.json");
5477
+ const normalizeUrl = (url) => url.replace(/\/+$/u, "");
5478
+ const { servers } = loadMcpConfig({
5479
+ agentDir: getAgentDir(),
5480
+ cwd: this.sessionManager.getCwd(),
5481
+ projectTrusted: false,
5482
+ });
5483
+ const existing = servers.find((server) => "url" in server.config && normalizeUrl(server.config.url) === normalizeUrl(RADIUS_MCP_URL));
5484
+ if (existing && "url" in existing.config && existing.config.auth?.provider === providerId)
5485
+ return;
5486
+ let name = existing?.name ?? "radius";
5487
+ if (!existing && servers.some((server) => server.name === name))
5488
+ name = "radius-mcp";
5489
+ const config = existing && "url" in existing.config
5490
+ ? { ...existing.config, auth: { provider: providerId } }
5491
+ : { url: RADIUS_MCP_URL, auth: { provider: providerId } };
5492
+ // `auth` replaces the MCP OAuth sign-in.
5493
+ delete config.oauth;
5494
+ this.showSelector((done) => {
5495
+ const selector = new ExtensionSelectorComponent(`Configure ${providerName} MCP in ${mcpPath}?`, ["Yes", "No"], (option) => {
5496
+ done();
5497
+ if (option !== "Yes")
5498
+ return;
5499
+ try {
5500
+ addMcpServerConfig(mcpPath, name, config);
5501
+ }
5502
+ catch (error) {
5503
+ this.showError(`Could not update ${mcpPath}: ${error instanceof Error ? error.message : String(error)}`);
5504
+ return;
5505
+ }
5506
+ // The MCP extension reads mcp.json when the session starts.
5507
+ void this.handleReloadCommand();
5508
+ }, () => {
5509
+ done();
5510
+ this.ui.requestRender();
5511
+ });
5512
+ return { component: selector, focus: selector };
5513
+ });
5514
+ }
5402
5515
  // =========================================================================
5403
5516
  // Command handlers
5404
5517
  // =========================================================================