@code-yeongyu/senpi 2026.9.19-2 → 2026.9.20

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 (171) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/bundle/chunks/{app-server-command-QBJ4GPN7.js → app-server-command-B35FQUTE.js} +1 -1
  3. package/dist/bundle/chunks/{bun-extension-importer-JQZRPNJH.js → bun-extension-importer-HHH4TV4B.js} +3 -3
  4. package/dist/bundle/chunks/{chunk-YRN3PCTM.js → chunk-DQZB4GVV.js} +4 -4
  5. package/dist/bundle/chunks/{chunk-EHSDTQ6I.js → chunk-E5R67F7G.js} +37 -37
  6. package/dist/bundle/chunks/{chunk-5E5TLB76.js → chunk-TWGC3TBR.js} +2 -2
  7. package/dist/bundle/chunks/{chunk-RLRLQRI2.js → chunk-V2ZB62DG.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-3FRTRCR6.js → chunk-YXQG2SUX.js} +2 -2
  9. package/dist/bundle/chunks/{chunk-PLZK7ABD.js → chunk-Z4LHCTJ6.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-3Q4WGKFA.js → chunk-ZMFHNH6U.js} +1 -1
  11. package/dist/bundle/chunks/{cli-main-MLJ2H637.js → cli-main-USVT7JMN.js} +1 -1
  12. package/dist/bundle/chunks/{interactive-host-runtime-AITXUDCT.js → interactive-host-runtime-ELBAZ4LN.js} +1 -1
  13. package/dist/bundle/chunks/{interactive-mode-7C75IXNF.js → interactive-mode-R6225BKI.js} +1 -1
  14. package/dist/bundle/chunks/{list-tips-KTDNGQUK.js → list-tips-Z7T2ACVU.js} +1 -1
  15. package/dist/bundle/chunks/{multi-session-host-2YCJKQOL.js → multi-session-host-QNEBJW2P.js} +3 -3
  16. package/dist/bundle/chunks/{package-manager-cli-4ANSELYT.js → package-manager-cli-4MVJ3JSE.js} +1 -1
  17. package/dist/bundle/chunks/{rpc-mode-OPFQIWFP.js → rpc-mode-CXVDRIBU.js} +1 -1
  18. package/dist/bundle/chunks/{session-picker-DXCITGLS.js → session-picker-6N666KQZ.js} +1 -1
  19. package/dist/bundle/chunks/session-worker.js +66 -66
  20. package/dist/bundle/cli.js +1 -1
  21. package/dist/bundle/index.js +1 -1
  22. package/dist/bundle/rpc-entry.js +1 -1
  23. package/dist/core/agent-session-services.d.ts.map +1 -1
  24. package/dist/core/agent-session-services.js +14 -1
  25. package/dist/core/agent-session-services.js.map +1 -1
  26. package/dist/core/agent-session.d.ts +45 -0
  27. package/dist/core/agent-session.d.ts.map +1 -1
  28. package/dist/core/agent-session.js +233 -11
  29. package/dist/core/agent-session.js.map +1 -1
  30. package/dist/core/extensions/builtin/ask-user/extension.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/ask-user/extension.js +16 -5
  32. package/dist/core/extensions/builtin/ask-user/extension.js.map +1 -1
  33. package/dist/core/extensions/builtin/ask-user/format.d.ts +5 -0
  34. package/dist/core/extensions/builtin/ask-user/format.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/ask-user/format.js +4 -0
  36. package/dist/core/extensions/builtin/ask-user/format.js.map +1 -1
  37. package/dist/core/extensions/builtin/ask-user/notify.d.ts +2 -0
  38. package/dist/core/extensions/builtin/ask-user/notify.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/ask-user/notify.js +2 -0
  40. package/dist/core/extensions/builtin/ask-user/notify.js.map +1 -1
  41. package/dist/core/extensions/builtin/ask-user/registry.d.ts +15 -2
  42. package/dist/core/extensions/builtin/ask-user/registry.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/ask-user/registry.js +13 -0
  44. package/dist/core/extensions/builtin/ask-user/registry.js.map +1 -1
  45. package/dist/core/extensions/builtin/ask-user/render.d.ts +6 -0
  46. package/dist/core/extensions/builtin/ask-user/render.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/ask-user/render.js +9 -0
  48. package/dist/core/extensions/builtin/ask-user/render.js.map +1 -1
  49. package/dist/core/extensions/builtin/ask-user/resume.d.ts.map +1 -1
  50. package/dist/core/extensions/builtin/ask-user/resume.js +59 -24
  51. package/dist/core/extensions/builtin/ask-user/resume.js.map +1 -1
  52. package/dist/core/extensions/builtin/ask-user/tool.d.ts +1 -1
  53. package/dist/core/extensions/builtin/ask-user/tool.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/ask-user/tool.js +98 -17
  55. package/dist/core/extensions/builtin/ask-user/tool.js.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts +18 -0
  57. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -1
  58. package/dist/core/extensions/builtin/compaction/model-usability-budget.js +16 -8
  59. package/dist/core/extensions/builtin/compaction/model-usability-budget.js.map +1 -1
  60. package/dist/core/extensions/builtin/compaction/switch-admission.d.ts +23 -0
  61. package/dist/core/extensions/builtin/compaction/switch-admission.d.ts.map +1 -0
  62. package/dist/core/extensions/builtin/compaction/switch-admission.js +19 -0
  63. package/dist/core/extensions/builtin/compaction/switch-admission.js.map +1 -0
  64. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/goal/index.js +10 -0
  66. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  67. package/dist/core/extensions/bun-extension-commonjs.d.ts +1 -1
  68. package/dist/core/extensions/bun-extension-commonjs.d.ts.map +1 -1
  69. package/dist/core/extensions/bun-extension-commonjs.js +3 -3
  70. package/dist/core/extensions/bun-extension-commonjs.js.map +1 -1
  71. package/dist/core/extensions/bun-extension-importer.d.ts.map +1 -1
  72. package/dist/core/extensions/bun-extension-importer.js +4 -1
  73. package/dist/core/extensions/bun-extension-importer.js.map +1 -1
  74. package/dist/core/extensions/bun-extension-registry.d.ts.map +1 -1
  75. package/dist/core/extensions/bun-extension-registry.js +5 -9
  76. package/dist/core/extensions/bun-extension-registry.js.map +1 -1
  77. package/dist/core/resolved-paths-memo.d.ts +31 -0
  78. package/dist/core/resolved-paths-memo.d.ts.map +1 -0
  79. package/dist/core/resolved-paths-memo.js +57 -0
  80. package/dist/core/resolved-paths-memo.js.map +1 -0
  81. package/dist/core/resource-loader.d.ts +4 -1
  82. package/dist/core/resource-loader.d.ts.map +1 -1
  83. package/dist/core/resource-loader.js +25 -12
  84. package/dist/core/resource-loader.js.map +1 -1
  85. package/dist/core/retry-fallback/settings.d.ts +10 -2
  86. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  87. package/dist/core/retry-fallback/settings.js +28 -6
  88. package/dist/core/retry-fallback/settings.js.map +1 -1
  89. package/dist/core/skill-discovery.d.ts.map +1 -1
  90. package/dist/core/skill-discovery.js +11 -1
  91. package/dist/core/skill-discovery.js.map +1 -1
  92. package/dist/main.d.ts +10 -0
  93. package/dist/main.d.ts.map +1 -1
  94. package/dist/main.js +19 -2
  95. package/dist/main.js.map +1 -1
  96. package/dist/modes/interactive/components/ask-user-answer-chip.d.ts +2 -5
  97. package/dist/modes/interactive/components/ask-user-answer-chip.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/ask-user-answer-chip.js +1 -4
  99. package/dist/modes/interactive/components/ask-user-answer-chip.js.map +1 -1
  100. package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
  101. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/assistant-message.js +8 -0
  103. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  104. package/dist/modes/interactive/components/assistant-render-descriptors.d.ts +1 -0
  105. package/dist/modes/interactive/components/assistant-render-descriptors.d.ts.map +1 -1
  106. package/dist/modes/interactive/components/assistant-render-descriptors.js +4 -0
  107. package/dist/modes/interactive/components/assistant-render-descriptors.js.map +1 -1
  108. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  109. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/status-indicator.js +15 -2
  111. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  112. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  113. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  114. package/dist/modes/interactive/interactive-mode.js +81 -6
  115. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  116. package/dist/modes/interactive/provider-error-presentation.d.ts +18 -0
  117. package/dist/modes/interactive/provider-error-presentation.d.ts.map +1 -0
  118. package/dist/modes/interactive/provider-error-presentation.js +100 -0
  119. package/dist/modes/interactive/provider-error-presentation.js.map +1 -0
  120. package/dist/modes/rpc/rpc-types.d.ts +18 -0
  121. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  122. package/dist/modes/rpc/rpc-types.js.map +1 -1
  123. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  124. package/dist/modes/rpc/session-command-router.js +14 -0
  125. package/dist/modes/rpc/session-command-router.js.map +1 -1
  126. package/dist/modes/rpc/session-event-writer.d.ts +9 -1
  127. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  128. package/dist/modes/rpc/session-event-writer.js +21 -0
  129. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  130. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  131. package/dist/modes/rpc/session-registry.js +9 -2
  132. package/dist/modes/rpc/session-registry.js.map +1 -1
  133. package/dist/modes/rpc/session-worker-requests.d.ts +10 -1
  134. package/dist/modes/rpc/session-worker-requests.d.ts.map +1 -1
  135. package/dist/modes/rpc/session-worker-requests.js +8 -2
  136. package/dist/modes/rpc/session-worker-requests.js.map +1 -1
  137. package/dist/modes/rpc/session-worker-requests.test.d.ts +2 -0
  138. package/dist/modes/rpc/session-worker-requests.test.d.ts.map +1 -0
  139. package/dist/modes/rpc/session-worker-requests.test.js +44 -0
  140. package/dist/modes/rpc/session-worker-requests.test.js.map +1 -0
  141. package/docs/rpc.md +7 -0
  142. package/docs/settings.md +1 -1
  143. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  144. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  145. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  146. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  147. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  148. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  149. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  150. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  151. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  152. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  153. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  154. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  155. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  156. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  157. package/node_modules/@earendil-works/pi-tui/dist/native-module-path.d.ts.map +1 -1
  158. package/node_modules/@earendil-works/pi-tui/dist/native-module-path.js +26 -3
  159. package/node_modules/@earendil-works/pi-tui/dist/native-module-path.js.map +1 -1
  160. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts.map +1 -1
  161. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js +13 -6
  162. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js.map +1 -1
  163. package/node_modules/@earendil-works/pi-tui/dist/tui-main-screen.d.ts.map +1 -1
  164. package/node_modules/@earendil-works/pi-tui/dist/tui-main-screen.js +11 -3
  165. package/node_modules/@earendil-works/pi-tui/dist/tui-main-screen.js.map +1 -1
  166. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +15 -2
  167. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
  168. package/node_modules/@earendil-works/pi-tui/dist/tui.js +45 -2
  169. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
  170. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  171. package/package.json +8 -8
@@ -20,6 +20,7 @@ import { resolveChangelogSource } from "../../core/changelog-source.js";
20
20
  import { collectEntriesForBranchSummary } from "../../core/compaction/branch-summarization.js";
21
21
  import { AssistantEditError, assistantTextEquals } from "../../core/edited-assistant-message.js";
22
22
  import { formatUserMessage } from "../../core/extensions/builtin/ask-user/format.js";
23
+ import { askUserRenderers } from "../../core/extensions/builtin/ask-user/render.js";
23
24
  import { buildNoticeBox } from "../../core/extensions/notice/index.js";
24
25
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
25
26
  import { appendUncaughtCrashLog } from "../../core/hidden-stdout-log.js";
@@ -105,6 +106,7 @@ import { buildResourceScopeGroups, formatResourceScopeGroups, getDisplaySourceIn
105
106
  import { describeLoginFailure } from "./login-outcome.js";
106
107
  import { refreshModelCatalogs } from "./model-catalog-refresh.js";
107
108
  import { getModelSearchText } from "./model-search.js";
109
+ import { isNetworkProviderError, isNetworkProviderMessage, ProviderErrorPresentation, } from "./provider-error-presentation.js";
108
110
  import { isRiskyMainModel, RISKY_MAIN_MODEL_WARNING } from "./risky-main-model-warning.js";
109
111
  import { DEFAULT_SMOOTH_FPS, StreamingRevealController } from "./streaming-reveal.js";
110
112
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getSelectListTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
@@ -2066,7 +2068,10 @@ export class InteractiveMode {
2066
2068
  * whatever this returns, so they never reach into the tool registry themselves.
2067
2069
  */
2068
2070
  getRegisteredToolDefinition(toolName) {
2069
- return withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName));
2071
+ // A question card can stream while a reload is in flight, when the registry no longer holds the
2072
+ // ask-user tools and session_start has not re-synchronized them yet. Its renderers do not depend
2073
+ // on the registration, so fall back to them instead of dumping the raw arguments.
2074
+ return withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName) ?? askUserRenderers(toolName));
2070
2075
  }
2071
2076
  getMarkdownTransformers() {
2072
2077
  return [this.mermaidMarkdownTransformer, ...this.session.extensionRunner.getMarkdownTransformers()];
@@ -3004,6 +3009,7 @@ export class InteractiveMode {
3004
3009
  tui: this.ui,
3005
3010
  timeoutMs: opts?.timeout ?? request.timeoutMs,
3006
3011
  getDeadlineAtMs: opts?.getDeadlineAtMs,
3012
+ initialDraft: opts?.initialDraft,
3007
3013
  onProgress: opts?.onProgress,
3008
3014
  });
3009
3015
  this.disposeActiveSelector();
@@ -3048,7 +3054,7 @@ export class InteractiveMode {
3048
3054
  completion: completion.promise,
3049
3055
  getDeadlineAtMs: opts?.getDeadlineAtMs,
3050
3056
  onProgress: opts?.onProgress,
3051
- draft: { answers: {} },
3057
+ draft: opts?.initialDraft ?? { answers: {} },
3052
3058
  finish: (response) => {
3053
3059
  if (this.pendingQuestions.get(request.requestId) !== state)
3054
3060
  return;
@@ -4069,6 +4075,10 @@ export class InteractiveMode {
4069
4075
  `more tokens for its ${event.contextWindow.toLocaleString()}-token window ` +
4070
4076
  `(${event.safetyMarginProfile} usability budget).`);
4071
4077
  break;
4078
+ case "model_change_pending":
4079
+ this.showWarning(event.notice);
4080
+ this.footer.invalidate();
4081
+ break;
4072
4082
  case "high_reasoning_warning":
4073
4083
  this.showHighReasoningWarning(event);
4074
4084
  break;
@@ -4087,6 +4097,7 @@ export class InteractiveMode {
4087
4097
  this.ui.requestRender();
4088
4098
  }
4089
4099
  else if (event.message.role === "user") {
4100
+ this.providerErrors?.newTurn();
4090
4101
  if (!this.optimisticUserEchoes.replaceNext(event.message))
4091
4102
  this.addMessageToChat(event.message);
4092
4103
  this.updatePendingMessagesDisplay();
@@ -4103,6 +4114,10 @@ export class InteractiveMode {
4103
4114
  break;
4104
4115
  case "message_update":
4105
4116
  if (this.streamingComponent && event.message.role === "assistant") {
4117
+ if (event.message.content.some((part) => part.type === "text" && part.text.trim())) {
4118
+ this.providerErrors?.clear();
4119
+ this.clearStatusIndicator("retry");
4120
+ }
4106
4121
  this.streamingMessage = event.message;
4107
4122
  this.streamingReveal.setTarget(assistantStreamingHeadMessage(event.message));
4108
4123
  for (const content of this.streamingMessage.content) {
@@ -4144,6 +4159,13 @@ export class InteractiveMode {
4144
4159
  }
4145
4160
  this.toolArgsReveal.flushAll();
4146
4161
  const renderedMessage = abortedMessageForRendering(this.streamingMessage, this.session.retryAttempt, this.session.currentAbortSource);
4162
+ if (isNetworkProviderMessage(renderedMessage)) {
4163
+ this.getProviderErrors().record(renderedMessage.errorMessage ?? "", this.toolOutputExpanded);
4164
+ }
4165
+ else if (renderedMessage.stopReason === "stop" || renderedMessage.stopReason === "toolUse") {
4166
+ this.providerErrors?.clear();
4167
+ this.clearStatusIndicator("retry");
4168
+ }
4147
4169
  let errorMessage = renderedMessage.errorMessage;
4148
4170
  this.syncTrailingAssistantText(renderedMessage);
4149
4171
  this.assistantTextSegments.clear();
@@ -4255,6 +4277,7 @@ export class InteractiveMode {
4255
4277
  break;
4256
4278
  case "agent_idle":
4257
4279
  this.agentIdle = true;
4280
+ this.providerErrors?.finish();
4258
4281
  if (this.pendingUserInputs.length === 0) {
4259
4282
  this.clearStatusIndicator("working");
4260
4283
  }
@@ -4263,6 +4286,12 @@ export class InteractiveMode {
4263
4286
  case "continuation_error":
4264
4287
  this.showError(sanitizeTerminalLabel(event.errorMessage));
4265
4288
  break;
4289
+ case "session_abort":
4290
+ this.providerErrors?.clear();
4291
+ this.clearStatusIndicator("retry");
4292
+ this.pendingZeroDelayRetryIndicator = undefined;
4293
+ this.ui.requestRender();
4294
+ break;
4266
4295
  case "compaction_start": {
4267
4296
  if (this.settingsManager.getShowTerminalProgress() && this.ui.terminal) {
4268
4297
  this.ui.terminal.setProgress(true);
@@ -4381,7 +4410,10 @@ export class InteractiveMode {
4381
4410
  }
4382
4411
  else if (event.errorMessage) {
4383
4412
  const errorMessage = sanitizeTerminalLabel(event.errorMessage);
4384
- if (event.reason === "manual") {
4413
+ if (isNetworkProviderError(errorMessage)) {
4414
+ this.getProviderErrors().finish(errorMessage);
4415
+ }
4416
+ else if (event.reason === "manual") {
4385
4417
  this.showError(errorMessage);
4386
4418
  }
4387
4419
  else {
@@ -4489,6 +4521,11 @@ export class InteractiveMode {
4489
4521
  this.setExtensionStatus(FALLBACK_STATUS_KEY, undefined);
4490
4522
  break;
4491
4523
  case "retry_fallback_exhausted":
4524
+ if (isNetworkProviderError(event.lastError)) {
4525
+ this.getProviderErrors().finish(event.lastError);
4526
+ this.setExtensionStatus(FALLBACK_STATUS_KEY, undefined);
4527
+ break;
4528
+ }
4492
4529
  this.showNoticeBox({
4493
4530
  title: `✕ Fallback chain exhausted · ${event.chainKey}`,
4494
4531
  tone: "error",
@@ -4506,6 +4543,9 @@ export class InteractiveMode {
4506
4543
  });
4507
4544
  break;
4508
4545
  case "auto_retry_start": {
4546
+ if (isNetworkProviderError(event.errorMessage)) {
4547
+ this.getProviderErrors().retrying(event.errorMessage, this.toolOutputExpanded);
4548
+ }
4509
4549
  // During retry waits, isStreaming flips false between attempts. The main Esc handler
4510
4550
  // keys off both isStreaming and retryAttempt so we keep the same close-out path here;
4511
4551
  // no separate retry-only handler is installed (the prior one only called
@@ -4540,14 +4580,25 @@ export class InteractiveMode {
4540
4580
  }
4541
4581
  this.clearStatusIndicator("retry");
4542
4582
  // Show error only on final failure (success shows normal response)
4543
- if (!event.success) {
4583
+ if (event.success || event.finalError === "Retry cancelled") {
4584
+ this.providerErrors?.clear();
4585
+ }
4586
+ else if (isNetworkProviderError(event.finalError)) {
4587
+ this.getProviderErrors().finish(event.finalError, event.attempt);
4588
+ }
4589
+ else {
4544
4590
  this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
4545
4591
  }
4546
4592
  this.ui.requestRender();
4547
4593
  break;
4548
4594
  }
4549
4595
  case "summarization_retry_scheduled": {
4550
- this.showError(event.errorMessage);
4596
+ if (isNetworkProviderError(event.errorMessage)) {
4597
+ this.getProviderErrors().retrying(event.errorMessage, this.toolOutputExpanded);
4598
+ }
4599
+ else {
4600
+ this.showError(event.errorMessage);
4601
+ }
4551
4602
  this.showSummarizationRetryStatusIndicator(event);
4552
4603
  break;
4553
4604
  }
@@ -4563,6 +4614,7 @@ export class InteractiveMode {
4563
4614
  break;
4564
4615
  }
4565
4616
  case "summarization_retry_finished": {
4617
+ this.providerErrors?.clear();
4566
4618
  this.clearStatusIndicator("retry");
4567
4619
  this.ui.requestRender();
4568
4620
  break;
@@ -4597,7 +4649,7 @@ export class InteractiveMode {
4597
4649
  showRetryStatusIndicatorWithCadence(event) {
4598
4650
  const refreshIntervalMs = largeSessionWorkingStatusInterval(this.sessionManager.getEntryCount(), DEFAULT_RETRY_STATUS_REFRESH_INTERVAL_MS, LARGE_SESSION_RETRY_STATUS_REFRESH_INTERVAL_MS);
4599
4651
  const indicator = refreshIntervalMs === DEFAULT_RETRY_STATUS_REFRESH_INTERVAL_MS ? undefined : { intervalMs: refreshIntervalMs };
4600
- this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs, indicator));
4652
+ this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs, indicator, isNetworkProviderError(event.errorMessage)));
4601
4653
  this.ui.requestRender();
4602
4654
  }
4603
4655
  /** Extract text content from a user message */
@@ -4701,6 +4753,8 @@ export class InteractiveMode {
4701
4753
  };
4702
4754
  }
4703
4755
  addMessageToChat(message, options) {
4756
+ if (message.role === "user")
4757
+ this.providerErrors?.newTurn();
4704
4758
  switch (message.role) {
4705
4759
  case "bashExecution": {
4706
4760
  const component = new BashExecutionComponent(message.command, this.ui, message.excludeFromContext);
@@ -4765,8 +4819,16 @@ export class InteractiveMode {
4765
4819
  break;
4766
4820
  }
4767
4821
  case "assistant": {
4822
+ if (isNetworkProviderMessage(message)) {
4823
+ this.getProviderErrors().record(message.errorMessage ?? "", this.toolOutputExpanded);
4824
+ this.providerErrors?.finish();
4825
+ }
4826
+ else if (message.stopReason === "stop" || message.stopReason === "toolUse") {
4827
+ this.providerErrors?.clear();
4828
+ }
4768
4829
  const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
4769
4830
  assistantComponent.setExpanded(this.toolOutputExpanded);
4831
+ assistantComponent.setProviderErrorOwned(isNetworkProviderMessage(message));
4770
4832
  this.chatContainer.addChild(assistantComponent);
4771
4833
  break;
4772
4834
  }
@@ -4786,6 +4848,7 @@ export class InteractiveMode {
4786
4848
  syncTrailingAssistantText(message) {
4787
4849
  if (!this.streamingComponent)
4788
4850
  return;
4851
+ this.streamingComponent.setProviderErrorOwned(isNetworkProviderMessage(message));
4789
4852
  const head = assistantStreamingHeadMessage(message);
4790
4853
  // Single writer: while smooth streaming paces the head (no toolCall block),
4791
4854
  // streamingReveal owns the streaming component. Overwriting the full head
@@ -4818,11 +4881,13 @@ export class InteractiveMode {
4818
4881
  const runMessage = { ...message, content: runBlocks };
4819
4882
  const existing = this.assistantTextSegments.get(runStart);
4820
4883
  if (existing) {
4884
+ existing.setProviderErrorOwned(isNetworkProviderMessage(message));
4821
4885
  existing.updateContent(runMessage, true);
4822
4886
  continue;
4823
4887
  }
4824
4888
  const segment = new AssistantMessageComponent(runMessage, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
4825
4889
  segment.setExpanded(this.toolOutputExpanded);
4890
+ segment.setProviderErrorOwned(isNetworkProviderMessage(message));
4826
4891
  this.assistantTextSegments.set(runStart, segment);
4827
4892
  const followingToolCall = content.slice(index).find((block) => block.type === "toolCall");
4828
4893
  const followingToolCallId = followingToolCall?.type === "toolCall" ? followingToolCall.id : undefined;
@@ -4859,6 +4924,7 @@ export class InteractiveMode {
4859
4924
  }, this.getRegisteredToolDefinition(toolName), this.ui, this.sessionManager.getCwd());
4860
4925
  }
4861
4926
  renderSessionItems(items, options = {}) {
4927
+ this.providerErrors = undefined;
4862
4928
  this.clearPendingTools();
4863
4929
  // The rebuilt transcript re-derives continuity notices from persisted
4864
4930
  // messages, so the tracker's suppression state must not survive the
@@ -5558,10 +5624,19 @@ export class InteractiveMode {
5558
5624
  this.ui.requestRender();
5559
5625
  }
5560
5626
  showError(errorMessage) {
5627
+ if (isNetworkProviderError(errorMessage, true)) {
5628
+ this.getProviderErrors().record(errorMessage, this.toolOutputExpanded);
5629
+ this.ui.requestRender();
5630
+ return;
5631
+ }
5561
5632
  this.chatContainer.addChild(new Spacer(1));
5562
5633
  this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${sanitizeTuiErrorMessage(errorMessage)}`), 1, 0));
5563
5634
  this.ui.requestRender();
5564
5635
  }
5636
+ getProviderErrors() {
5637
+ this.providerErrors ??= new ProviderErrorPresentation(this.chatContainer);
5638
+ return this.providerErrors;
5639
+ }
5565
5640
  showNoticeBox(spec) {
5566
5641
  const sanitized = {
5567
5642
  ...spec,