gsd-pi 2.24.0 → 2.26.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 (206) hide show
  1. package/README.md +13 -3
  2. package/dist/headless.js +24 -4
  3. package/dist/models-resolver.d.ts +0 -11
  4. package/dist/models-resolver.js +0 -15
  5. package/dist/resource-loader.d.ts +0 -1
  6. package/dist/resource-loader.js +0 -9
  7. package/dist/resources/GSD-WORKFLOW.md +12 -9
  8. package/dist/resources/extensions/async-jobs/index.ts +9 -1
  9. package/dist/resources/extensions/bg-shell/index.ts +3 -2
  10. package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
  11. package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
  12. package/dist/resources/extensions/gsd/activity-log.ts +5 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.ts +14 -0
  14. package/dist/resources/extensions/gsd/auto-recovery.ts +7 -4
  15. package/dist/resources/extensions/gsd/auto-worktree.ts +132 -3
  16. package/dist/resources/extensions/gsd/auto.ts +265 -48
  17. package/dist/resources/extensions/gsd/cache.ts +3 -1
  18. package/dist/resources/extensions/gsd/doctor-proactive.ts +7 -6
  19. package/dist/resources/extensions/gsd/doctor.ts +26 -1
  20. package/dist/resources/extensions/gsd/files.ts +13 -2
  21. package/dist/resources/extensions/gsd/git-service.ts +74 -14
  22. package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
  23. package/dist/resources/extensions/gsd/guided-flow.ts +54 -22
  24. package/dist/resources/extensions/gsd/index.ts +62 -8
  25. package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
  26. package/dist/resources/extensions/gsd/memory-store.ts +441 -0
  27. package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
  28. package/dist/resources/extensions/gsd/migrate/writer.ts +39 -0
  29. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
  30. package/dist/resources/extensions/gsd/preferences.ts +2 -1
  31. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  32. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
  33. package/dist/resources/extensions/gsd/prompts/discuss.md +5 -5
  34. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  35. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  36. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  37. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/queue.md +3 -3
  39. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  40. package/dist/resources/extensions/gsd/roadmap-slices.ts +45 -1
  41. package/dist/resources/extensions/gsd/state.ts +17 -6
  42. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  43. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  44. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
  45. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
  46. package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  47. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  48. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  49. package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  50. package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  51. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +13 -7
  52. package/dist/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
  53. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  54. package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
  55. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  56. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  57. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  58. package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
  59. package/dist/resources/extensions/gsd/types.ts +2 -0
  60. package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
  61. package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  62. package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
  63. package/dist/resources/extensions/gsd/worktree.ts +9 -2
  64. package/dist/resources/extensions/search-the-web/native-search.ts +19 -5
  65. package/dist/resources/extensions/shared/path-display.ts +19 -0
  66. package/package.json +1 -6
  67. package/packages/pi-agent-core/dist/agent-loop.js +2 -0
  68. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  69. package/packages/pi-agent-core/src/agent-loop.ts +2 -0
  70. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  71. package/packages/pi-ai/dist/providers/anthropic.js +64 -0
  72. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  73. package/packages/pi-ai/dist/providers/mistral.js +3 -0
  74. package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
  75. package/packages/pi-ai/dist/types.d.ts +23 -1
  76. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  77. package/packages/pi-ai/dist/types.js.map +1 -1
  78. package/packages/pi-ai/src/providers/anthropic.ts +65 -1
  79. package/packages/pi-ai/src/providers/mistral.ts +3 -0
  80. package/packages/pi-ai/src/types.ts +19 -1
  81. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +7 -0
  82. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  83. package/packages/pi-coding-agent/dist/core/agent-session.js +32 -0
  84. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  85. package/packages/pi-coding-agent/dist/core/keybindings.js +1 -1
  86. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  87. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/lsp/client.js +12 -1
  89. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  91. package/packages/pi-coding-agent/dist/core/lsp/index.js +7 -0
  92. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  93. package/packages/pi-coding-agent/dist/core/sdk.d.ts +2 -2
  94. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  95. package/packages/pi-coding-agent/dist/core/sdk.js +8 -3
  96. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  97. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  98. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  99. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  100. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  103. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -1
  106. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/index.d.ts +2 -1
  108. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/index.js +5 -1
  110. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +41 -3
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +301 -62
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +5 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +135 -30
  121. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts +8 -0
  123. package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts.map +1 -0
  124. package/packages/pi-coding-agent/dist/tests/path-display.test.js +60 -0
  125. package/packages/pi-coding-agent/dist/tests/path-display.test.js.map +1 -0
  126. package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/utils/clipboard-image.js +32 -6
  128. package/packages/pi-coding-agent/dist/utils/clipboard-image.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/utils/path-display.d.ts +34 -0
  130. package/packages/pi-coding-agent/dist/utils/path-display.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/utils/path-display.js +36 -0
  132. package/packages/pi-coding-agent/dist/utils/path-display.js.map +1 -0
  133. package/packages/pi-coding-agent/src/core/agent-session.ts +36 -0
  134. package/packages/pi-coding-agent/src/core/keybindings.ts +1 -1
  135. package/packages/pi-coding-agent/src/core/lsp/client.ts +11 -1
  136. package/packages/pi-coding-agent/src/core/lsp/index.ts +7 -0
  137. package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
  138. package/packages/pi-coding-agent/src/core/settings-manager.ts +11 -0
  139. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  140. package/packages/pi-coding-agent/src/core/system-prompt.ts +2 -1
  141. package/packages/pi-coding-agent/src/index.ts +15 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +347 -62
  143. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +124 -4
  145. package/packages/pi-coding-agent/src/tests/path-display.test.ts +85 -0
  146. package/packages/pi-coding-agent/src/utils/clipboard-image.ts +33 -6
  147. package/packages/pi-coding-agent/src/utils/path-display.ts +36 -0
  148. package/src/resources/GSD-WORKFLOW.md +12 -9
  149. package/src/resources/extensions/async-jobs/index.ts +9 -1
  150. package/src/resources/extensions/bg-shell/index.ts +3 -2
  151. package/src/resources/extensions/bg-shell/overlay.ts +18 -17
  152. package/src/resources/extensions/get-secrets-from-user.ts +5 -23
  153. package/src/resources/extensions/gsd/activity-log.ts +5 -3
  154. package/src/resources/extensions/gsd/auto-prompts.ts +14 -0
  155. package/src/resources/extensions/gsd/auto-recovery.ts +7 -4
  156. package/src/resources/extensions/gsd/auto-worktree.ts +132 -3
  157. package/src/resources/extensions/gsd/auto.ts +265 -48
  158. package/src/resources/extensions/gsd/cache.ts +3 -1
  159. package/src/resources/extensions/gsd/doctor-proactive.ts +7 -6
  160. package/src/resources/extensions/gsd/doctor.ts +26 -1
  161. package/src/resources/extensions/gsd/files.ts +13 -2
  162. package/src/resources/extensions/gsd/git-service.ts +74 -14
  163. package/src/resources/extensions/gsd/gsd-db.ts +78 -1
  164. package/src/resources/extensions/gsd/guided-flow.ts +54 -22
  165. package/src/resources/extensions/gsd/index.ts +62 -8
  166. package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
  167. package/src/resources/extensions/gsd/memory-store.ts +441 -0
  168. package/src/resources/extensions/gsd/migrate/command.ts +2 -2
  169. package/src/resources/extensions/gsd/migrate/writer.ts +39 -0
  170. package/src/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
  171. package/src/resources/extensions/gsd/preferences.ts +2 -1
  172. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
  174. package/src/resources/extensions/gsd/prompts/discuss.md +5 -5
  175. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  176. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  177. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  178. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  179. package/src/resources/extensions/gsd/prompts/queue.md +3 -3
  180. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  181. package/src/resources/extensions/gsd/roadmap-slices.ts +45 -1
  182. package/src/resources/extensions/gsd/state.ts +17 -6
  183. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  184. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  185. package/src/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
  186. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
  187. package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  188. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  189. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  190. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  191. package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  192. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +13 -7
  193. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
  194. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  195. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
  196. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  197. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  198. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  199. package/src/resources/extensions/gsd/triage-ui.ts +1 -1
  200. package/src/resources/extensions/gsd/types.ts +2 -0
  201. package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
  202. package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  203. package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
  204. package/src/resources/extensions/gsd/worktree.ts +9 -2
  205. package/src/resources/extensions/search-the-web/native-search.ts +19 -5
  206. package/src/resources/extensions/shared/path-display.ts +19 -0
@@ -885,6 +885,32 @@ export class InteractiveMode {
885
885
  const registeredTool = tools.find((t) => t.definition.name === toolName);
886
886
  return registeredTool?.definition;
887
887
  }
888
+ /**
889
+ * Format web search result content for display in the TUI.
890
+ */
891
+ formatWebSearchResult(content) {
892
+ if (!content)
893
+ return "Web search completed";
894
+ // Error result
895
+ if (typeof content === "object" && "type" in content && content.type === "web_search_tool_result_error") {
896
+ const error = content;
897
+ return `Search error: ${error.error_code || "unknown"}`;
898
+ }
899
+ // Array of search results
900
+ if (Array.isArray(content)) {
901
+ const results = content.filter((r) => r.type === "web_search_result");
902
+ if (results.length === 0)
903
+ return "No results found";
904
+ return results
905
+ .map((r) => {
906
+ const title = r.title || "Untitled";
907
+ const url = r.url || "";
908
+ return `${title}\n ${url}`;
909
+ })
910
+ .join("\n");
911
+ }
912
+ return "Web search completed";
913
+ }
888
914
  /**
889
915
  * Set up keyboard shortcuts registered by extensions.
890
916
  */
@@ -1649,6 +1675,12 @@ export class InteractiveMode {
1649
1675
  this.handleThinkingCommand(arg);
1650
1676
  return;
1651
1677
  }
1678
+ if (text === "/edit-mode" || text.startsWith("/edit-mode ")) {
1679
+ const arg = text.startsWith("/edit-mode ") ? text.slice(11).trim() : undefined;
1680
+ this.editor.setText("");
1681
+ this.handleEditModeCommand(arg);
1682
+ return;
1683
+ }
1652
1684
  if (text === "/debug") {
1653
1685
  this.handleDebugCommand();
1654
1686
  this.editor.setText("");
@@ -1793,6 +1825,31 @@ export class InteractiveMode {
1793
1825
  }
1794
1826
  }
1795
1827
  }
1828
+ else if (content.type === "serverToolUse") {
1829
+ // Server-side tool (e.g., native web search) — show as pending tool execution
1830
+ if (!this.pendingTools.has(content.id)) {
1831
+ const component = new ToolExecutionComponent(content.name, content.input ?? {}, {
1832
+ showImages: this.settingsManager.getShowImages(),
1833
+ }, undefined, this.ui);
1834
+ component.setExpanded(this.toolOutputExpanded);
1835
+ this.chatContainer.addChild(component);
1836
+ this.pendingTools.set(content.id, component);
1837
+ }
1838
+ }
1839
+ else if (content.type === "webSearchResult") {
1840
+ // Server-side tool result — resolve the pending server tool execution
1841
+ const component = this.pendingTools.get(content.toolUseId);
1842
+ if (component) {
1843
+ const searchContent = content.content;
1844
+ const isError = searchContent && typeof searchContent === "object" && "type" in searchContent && searchContent.type === "web_search_tool_result_error";
1845
+ const resultText = this.formatWebSearchResult(searchContent);
1846
+ component.updateResult({
1847
+ content: [{ type: "text", text: resultText }],
1848
+ isError: !!isError,
1849
+ });
1850
+ this.pendingTools.delete(content.toolUseId);
1851
+ }
1852
+ }
1796
1853
  }
1797
1854
  this.ui.requestRender();
1798
1855
  }
@@ -2129,6 +2186,27 @@ export class InteractiveMode {
2129
2186
  this.pendingTools.set(content.id, component);
2130
2187
  }
2131
2188
  }
2189
+ else if (content.type === "serverToolUse") {
2190
+ // Server-side tool (e.g., native web search)
2191
+ const component = new ToolExecutionComponent(content.name, content.input ?? {}, { showImages: this.settingsManager.getShowImages() }, undefined, this.ui);
2192
+ component.setExpanded(this.toolOutputExpanded);
2193
+ this.chatContainer.addChild(component);
2194
+ // Find matching webSearchResult in this message's content
2195
+ const resultBlock = message.content.find((c) => c.type === "webSearchResult" && c.toolUseId === content.id);
2196
+ if (resultBlock && resultBlock.type === "webSearchResult") {
2197
+ const searchContent = resultBlock.content;
2198
+ const isError = searchContent && typeof searchContent === "object" && "type" in searchContent && searchContent.type === "web_search_tool_result_error";
2199
+ const resultText = this.formatWebSearchResult(searchContent);
2200
+ component.updateResult({
2201
+ content: [{ type: "text", text: resultText }],
2202
+ isError: !!isError,
2203
+ });
2204
+ }
2205
+ else {
2206
+ // No result yet (aborted stream?) — show as pending
2207
+ this.pendingTools.set(content.id, component);
2208
+ }
2209
+ }
2132
2210
  }
2133
2211
  }
2134
2212
  else if (message.role === "toolResult") {
@@ -2316,6 +2394,24 @@ export class InteractiveMode {
2316
2394
  }
2317
2395
  this.showThinkingSelector();
2318
2396
  }
2397
+ handleEditModeCommand(arg) {
2398
+ const modes = ["standard", "hashline"];
2399
+ if (arg) {
2400
+ const mode = arg.toLowerCase();
2401
+ if (!modes.includes(mode)) {
2402
+ this.showStatus(`Invalid edit mode "${arg}". Available: standard, hashline`);
2403
+ return;
2404
+ }
2405
+ this.session.setEditMode(mode);
2406
+ this.showStatus(`Edit mode: ${mode}${mode === "hashline" ? " (LINE#ID anchored edits)" : " (text-match edits)"}`);
2407
+ return;
2408
+ }
2409
+ // Toggle
2410
+ const current = this.session.editMode;
2411
+ const next = current === "standard" ? "hashline" : "standard";
2412
+ this.session.setEditMode(next);
2413
+ this.showStatus(`Edit mode: ${next}${next === "hashline" ? " (LINE#ID anchored edits)" : " (text-match edits)"}`);
2414
+ }
2319
2415
  showThinkingSelector() {
2320
2416
  const availableLevels = this.session.getAvailableThinkingLevels();
2321
2417
  this.showSelector((done) => {
@@ -3165,41 +3261,50 @@ export class InteractiveMode {
3165
3261
  }
3166
3262
  }
3167
3263
  this.showSelector((done) => {
3168
- const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, async (providerId) => {
3264
+ const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, (providerId) => {
3169
3265
  done();
3170
- if (mode === "login") {
3171
- await this.showLoginDialog(providerId);
3172
- }
3173
- else {
3174
- // Logout flow
3175
- const providerInfo = this.session.modelRegistry.authStorage
3176
- .getOAuthProviders()
3177
- .find((p) => p.id === providerId);
3178
- const providerName = providerInfo?.name || providerId;
3179
- try {
3180
- this.session.modelRegistry.authStorage.logout(providerId);
3181
- this.session.modelRegistry.refresh();
3182
- await this.updateAvailableProviderCount();
3183
- // Auto-switch model if current model belongs to the logged-out provider
3184
- const currentModel = this.session.model;
3185
- if (currentModel?.provider === providerId) {
3186
- try {
3187
- const available = this.session.modelRegistry.getAvailable();
3188
- const fallback = available.find((m) => m.provider !== providerId);
3189
- if (fallback) {
3190
- await this.session.setModel(fallback);
3266
+ // OAuthSelectorComponent calls this synchronously (no await),
3267
+ // so we must catch async errors here to prevent unhandled rejections
3268
+ // when the user cancels the login dialog (#821).
3269
+ const handleAsync = async () => {
3270
+ if (mode === "login") {
3271
+ await this.showLoginDialog(providerId);
3272
+ }
3273
+ else {
3274
+ // Logout flow
3275
+ const providerInfo = this.session.modelRegistry.authStorage
3276
+ .getOAuthProviders()
3277
+ .find((p) => p.id === providerId);
3278
+ const providerName = providerInfo?.name || providerId;
3279
+ try {
3280
+ this.session.modelRegistry.authStorage.logout(providerId);
3281
+ this.session.modelRegistry.refresh();
3282
+ await this.updateAvailableProviderCount();
3283
+ // Auto-switch model if current model belongs to the logged-out provider
3284
+ const currentModel = this.session.model;
3285
+ if (currentModel?.provider === providerId) {
3286
+ try {
3287
+ const available = this.session.modelRegistry.getAvailable();
3288
+ const fallback = available.find((m) => m.provider !== providerId);
3289
+ if (fallback) {
3290
+ await this.session.setModel(fallback);
3291
+ }
3292
+ }
3293
+ catch {
3294
+ // Model switch failed — user can manually switch via /model
3191
3295
  }
3192
3296
  }
3193
- catch {
3194
- // Model switch failed — user can manually switch via /model
3195
- }
3297
+ this.showStatus(`Logged out of ${providerName}`);
3298
+ }
3299
+ catch (error) {
3300
+ this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
3196
3301
  }
3197
- this.showStatus(`Logged out of ${providerName}`);
3198
- }
3199
- catch (error) {
3200
- this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
3201
3302
  }
3202
- }
3303
+ };
3304
+ handleAsync().catch(() => {
3305
+ // Swallow — showLoginDialog already handles its own errors.
3306
+ // This prevents unhandled rejections when login is cancelled.
3307
+ });
3203
3308
  }, () => {
3204
3309
  done();
3205
3310
  this.ui.requestRender();