@toddzheng024/dscode-bundle 0.7.1 → 0.7.3

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 (41) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/cordis.patch.yml +4 -0
  3. package/package.json +4 -2
  4. package/plugins/auto-review/index.mjs +21 -3
  5. package/plugins/auto-review/policy.mjs +25 -0
  6. package/plugins/code-review/git.mjs +24 -2
  7. package/plugins/code-review/index.mjs +13 -6
  8. package/plugins/credentials/index.mjs +7 -5
  9. package/plugins/exec/cli.mjs +63 -0
  10. package/plugins/exec/index.mjs +1 -1
  11. package/plugins/i18n/messages.mjs +6 -6
  12. package/plugins/memory/index.mjs +17 -12
  13. package/plugins/providers/catalog.mjs +134 -0
  14. package/plugins/session-cards/index.mjs +11 -8
  15. package/plugins/session-cards/manager.mjs +1 -1
  16. package/plugins/session-metrics/attribution.mjs +19 -0
  17. package/plugins/session-metrics/balance.mjs +71 -0
  18. package/plugins/session-metrics/index.mjs +39 -9
  19. package/plugins/session-metrics/pricing.mjs +39 -5
  20. package/plugins/session-metrics/view.mjs +36 -15
  21. package/plugins/tui-tools/doctor.mjs +9 -6
  22. package/plugins/tui-tools/index.mjs +2 -2
  23. package/plugins/ultra/policy.mjs +16 -0
  24. package/vendor/persistent/index.js +19 -2
  25. package/vendor/pi-ai/LICENSE +21 -0
  26. package/vendor/pi-ai/index.js +2702 -0
  27. package/vendor/pi-ai/types/adapter.d.ts +105 -0
  28. package/vendor/pi-ai/types/auth.d.ts +60 -0
  29. package/vendor/pi-ai/types/catalog.d.ts +355 -0
  30. package/vendor/pi-ai/types/config.d.ts +208 -0
  31. package/vendor/pi-ai/types/context.d.ts +42 -0
  32. package/vendor/pi-ai/types/discovery.d.ts +43 -0
  33. package/vendor/pi-ai/types/index.d.ts +69 -0
  34. package/vendor/pi-ai/types/login.d.ts +21 -0
  35. package/vendor/pi-ai/types/provider.d.ts +59 -0
  36. package/vendor/pi-ai/types/replay.d.ts +63 -0
  37. package/vendor/pi-ai/types/stream.d.ts +43 -0
  38. package/vendor/tui/dscode-providers/catalog.mjs +134 -0
  39. package/vendor/tui/index.mjs +310 -295
  40. package/vendor/tui/.dscode-viewport-probe-78546.mjs +0 -39239
  41. package/vendor/tui/dscode-email.mjs +0 -69
@@ -1,7 +1,7 @@
1
1
  // dscode-language-v2
2
2
  // dscode-language-v1
3
3
  const DSCODE_LANGUAGES = [{"code":"en","name":"English"},{"code":"zh-CN","name":"简体中文"},{"code":"zh-TW","name":"繁體中文"},{"code":"ja","name":"日本語"},{"code":"ko","name":"한국어"},{"code":"es","name":"Español"}];
4
- const DSCODE_MESSAGES = {"en":{"activity.replying":"Replying","activity.thinking":"Thinking","activity.running":"Running","activity.turn":"this turn","activity.interrupt":"Esc to interrupt","agents.running":"running","agents.idle":"idle","agents.done":"done","agents.total":"total","welcome.model":"model","welcome.effort":"effort","welcome.project":"project","verbose.on":"verbose on: thinking and tool calls are shown in the chat","verbose.off":"verbose off","mouse.on":"mouse on: the wheel scrolls the chat · hold Shift while dragging to select text (Option in iTerm2, Fn in Terminal)","mouse.off":"mouse off: select and copy freely · PageUp/PageDown scroll the chat · /mouse turns wheel scrolling on","language.current":"Language: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"language → {name}","language.title":"/language — interface language","language.currentMark":"current","language.unknown":"Unknown language \"{value}\". Choose en, zh-CN, zh-TW, ja, ko or es.","language.saveFailed":"language save failed: {error}","doctor.logs.new":"Only warnings/errors since this TUI version started are recorded.","footer.current":"current","footer.average":"average","footer.context":"context","footer.cache":"cache","composer.placeholder":"type a message","doctor.nearTimeout":"Assessment: {count} Bash calls ended at about 300 seconds, which matches the tool timeout; traces alone cannot prove the root cause. Next, check those calls' shell completion markers and terminal errors.","doctor.logs.none":"No log file yet; earlier console logs were not persisted and cannot be recovered.","doctor.evidence":"Diagnostic evidence: {traces} recent sessions, {logs} warnings/errors.","doctor.noFindings":"No clear timeouts, unfinished tool calls or error events in recent traces.","doctor.noRoute":"No model route is available, so the model analysis could not run.","doctor.scope":"Evidence scope: {traces} recent sessions, {logs} warnings/errors; conversation text, tool arguments and tool output were not read.","doctor.failed":"Model analysis did not finish: {error}."},"zh-CN":{"activity.replying":"正在回复","activity.thinking":"正在思考","activity.running":"正在执行","activity.turn":"本轮","activity.interrupt":"Esc 中断","agents.running":"运行中","agents.idle":"空闲","agents.done":"已完成","agents.total":"总计","welcome.model":"模型","welcome.effort":"推理","welcome.project":"项目","verbose.on":"详细模式已开启:对话中显示思考与工具调用","verbose.off":"详细模式已关闭","mouse.on":"鼠标捕获已开启:滚轮滚动对话 · 拖选文本时按住 Shift(iTerm2 按住 Option、Terminal 按住 Fn)","mouse.off":"鼠标捕获已关闭:可自由选择复制 · PageUp/PageDown 滚动对话 · /mouse 重新开启","language.current":"语言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"语言 → {name}","language.title":"/language — 界面语言","language.currentMark":"当前","language.unknown":"未知语言 \"{value}\"。可选 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"语言设置保存失败:{error}","doctor.logs.new":"仅记录新版 TUI 启动后的 warning/error。","footer.current":"当前","footer.average":"平均","footer.context":"上下文","footer.cache":"缓存","composer.placeholder":"输入消息","doctor.nearTimeout":"判断:{count} 次 Bash 调用在约 300 秒结束,符合工具超时特征;trace 不能单独证明触发超时的根因。下一步检查这些调用的 shell 完成标记和终端错误。","doctor.logs.none":"日志文件尚未建立;旧版控制台日志未持久化,无法回溯。","doctor.evidence":"诊断证据:{traces} 个近期会话、{logs} 条 warning/error。","doctor.noFindings":"近期 trace 中未发现明确的超时、未完成工具调用或错误事件。","doctor.noRoute":"没有可用的模型路由,无法运行模型分析。","doctor.scope":"证据范围:{traces} 个近期会话、{logs} 条 warning/error;未读取对话正文、工具参数或工具输出。","doctor.failed":"模型分析未完成:{error}。"},"zh-TW":{"activity.replying":"正在回覆","activity.thinking":"正在思考","activity.running":"正在執行","activity.turn":"本輪","activity.interrupt":"Esc 中斷","agents.running":"執行中","agents.idle":"閒置","agents.done":"已完成","agents.total":"總計","welcome.model":"模型","welcome.effort":"推理","welcome.project":"專案","verbose.on":"詳細模式已開啟:對話中顯示思考與工具呼叫","verbose.off":"詳細模式已關閉","mouse.on":"滑鼠擷取已開啟:滾輪捲動對話 · 拖選文字時按住 Shift(iTerm2 按住 Option、Terminal 按住 Fn)","mouse.off":"滑鼠擷取已關閉:可自由選取複製 · PageUp/PageDown 捲動對話 · /mouse 重新開啟","language.current":"語言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"語言 → {name}","language.title":"/language — 介面語言","language.currentMark":"目前","language.unknown":"未知語言 \"{value}\"。可選 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"語言設定儲存失敗:{error}","doctor.logs.new":"僅記錄新版 TUI 啟動後的 warning/error。","footer.current":"目前","footer.average":"平均","footer.context":"上下文","footer.cache":"快取","composer.placeholder":"輸入訊息","doctor.nearTimeout":"判斷:{count} 次 Bash 呼叫在約 300 秒結束,符合工具逾時特徵;trace 無法單獨證明觸發逾時的根因。下一步檢查這些呼叫的 shell 完成標記和終端錯誤。","doctor.logs.none":"日誌檔尚未建立;舊版主控台日誌未持久化,無法回溯。","doctor.evidence":"診斷證據:{traces} 個近期工作階段、{logs} 筆 warning/error。","doctor.noFindings":"近期 trace 中未發現明確的逾時、未完成工具呼叫或錯誤事件。","doctor.noRoute":"沒有可用的模型路由,無法執行模型分析。","doctor.scope":"證據範圍:{traces} 個近期工作階段、{logs} 筆 warning/error;未讀取對話內容、工具參數或工具輸出。","doctor.failed":"模型分析未完成:{error}。"},"ja":{"activity.replying":"応答中","activity.thinking":"思考中","activity.running":"実行中","activity.turn":"今回のターン","activity.interrupt":"Esc で中断","agents.running":"実行中","agents.idle":"待機中","agents.done":"完了","agents.total":"合計","welcome.model":"モデル","welcome.effort":"推論","welcome.project":"プロジェクト","verbose.on":"詳細モード オン:思考とツール呼び出しをチャットに表示します","verbose.off":"詳細モード オフ","mouse.on":"マウス キャプチャ オン:ホイールでチャットをスクロール · Shift(iTerm2 は Option、Terminal は Fn)を押しながらドラッグでテキスト選択","mouse.off":"マウス キャプチャ オフ:自由に選択・コピーできます · PageUp/PageDown でスクロール · /mouse で再びオン","language.current":"言語:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"言語 → {name}","language.title":"/language — 表示言語","language.currentMark":"現在","language.unknown":"不明な言語 \"{value}\"。en、zh-CN、zh-TW、ja、ko、es から選んでください。","language.saveFailed":"言語設定の保存に失敗しました:{error}","doctor.logs.new":"この TUI バージョンの起動以降の warning/error のみ記録されています。","footer.current":"現在","footer.average":"平均","footer.context":"コンテキスト","footer.cache":"キャッシュ","composer.placeholder":"メッセージを入力","doctor.nearTimeout":"判断:Bash 呼び出し {count} 件が約 300 秒で終了しており、ツールのタイムアウトの特徴に一致します。トレースだけでは根本原因を証明できません。次はこれらの呼び出しのシェル完了マーカーと端末エラーを確認してください。","doctor.logs.none":"ログファイルはまだありません。以前のコンソールログは保存されておらず、遡れません。","doctor.evidence":"診断の根拠:直近のセッション {traces} 件、warning/error {logs} 件。","doctor.noFindings":"直近のトレースに明確なタイムアウト、未完了ツール、停滞は見つかりませんでした。","doctor.noRoute":"利用できるモデルルートがないため、モデル分析を実行できません。","doctor.scope":"根拠の範囲:直近のセッション {traces} 件、warning/error {logs} 件。会話本文、ツール引数、ツール出力は読んでいません。","doctor.failed":"モデル分析が完了しませんでした:{error}。"},"ko":{"activity.replying":"응답 중","activity.thinking":"생각 중","activity.running":"실행 중","activity.turn":"이번 턴","activity.interrupt":"Esc 중단","agents.running":"실행 중","agents.idle":"대기","agents.done":"완료","agents.total":"전체","welcome.model":"모델","welcome.effort":"추론","welcome.project":"프로젝트","verbose.on":"상세 모드 켜짐: 생각과 도구 호출을 채팅에 표시합니다","verbose.off":"상세 모드 꺼짐","mouse.on":"마우스 캡처 켜짐: 휠로 채팅 스크롤 · Shift(iTerm2는 Option, Terminal은 Fn)를 누른 채 드래그하여 텍스트 선택","mouse.off":"마우스 캡처 꺼짐: 자유롭게 선택·복사 · PageUp/PageDown으로 채팅 스크롤 · /mouse로 다시 켜기","language.current":"언어: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"언어 → {name}","language.title":"/language — 인터페이스 언어","language.currentMark":"현재","language.unknown":"알 수 없는 언어 \"{value}\". en, zh-CN, zh-TW, ja, ko, es 중에서 선택하세요.","language.saveFailed":"언어 설정 저장 실패: {error}","doctor.logs.new":"이 TUI 버전 시작 이후의 warning/error만 기록됩니다.","footer.current":"현재","footer.average":"평균","footer.context":"컨텍스트","footer.cache":"캐시","composer.placeholder":"메시지를 입력하세요","doctor.nearTimeout":"판단: Bash 호출 {count}건이 약 300초에 종료되어 도구 시간 초과 특징과 일치합니다. 트레이스만으로는 근본 원인을 증명할 수 없습니다. 다음으로 해당 호출의 셸 완료 표시와 터미널 오류를 확인하세요.","doctor.logs.none":"아직 로그 파일이 없습니다. 이전 콘솔 로그는 저장되지 않아 복구할 수 없습니다.","doctor.evidence":"진단 근거: 최근 세션 {traces}개, warning/error {logs}건.","doctor.noFindings":"최근 트레이스에서 명확한 시간 초과, 미완료 도구, 정지는 발견되지 않았습니다.","doctor.noRoute":"사용 가능한 모델 경로가 없어 모델 분석을 실행할 수 없습니다.","doctor.scope":"근거 범위: 최근 세션 {traces}개, warning/error {logs}건. 대화 본문, 도구 인수, 도구 출력은 읽지 않았습니다.","doctor.failed":"모델 분석이 완료되지 않았습니다: {error}."},"es":{"activity.replying":"Respondiendo","activity.thinking":"Pensando","activity.running":"Ejecutando","activity.turn":"este turno","activity.interrupt":"Esc para interrumpir","agents.running":"en ejecución","agents.idle":"inactivo","agents.done":"terminado","agents.total":"en total","welcome.model":"modelo","welcome.effort":"esfuerzo","welcome.project":"proyecto","verbose.on":"modo detallado activado: el razonamiento y las llamadas a herramientas se muestran en el chat","verbose.off":"modo detallado desactivado","mouse.on":"ratón activado: la rueda desplaza el chat · mantén Shift al arrastrar para seleccionar texto (Option en iTerm2, Fn en Terminal)","mouse.off":"ratón desactivado: selecciona y copia libremente · PageUp/PageDown desplazan el chat · /mouse vuelve a activar la captura","language.current":"Idioma: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"idioma → {name}","language.title":"/language — idioma de la interfaz","language.currentMark":"actual","language.unknown":"Idioma desconocido \"{value}\". Elige en, zh-CN, zh-TW, ja, ko o es.","language.saveFailed":"no se pudo guardar el idioma: {error}","doctor.logs.new":"Solo se registran los warnings/errores desde que arrancó esta versión del TUI.","footer.current":"actual","footer.average":"promedio","footer.context":"contexto","footer.cache":"caché","composer.placeholder":"escribe un mensaje","doctor.nearTimeout":"Valoración: {count} llamadas a Bash terminaron a unos 300 segundos, lo que coincide con el tiempo de espera de la herramienta; las trazas por sí solas no prueban la causa raíz. A continuación, revisa los marcadores de finalización del shell y los errores de terminal de esas llamadas.","doctor.logs.none":"Aún no hay archivo de registro; los registros de consola anteriores no se conservaron y no se pueden recuperar.","doctor.evidence":"Evidencia del diagnóstico: {traces} sesiones recientes, {logs} warnings/errores.","doctor.noFindings":"No hay tiempos de espera, herramientas sin terminar ni bloqueos claros en las trazas recientes.","doctor.noRoute":"No hay una ruta de modelo disponible, así que el análisis con modelo no se pudo ejecutar.","doctor.scope":"Alcance de la evidencia: {traces} sesiones recientes, {logs} warnings/errores; no se leyó el texto de la conversación, los argumentos ni la salida de las herramientas.","doctor.failed":"El análisis con modelo no terminó: {error}."}};
4
+ const DSCODE_MESSAGES = {"en":{"activity.replying":"Replying","activity.thinking":"Thinking","activity.running":"Running","activity.turn":"this turn","activity.interrupt":"Esc to interrupt","agents.running":"running","agents.idle":"idle","agents.done":"done","agents.total":"total","welcome.model":"model","welcome.effort":"effort","welcome.project":"project","verbose.on":"verbose on: thinking and tool calls are shown in the chat","verbose.off":"verbose off","mouse.on":"mouse on: the wheel scrolls the chat · hold Shift while dragging to select text (Option in iTerm2, Fn in Terminal)","mouse.off":"mouse off: select and copy freely · PageUp/PageDown scroll the chat · /mouse turns wheel scrolling on","language.current":"Language: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"language → {name}","language.title":"/language — interface language","language.currentMark":"current","language.unknown":"Unknown language \"{value}\". Choose en, zh-CN, zh-TW, ja, ko or es.","language.saveFailed":"language save failed: {error}","doctor.logs.new":"Only warnings/errors since this TUI version started are recorded.","footer.current":"current","footer.average":"average","footer.context":"context","footer.cache":"cache hit","composer.placeholder":"type a message","doctor.nearTimeout":"Assessment: {count} Bash calls ended at about 300 seconds, which matches the tool timeout; traces alone cannot prove the root cause. Next, check those calls' shell completion markers and terminal errors.","doctor.logs.none":"No log file yet; earlier console logs were not persisted and cannot be recovered.","doctor.evidence":"Diagnostic evidence: {traces} recent sessions, {logs} warnings/errors.","doctor.noFindings":"No clear timeouts, unfinished tool calls or error events in recent traces.","doctor.noRoute":"No model route is available, so the model analysis could not run.","doctor.scope":"Evidence scope: {traces} recent sessions, {logs} warnings/errors; conversation text, tool arguments and tool output were not read.","doctor.failed":"Model analysis did not finish: {error}."},"zh-CN":{"activity.replying":"正在回复","activity.thinking":"正在思考","activity.running":"正在执行","activity.turn":"本轮","activity.interrupt":"Esc 中断","agents.running":"运行中","agents.idle":"空闲","agents.done":"已完成","agents.total":"总计","welcome.model":"模型","welcome.effort":"推理","welcome.project":"项目","verbose.on":"详细模式已开启:对话中显示思考与工具调用","verbose.off":"详细模式已关闭","mouse.on":"鼠标捕获已开启:滚轮滚动对话 · 拖选文本时按住 Shift(iTerm2 按住 Option、Terminal 按住 Fn)","mouse.off":"鼠标捕获已关闭:可自由选择复制 · PageUp/PageDown 滚动对话 · /mouse 重新开启","language.current":"语言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"语言 → {name}","language.title":"/language — 界面语言","language.currentMark":"当前","language.unknown":"未知语言 \"{value}\"。可选 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"语言设置保存失败:{error}","doctor.logs.new":"仅记录新版 TUI 启动后的 warning/error。","footer.current":"当前","footer.average":"平均","footer.context":"上下文","footer.cache":"缓存命中","composer.placeholder":"输入消息","doctor.nearTimeout":"判断:{count} 次 Bash 调用在约 300 秒结束,符合工具超时特征;trace 不能单独证明触发超时的根因。下一步检查这些调用的 shell 完成标记和终端错误。","doctor.logs.none":"日志文件尚未建立;旧版控制台日志未持久化,无法回溯。","doctor.evidence":"诊断证据:{traces} 个近期会话、{logs} 条 warning/error。","doctor.noFindings":"近期 trace 中未发现明确的超时、未完成工具调用或错误事件。","doctor.noRoute":"没有可用的模型路由,无法运行模型分析。","doctor.scope":"证据范围:{traces} 个近期会话、{logs} 条 warning/error;未读取对话正文、工具参数或工具输出。","doctor.failed":"模型分析未完成:{error}。"},"zh-TW":{"activity.replying":"正在回覆","activity.thinking":"正在思考","activity.running":"正在執行","activity.turn":"本輪","activity.interrupt":"Esc 中斷","agents.running":"執行中","agents.idle":"閒置","agents.done":"已完成","agents.total":"總計","welcome.model":"模型","welcome.effort":"推理","welcome.project":"專案","verbose.on":"詳細模式已開啟:對話中顯示思考與工具呼叫","verbose.off":"詳細模式已關閉","mouse.on":"滑鼠擷取已開啟:滾輪捲動對話 · 拖選文字時按住 Shift(iTerm2 按住 Option、Terminal 按住 Fn)","mouse.off":"滑鼠擷取已關閉:可自由選取複製 · PageUp/PageDown 捲動對話 · /mouse 重新開啟","language.current":"語言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"語言 → {name}","language.title":"/language — 介面語言","language.currentMark":"目前","language.unknown":"未知語言 \"{value}\"。可選 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"語言設定儲存失敗:{error}","doctor.logs.new":"僅記錄新版 TUI 啟動後的 warning/error。","footer.current":"目前","footer.average":"平均","footer.context":"上下文","footer.cache":"快取命中","composer.placeholder":"輸入訊息","doctor.nearTimeout":"判斷:{count} 次 Bash 呼叫在約 300 秒結束,符合工具逾時特徵;trace 無法單獨證明觸發逾時的根因。下一步檢查這些呼叫的 shell 完成標記和終端錯誤。","doctor.logs.none":"日誌檔尚未建立;舊版主控台日誌未持久化,無法回溯。","doctor.evidence":"診斷證據:{traces} 個近期工作階段、{logs} 筆 warning/error。","doctor.noFindings":"近期 trace 中未發現明確的逾時、未完成工具呼叫或錯誤事件。","doctor.noRoute":"沒有可用的模型路由,無法執行模型分析。","doctor.scope":"證據範圍:{traces} 個近期工作階段、{logs} 筆 warning/error;未讀取對話內容、工具參數或工具輸出。","doctor.failed":"模型分析未完成:{error}。"},"ja":{"activity.replying":"応答中","activity.thinking":"思考中","activity.running":"実行中","activity.turn":"今回のターン","activity.interrupt":"Esc で中断","agents.running":"実行中","agents.idle":"待機中","agents.done":"完了","agents.total":"合計","welcome.model":"モデル","welcome.effort":"推論","welcome.project":"プロジェクト","verbose.on":"詳細モード オン:思考とツール呼び出しをチャットに表示します","verbose.off":"詳細モード オフ","mouse.on":"マウス キャプチャ オン:ホイールでチャットをスクロール · Shift(iTerm2 は Option、Terminal は Fn)を押しながらドラッグでテキスト選択","mouse.off":"マウス キャプチャ オフ:自由に選択・コピーできます · PageUp/PageDown でスクロール · /mouse で再びオン","language.current":"言語:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"言語 → {name}","language.title":"/language — 表示言語","language.currentMark":"現在","language.unknown":"不明な言語 \"{value}\"。en、zh-CN、zh-TW、ja、ko、es から選んでください。","language.saveFailed":"言語設定の保存に失敗しました:{error}","doctor.logs.new":"この TUI バージョンの起動以降の warning/error のみ記録されています。","footer.current":"現在","footer.average":"平均","footer.context":"コンテキスト","footer.cache":"キャッシュヒット","composer.placeholder":"メッセージを入力","doctor.nearTimeout":"判断:Bash 呼び出し {count} 件が約 300 秒で終了しており、ツールのタイムアウトの特徴に一致します。トレースだけでは根本原因を証明できません。次はこれらの呼び出しのシェル完了マーカーと端末エラーを確認してください。","doctor.logs.none":"ログファイルはまだありません。以前のコンソールログは保存されておらず、遡れません。","doctor.evidence":"診断の根拠:直近のセッション {traces} 件、warning/error {logs} 件。","doctor.noFindings":"直近のトレースに明確なタイムアウト、未完了ツール、停滞は見つかりませんでした。","doctor.noRoute":"利用できるモデルルートがないため、モデル分析を実行できません。","doctor.scope":"根拠の範囲:直近のセッション {traces} 件、warning/error {logs} 件。会話本文、ツール引数、ツール出力は読んでいません。","doctor.failed":"モデル分析が完了しませんでした:{error}。"},"ko":{"activity.replying":"응답 중","activity.thinking":"생각 중","activity.running":"실행 중","activity.turn":"이번 턴","activity.interrupt":"Esc 중단","agents.running":"실행 중","agents.idle":"대기","agents.done":"완료","agents.total":"전체","welcome.model":"모델","welcome.effort":"추론","welcome.project":"프로젝트","verbose.on":"상세 모드 켜짐: 생각과 도구 호출을 채팅에 표시합니다","verbose.off":"상세 모드 꺼짐","mouse.on":"마우스 캡처 켜짐: 휠로 채팅 스크롤 · Shift(iTerm2는 Option, Terminal은 Fn)를 누른 채 드래그하여 텍스트 선택","mouse.off":"마우스 캡처 꺼짐: 자유롭게 선택·복사 · PageUp/PageDown으로 채팅 스크롤 · /mouse로 다시 켜기","language.current":"언어: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"언어 → {name}","language.title":"/language — 인터페이스 언어","language.currentMark":"현재","language.unknown":"알 수 없는 언어 \"{value}\". en, zh-CN, zh-TW, ja, ko, es 중에서 선택하세요.","language.saveFailed":"언어 설정 저장 실패: {error}","doctor.logs.new":"이 TUI 버전 시작 이후의 warning/error만 기록됩니다.","footer.current":"현재","footer.average":"평균","footer.context":"컨텍스트","footer.cache":"캐시 적중","composer.placeholder":"메시지를 입력하세요","doctor.nearTimeout":"판단: Bash 호출 {count}건이 약 300초에 종료되어 도구 시간 초과 특징과 일치합니다. 트레이스만으로는 근본 원인을 증명할 수 없습니다. 다음으로 해당 호출의 셸 완료 표시와 터미널 오류를 확인하세요.","doctor.logs.none":"아직 로그 파일이 없습니다. 이전 콘솔 로그는 저장되지 않아 복구할 수 없습니다.","doctor.evidence":"진단 근거: 최근 세션 {traces}개, warning/error {logs}건.","doctor.noFindings":"최근 트레이스에서 명확한 시간 초과, 미완료 도구, 정지는 발견되지 않았습니다.","doctor.noRoute":"사용 가능한 모델 경로가 없어 모델 분석을 실행할 수 없습니다.","doctor.scope":"근거 범위: 최근 세션 {traces}개, warning/error {logs}건. 대화 본문, 도구 인수, 도구 출력은 읽지 않았습니다.","doctor.failed":"모델 분석이 완료되지 않았습니다: {error}."},"es":{"activity.replying":"Respondiendo","activity.thinking":"Pensando","activity.running":"Ejecutando","activity.turn":"este turno","activity.interrupt":"Esc para interrumpir","agents.running":"en ejecución","agents.idle":"inactivo","agents.done":"terminado","agents.total":"en total","welcome.model":"modelo","welcome.effort":"esfuerzo","welcome.project":"proyecto","verbose.on":"modo detallado activado: el razonamiento y las llamadas a herramientas se muestran en el chat","verbose.off":"modo detallado desactivado","mouse.on":"ratón activado: la rueda desplaza el chat · mantén Shift al arrastrar para seleccionar texto (Option en iTerm2, Fn en Terminal)","mouse.off":"ratón desactivado: selecciona y copia libremente · PageUp/PageDown desplazan el chat · /mouse vuelve a activar la captura","language.current":"Idioma: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"idioma → {name}","language.title":"/language — idioma de la interfaz","language.currentMark":"actual","language.unknown":"Idioma desconocido \"{value}\". Elige en, zh-CN, zh-TW, ja, ko o es.","language.saveFailed":"no se pudo guardar el idioma: {error}","doctor.logs.new":"Solo se registran los warnings/errores desde que arrancó esta versión del TUI.","footer.current":"actual","footer.average":"promedio","footer.context":"contexto","footer.cache":"éxitos de caché","composer.placeholder":"escribe un mensaje","doctor.nearTimeout":"Valoración: {count} llamadas a Bash terminaron a unos 300 segundos, lo que coincide con el tiempo de espera de la herramienta; las trazas por sí solas no prueban la causa raíz. A continuación, revisa los marcadores de finalización del shell y los errores de terminal de esas llamadas.","doctor.logs.none":"Aún no hay archivo de registro; los registros de consola anteriores no se conservaron y no se pueden recuperar.","doctor.evidence":"Evidencia del diagnóstico: {traces} sesiones recientes, {logs} warnings/errores.","doctor.noFindings":"No hay tiempos de espera, herramientas sin terminar ni bloqueos claros en las trazas recientes.","doctor.noRoute":"No hay una ruta de modelo disponible, así que el análisis con modelo no se pudo ejecutar.","doctor.scope":"Alcance de la evidencia: {traces} sesiones recientes, {logs} warnings/errores; no se leyó el texto de la conversación, los argumentos ni la salida de las herramientas.","doctor.failed":"El análisis con modelo no terminó: {error}."}};
5
5
  const DSCODE_LANGUAGE_ALIASES = {"en":["en","english","eng","英文","英语","英語"],"zh-CN":["zh-cn","zh","zh-hans","zhhans","cn","chinese","simplified","中文","简体","简体中文","简中","中文简体"],"zh-TW":["zh-tw","zh-hant","zhhant","tw","hk","zh-hk","traditional","繁体","繁體","繁體中文","繁体中文","繁中"],"ja":["ja","jp","japanese","日本語","日语","日語"],"ko":["ko","kr","korean","한국어","韩语","韓語","韓文"],"es":["es","spanish","español","espanol","西班牙语","西班牙語"]};
6
6
  function dscodeNormalizeLanguage(value) {
7
7
  const aliasesByCode = DSCODE_LANGUAGE_ALIASES;
@@ -39,32 +39,8 @@ function dscodeLoadFlag(name, fallback = false) {
39
39
  function dscodeSaveFlag(name, value) {
40
40
  try { fs.mkdirSync(join(homedir(), ".dsh", "dsh-code"), { recursive: true }); fs.writeFileSync(dscodeFlagFile(name), JSON.stringify({ [name]: value }, null, 2) + "\n"); } catch {}
41
41
  }
42
- // dscode-scroll-v3
43
- // dscode-interaction-v2
44
- // dscode-welcome-scroll-v2
45
- // dscode-welcome-v2
46
- const WELCOME_ART = ["...........1.1..........","............1...........","............1...........",".........1..1..1........",".......1..22222..1......","...1...1....2....1...1..","..11...2....2....2...11.","....11.2...222...2.11...","......22.22.3.22.22.....",".....22.22.333.22.22....","....1...2.33332.2...1...","........2.33322.2.......","....1...2.33222.2...1...",".....22.22.222.22.22....","......22.22.3.22.22.....","....11.2...222...2.11...","..11...2....2....2...11.","...1...1....2....1...1..",".......1..22222..1......",".........1..1..1........","............1...........","............1...........","...........1.1..........","........................"];
47
- const WELCOME_ART_SMALL = ["..........1.1.........","...........1..........","........1..1..1.......",".........1.2.1........","...1..1...222...1..1..","...1..1....2....1..1..","..1.1.2...222...2.1.1.",".....22.22.3.22.22....","....12.22.333.22.21...","...1...2.33332.2...1..",".......2.33322.2......","...1...2.33222.2...1..","....12.22.222.22.21...",".....22.22.3.22.22....","..1.1.2...222...2.1.1.","...1..1....2....1..1..","...1..1...222...1..1..",".........1.2.1........","........1..1..1.......","...........1..........","..........1.1.........","......................"];
48
- function welcomeArtRows(grid, tones) {
49
- const rows = [];
50
- for (let y = 0; y < grid.length; y += 2) {
51
- const top = grid[y] || '';
52
- const bottom = grid[y + 1] || '';
53
- const segments = [];
54
- for (let x = 0; x < Math.max(top.length, bottom.length); x++) {
55
- const upper = tones[top[x]] || '';
56
- const lower = tones[bottom[x]] || '';
57
- const glyph = upper && lower ? upper === lower ? '\u2588' : '\u2580' : upper ? '\u2580' : lower ? '\u2584' : ' ';
58
- const color = upper || lower;
59
- const background = upper && lower && upper !== lower ? lower : '';
60
- const last = segments[segments.length - 1];
61
- if (last && last.glyph === glyph && last.color === color && last.background === background) last.text += glyph;
62
- else segments.push({ glyph, text: glyph, color, background });
63
- }
64
- rows.push(segments);
65
- }
66
- return rows;
67
- }
42
+ // dscode-provider-v1
43
+ import { PROVIDERS as DSCODE_PROVIDERS, providerSpec as dscodeProviderSpec, providerArgument as dscodeProviderArgument, providerOfLabel as dscodeProviderOfLabel, splitModelLabel as dscodeSplitModelLabel, pickModel as dscodePickModel, credentialState as dscodeCredentialState, ensureProviderRoute as dscodeEnsureProviderRoute, waitForModels as dscodeWaitForModels } from "./dscode-providers/catalog.mjs";
68
44
  import { readClipboardImage as dscodeReadClipboardImage } from "./dscode-clipboard-image/index.mjs";
69
45
  // dscode-clipboard-image-v1
70
46
  // dscode-large-paste-v1
@@ -147,52 +123,11 @@ function userBackgroundRows(rows, columns, measure) {
147
123
  };
148
124
  });
149
125
  }
150
- // dscode-turn-divider-v1
151
- function turnDividedLines(entry, columns, rendered, remaining) {
152
- if (entry?.turnEnded !== true || (rendered.length > 0 && rendered.length >= remaining)) return rendered;
126
+ // dscode-turn-divider-v2
127
+ function turnDividedLines(entry, columns, rendered) {
128
+ if (entry?.turnEnded !== true) return rendered;
153
129
  // Full terminal width: the row renders with truncate-end, so it can never wrap.
154
- return [...rendered, { segments: [{ text: '─'.repeat(Math.max(1, columns)), style: 'dim' }] }];
155
- }
156
- // dscode-scroll-v1
157
- function transcriptWindow(lines, rows, offset) {
158
- if (rows <= 0) return [];
159
- const end = Math.max(0, lines.length - Math.max(0, offset));
160
- return lines.slice(Math.max(0, end - rows), end);
161
- }
162
- function mouseWheelDirection(unit) {
163
- const match = /^\x1b\[<(\d+);\d+;\d+([Mm])$/.exec(unit);
164
- if (!match) return null;
165
- if (match[2] !== 'M') return 0;
166
- const button = Number(match[1]) & ~(4 | 8 | 16);
167
- return button === 64 ? 1 : button === 65 ? -1 : 0;
168
- }
169
- const DSCODE_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
170
- const DSCODE_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1000l";
171
- const DSCODE_WHEEL_FRAME_MS = 16;
172
- const dscodeWheelListeners = new Set();
173
- let dscodeWheelPending = 0;
174
- let dscodeWheelTimer = void 0;
175
- function dscodeQueueWheel(direction) {
176
- dscodeWheelPending += direction;
177
- if (dscodeWheelTimer !== void 0) return;
178
- dscodeWheelTimer = setTimeout(() => {
179
- dscodeWheelTimer = void 0;
180
- const delta = dscodeWheelPending;
181
- dscodeWheelPending = 0;
182
- if (delta !== 0) for (const listener of dscodeWheelListeners) listener(delta);
183
- }, DSCODE_WHEEL_FRAME_MS);
184
- }
185
- let dscodeMouseEnabled = dscodeLoadFlag("mouse", true);
186
- function dscodeSetMouse(enabled) {
187
- dscodeMouseEnabled = enabled;
188
- if (process.stdout.isTTY === true) process.stdout.write(enabled ? DSCODE_MOUSE_ENABLE : DSCODE_MOUSE_DISABLE);
189
- return enabled;
190
- }
191
- function dscodeHandleMouseUnit(unit) {
192
- const direction = mouseWheelDirection(unit);
193
- if (direction === null) return false;
194
- if (direction !== 0) dscodeQueueWheel(direction);
195
- return true;
130
+ return [...rendered, { segments: [] }, { segments: [{ text: '─'.repeat(Math.max(1, columns)), style: 'dim' }] }, { segments: [] }];
196
131
  }
197
132
  // dscode-interrupt-v1
198
133
  function ctrlCAction(armed, { interrupt, busy, preparingImages, active, hasDraft }) {
@@ -202,11 +137,6 @@ function ctrlCAction(armed, { interrupt, busy, preparingImages, active, hasDraft
202
137
  if (!active) return 'wait';
203
138
  return hasDraft ? 'clear-draft' : 'quit';
204
139
  }
205
- // dscode-welcome-scroll-v1
206
- function welcomeVisibleRows(capacity, maximum, demand, canScroll = true) {
207
- if (!canScroll || capacity < maximum) return maximum;
208
- return Math.max(0, Math.min(maximum, capacity - demand));
209
- }
210
140
  // dscode-email-v4
211
141
  import { createImapConnector as dscodeCreateImapConnector } from "./dscode-email/imap.mjs";
212
142
  import { createGmailConnector as dscodeCreateGmailConnector } from "./dscode-email/gmail.mjs";
@@ -339,6 +269,7 @@ function DscodeEmailPanel({ columns, rows, pick, close, gmail, imap }) {
339
269
  }
340
270
  // dscode-effort-bar-v6
341
271
  // dscode-welcome-v1
272
+ // dscode-welcome-v2
342
273
  function welcomePath(path, width) {
343
274
  const full = singleLineText(path || '');
344
275
  if (visibleColumns(full) <= width) return full;
@@ -351,23 +282,46 @@ function welcomePath(path, width) {
351
282
  }
352
283
  return suffix ? '…' + suffix : '…' + truncateColumns(segments.at(-1) || full, Math.max(1, width - 1));
353
284
  }
354
- // dscode-viewport-v1
355
- function visibleSettledLines(entries, settled, budget, columns, showReasoning, renderEntry) {
356
- if (budget <= 0) return [];
357
- const chunks = [];
358
- let remaining = budget;
359
- for (let index = settled - 1; index >= 0 && remaining > 0; index--) {
360
- const lines = turnDividedLines(entries[index], columns, renderEntry(entries[index], Math.max(10, columns - 2), showReasoning), remaining);
361
- const kept = lines.slice(-remaining);
362
- if (kept.length) {
363
- chunks.unshift(kept);
364
- remaining -= kept.length;
285
+ const WELCOME_ART = ["...........1.1..........","............1...........","............1...........",".........1..1..1........",".......1..22222..1......","...1...1....2....1...1..","..11...2....2....2...11.","....11.2...222...2.11...","......22.22.3.22.22.....",".....22.22.333.22.22....","....1...2.33332.2...1...","........2.33322.2.......","....1...2.33222.2...1...",".....22.22.222.22.22....","......22.22.3.22.22.....","....11.2...222...2.11...","..11...2....2....2...11.","...1...1....2....1...1..",".......1..22222..1......",".........1..1..1........","............1...........","............1...........","...........1.1..........","........................"];
286
+ const WELCOME_ART_SMALL = ["..........1.1.........","...........1..........","........1..1..1.......",".........1.2.1........","...1..1...222...1..1..","...1..1....2....1..1..","..1.1.2...222...2.1.1.",".....22.22.3.22.22....","....12.22.333.22.21...","...1...2.33332.2...1..",".......2.33322.2......","...1...2.33222.2...1..","....12.22.222.22.21...",".....22.22.3.22.22....","..1.1.2...222...2.1.1.","...1..1....2....1..1..","...1..1...222...1..1..",".........1.2.1........","........1..1..1.......","...........1..........","..........1.1.........","......................"];
287
+ function welcomeArtRows(grid, tones) {
288
+ const rows = [];
289
+ for (let y = 0; y < grid.length; y += 2) {
290
+ const top = grid[y] || '';
291
+ const bottom = grid[y + 1] || '';
292
+ const segments = [];
293
+ for (let x = 0; x < Math.max(top.length, bottom.length); x++) {
294
+ const upper = tones[top[x]] || '';
295
+ const lower = tones[bottom[x]] || '';
296
+ const glyph = upper && lower ? upper === lower ? '\u2588' : '\u2580' : upper ? '\u2580' : lower ? '\u2584' : ' ';
297
+ const color = upper || lower;
298
+ const background = upper && lower && upper !== lower ? lower : '';
299
+ const last = segments[segments.length - 1];
300
+ if (last && last.glyph === glyph && last.color === color && last.background === background) last.text += glyph;
301
+ else segments.push({ glyph, text: glyph, color, background });
365
302
  }
303
+ rows.push(segments);
366
304
  }
367
- return chunks.flat();
305
+ return rows;
368
306
  }
369
307
  // dscode-style-v1
370
308
 
309
+ function dscodeFooterHeader(facts, stats) {
310
+ const raw = typeof facts?.model === "string" ? facts.model : "";
311
+ if (raw === "") return "";
312
+ const cut = raw.indexOf("/");
313
+ const provider = cut > 0 ? raw.slice(0, cut) : "";
314
+ const model = cut > 0 ? raw.slice(cut + 1) : raw;
315
+ const effort = typeof facts?.effort === "string" && facts.effort !== "" ? facts.effort : (typeof stats?.reasoningEffort === "string" ? stats.reasoningEffort : "");
316
+ return (provider === "" ? model : provider + ": " + model) + (effort === "" ? "" : " @ " + effort);
317
+ }
318
+ function dscodeStatusLead(facts, model, effort) {
319
+ const source = facts?.title !== void 0 && facts.title !== "" ? facts.title : facts?.sessionId;
320
+ const title = source === void 0 || source === "" ? "" : truncateColumns(safe(source), TITLE_BUDGET);
321
+ if (title !== "") return title;
322
+ return effort === "" ? model : model + " | " + effort;
323
+ }
324
+
371
325
 
372
326
  // dscode-tps-colors-v1
373
327
  function dscodeTpsTone(rate) {
@@ -377,8 +331,18 @@ function dscodeTpsTone(rate) {
377
331
  if (rate <= 250) return "blue";
378
332
  return "purple";
379
333
  }
334
+ // Cache hit rate: red under 90, yellow to 95, green to 98, blue above.
335
+ const dscodeCacheLabels = ["cache hit", "缓存命中", "快取命中", "キャッシュヒット", "캐시 적중", "éxitos de caché"];
336
+ function dscodeCacheTone(rate) {
337
+ if (!Number.isFinite(rate) || rate < 0) return null;
338
+ if (rate < 90) return "red";
339
+ if (rate < 95) return "yellow";
340
+ if (rate < 98) return "green";
341
+ return "blue";
342
+ }
380
343
  function dscodeTpsInkColor(tone) {
381
344
  const palette = getPalette();
345
+ if (tone === "red") return inkColor(getTheme() === "light" ? [185, 28, 28] : [248, 113, 113]);
382
346
  if (tone === "yellow") return inkColor(palette.warn);
383
347
  if (tone === "green") return inkColor(palette.success);
384
348
  if (tone === "blue") return inkColor(palette.brandBright);
@@ -387,13 +351,25 @@ function dscodeTpsInkColor(tone) {
387
351
  }
388
352
  function dscodeTelemetryParts(value) {
389
353
  const result = [];
390
- for (const [index, part] of value.split(" | ").entries()) {
391
- if (index > 0) result.push({ text: " | ", tone: null });
392
- const prefix = part.startsWith("current: ") ? "current: " : part.startsWith("average: ") ? "average: " : "";
393
- if (!prefix || !part.endsWith(" tps")) { result.push({ text: part, tone: null }); continue; }
394
- const display = part.slice(prefix.length, -4);
395
- const rate = Number(display.startsWith("~") ? display.slice(1) : display);
396
- result.push({ text: prefix, tone: null }, { text: display + " tps", tone: display && dscodeTpsTone(rate) });
354
+ const parts = String(value).split(" | ");
355
+ for (const [index, part] of parts.entries()) {
356
+ if (index > 0) result.push({ text: " | ", tone: null });
357
+ const match = /^(.+?: )(~?(?:\d+(?:\.\d+)?|--)) tps$/.exec(part);
358
+ if (match) {
359
+ const display = match[2];
360
+ const rate = Number(display.startsWith("~") ? display.slice(1) : display);
361
+ result.push({ text: match[1], tone: null }, { text: display + " tps", tone: display && dscodeTpsTone(rate) });
362
+ continue;
363
+ }
364
+ // The cache hit rate is the last segment of the telemetry line.
365
+ // The cache hit rate closes the telemetry line; only a recognised cache label is tinted.
366
+ const labelled = index === parts.length - 1 ? /^(.*?)(\d+(?:\.\d+)?)%$/.exec(part) : null;
367
+ const cache = labelled && dscodeCacheLabels.includes(labelled[1].replace(/:\s*$/, "").trim().toLowerCase()) ? labelled : null;
368
+ if (cache) {
369
+ result.push({ text: cache[1], tone: null }, { text: cache[2] + "%", tone: dscodeCacheTone(Number(cache[2])) });
370
+ continue;
371
+ }
372
+ result.push({ text: part, tone: null });
397
373
  }
398
374
  return result;
399
375
  }
@@ -402,6 +378,7 @@ function dscodeTelemetryNodes(value, key) {
402
378
  { key: key + "p" + index, color: dscodeTpsInkColor(part.tone) }, part.text));
403
379
  }
404
380
 
381
+
405
382
  function dscodeActivity(entries, streaming) {
406
383
  const running = entries.filter(entry => entry.kind === "tool" && entry.state === "running");
407
384
  const tool = running.at(-1);
@@ -454,7 +431,6 @@ function DscodeActivityLine({ entries, streaming, since, animated = true }) {
454
431
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, suffix)));
455
432
  }
456
433
 
457
-
458
434
  // dscode-footer-v1
459
435
  import { footerFor as dscodeFooterFor } from "../../plugins/session-metrics/view.mjs";
460
436
  // dscode-ime-v1
@@ -500,6 +476,7 @@ function imeWriter(stream, anchors) {
500
476
  function dscodeVisibleRelay(message) {
501
477
  return message.source.kind === "plugin" && message.source.plugin === "dscode-session-bridge" && message.source.form === "relay";
502
478
  }
479
+ // dscode-interaction-v2
503
480
  // dscode-interaction-v1
504
481
  function dscodeChatLines(entry, columns, verbose = false) {
505
482
  const width = Math.max(1, Math.floor(columns));
@@ -517,7 +494,7 @@ function dscodeChatLines(entry, columns, verbose = false) {
517
494
  return [...thinking, ...transcriptEntryLines({ ...entry, reasoning: "" }, columns, false, false, false)];
518
495
  }
519
496
  const rows = transcriptEntryLines(entry, columns, false, false, false);
520
- return entry.kind === "user" && !entry.notice ? userBackgroundRows(rows, columns, visibleColumns) : rows;
497
+ return entry.kind === "user" && !entry.notice ? [...userBackgroundRows(rows, columns, visibleColumns), { segments: [] }] : rows;
521
498
  }
522
499
  function dscodeThinkingLines(reasoning, width) {
523
500
  const lines = hangingTextLines("Thinking: " + reasoning.replace(/\s+/g, " ").trim(), width, "· ", "dimItalic", " ");
@@ -26148,7 +26125,7 @@ function replayProjectEvent(acc, event) {
26148
26125
  acc.streamingReasoning = "";
26149
26126
  acc.streaming = "";
26150
26127
  if (reason.kind === "error") {
26151
- const recovery = reason.error.code === "MISSING_CREDENTIAL" ? " · open /model to add an API key" : "";
26128
+ const recovery = reason.error.code === "MISSING_CREDENTIAL" ? " · run /login to add an API key" : "";
26152
26129
  appended.push({
26153
26130
  kind: "error",
26154
26131
  text: `${reason.error.code}: ${reason.error.message}${recovery}`
@@ -26181,18 +26158,9 @@ function replayProjectEvent(acc, event) {
26181
26158
  acc.busy = false;
26182
26159
  acc.busySince = 0;
26183
26160
  for (const entry of appended) appendReplayEntry(acc, entry);
26184
- for (let index = acc.entries.length - 1; index >= 0; index--) {
26185
- const entry = acc.entries[index];
26186
- if (entry === void 0 || entry.kind === "pending") continue;
26187
- if (entry.turnEnded !== true) {
26188
- acc.entries[index] = { ...entry, turnEnded: true };
26189
- acc.ops += 1;
26190
- }
26191
- break;
26192
- }
26193
- return true;
26194
- }
26195
- case "llm/retry": {
26161
+ return true;
26162
+ }
26163
+ case "llm/retry": {
26196
26164
  const data = event.data;
26197
26165
  acc.streaming = "";
26198
26166
  acc.streamingReasoning = "";
@@ -28912,10 +28880,11 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
28912
28880
  identity.push(span);
28913
28881
  };
28914
28882
  const model = safe(facts.model).split("/").at(-1);
28915
- if (model !== "" && enabled.has("model")) {
28883
+ // dscode-footer-rows-v1: the title leads row 1, the model rides the footer header.
28884
+ if ((model !== "" && enabled.has("model")) || enabled.has("title")) {
28916
28885
  const effort = safe(facts.effort ?? stats.reasoningEffort);
28917
28886
  push({
28918
- text: effort === "" ? model : `${model} | ${effort}`,
28887
+ text: dscodeStatusLead(facts, model, effort),
28919
28888
  tone: "model"
28920
28889
  });
28921
28890
  }
@@ -29045,15 +29014,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
29045
29014
  id: "tokens"
29046
29015
  });
29047
29016
  }
29048
- const label = truncateColumns(safe(facts.title !== void 0 && facts.title !== "" ? facts.title : facts.sessionId), TITLE_BUDGET);
29049
- if (label !== "" && enabled.has("title")) row2.push({
29050
- group: { spans: [{
29051
- text: label,
29052
- tone: "meta"
29053
- }] },
29054
- rank: RANK_TITLE,
29055
- id: "title"
29056
- });
29017
+ // dscode-footer-rows-v1: the title moved to the identity lead on row 1.
29057
29018
  if (facts.goal !== void 0 && enabled.has("goal")) row2.push({
29058
29019
  group: { spans: [{
29059
29020
  text: facts.goal.phase === "active" ? "◎ round " + facts.goal.rounds + "/" + facts.goal.max : "◎ " + safe(facts.goal.phase),
@@ -29074,15 +29035,16 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
29074
29035
  const permission = safe(facts.permission);
29075
29036
  let badge = -1;
29076
29037
  if (permission !== "" && enabled.has("permission")) {
29077
- right.push({
29078
- span: {
29038
+ // dscode-footer-rows-v1: the permission rides row 1 next to the title.
29039
+ left.push({
29040
+ group: { spans: [{
29079
29041
  text: permission,
29080
29042
  tone: permissionTone(permission)
29081
- },
29043
+ }] },
29082
29044
  rank: RANK_BADGE,
29083
29045
  id: "permission"
29084
29046
  });
29085
- badge = right.length - 1;
29047
+ badge = left.length - 1;
29086
29048
  }
29087
29049
  return {
29088
29050
  left,
@@ -29446,7 +29408,7 @@ function transcriptEntryLines(entry, columns, showReasoning = true, reasoningTog
29446
29408
  }
29447
29409
  /** Settled-history variant carrying the Ctrl+R reasoning fold. */
29448
29410
  function settledEntryLines(entry, columns, showReasoning) {
29449
- return dscodeChatLines(entry, columns, showReasoning);
29411
+ return turnDividedLines(entry, columns, dscodeChatLines(entry, columns, showReasoning));
29450
29412
  }
29451
29413
  /**
29452
29414
  * Clamp the live-region allocation so the flexible dynamic rows never exceed
@@ -30893,6 +30855,37 @@ function DscodeUltraRipple({ columns }) {
30893
30855
  bold: settled || Math.abs(Math.abs(index - center) - Math.max(0, tick - 1) * 3) < 3
30894
30856
  }, letter))), rippleLine(2));
30895
30857
  }
30858
+ function DscodeUltraFocus({ width, animations }) {
30859
+ const palette = getPalette();
30860
+ const [frame, setFrame] = (0, import_react.useState)(0);
30861
+ const lastFrame = Math.ceil(width / 6) + 2;
30862
+ (0, import_react.useEffect)(() => {
30863
+ if (!animations) return;
30864
+ let step = 0;
30865
+ const timer = setInterval(() => {
30866
+ step++;
30867
+ setFrame(step);
30868
+ if (step >= lastFrame) clearInterval(timer);
30869
+ }, 55);
30870
+ return () => clearInterval(timer);
30871
+ }, [animations, lastFrame]);
30872
+ const label = dscodeEffortCenter(truncateColumns('✦ ULTRA ✦', width), width);
30873
+ const center = (width - 1) / 2;
30874
+ const settled = !animations || frame >= lastFrame;
30875
+ const first = label.length - label.trimStart().length;
30876
+ const last = label.trimEnd().length - 1;
30877
+ return (0, import_react.createElement)(Text, { wrap: 'truncate-end' },
30878
+ ...Array.from(label, (letter, index) => {
30879
+ const outer = index < first || index > last;
30880
+ const burst = animations && !settled && outer &&
30881
+ (index === Math.floor(center - frame * 3) || index === Math.ceil(center + frame * 3));
30882
+ return (0, import_react.createElement)(Text, {
30883
+ key: index,
30884
+ color: inkColor(settled ? outer ? palette.brandDeep : palette.brandBright : dscodeRippleTone(index, center, frame, palette)),
30885
+ bold: burst || letter !== ' ' && (settled || Math.abs(Math.abs(index - center) - frame * 3) < 3)
30886
+ }, burst ? '✦' : outer ? '─' : letter);
30887
+ }));
30888
+ }
30896
30889
  function DscodeEffortBar({ row, current, select, back, onExit, animations = true }) {
30897
30890
  const ids = ['low', 'high', 'max', 'ultra'];
30898
30891
  const advertised = row.reasoning.efforts;
@@ -30957,37 +30950,6 @@ function DscodeEffortBar({ row, current, select, back, onExit, animations = true
30957
30950
  (0, import_react.createElement)(Text, { color: inkColor(palette.dim), wrap: 'truncate-end' },
30958
30951
  truncateColumns(footer, contentWidth)));
30959
30952
  }
30960
- function DscodeUltraFocus({ width, animations }) {
30961
- const palette = getPalette();
30962
- const [frame, setFrame] = (0, import_react.useState)(0);
30963
- const lastFrame = Math.ceil(width / 6) + 2;
30964
- (0, import_react.useEffect)(() => {
30965
- if (!animations) return;
30966
- let step = 0;
30967
- const timer = setInterval(() => {
30968
- step++;
30969
- setFrame(step);
30970
- if (step >= lastFrame) clearInterval(timer);
30971
- }, 55);
30972
- return () => clearInterval(timer);
30973
- }, [animations, lastFrame]);
30974
- const label = dscodeEffortCenter(truncateColumns('✦ ULTRA ✦', width), width);
30975
- const center = (width - 1) / 2;
30976
- const settled = !animations || frame >= lastFrame;
30977
- const first = label.length - label.trimStart().length;
30978
- const last = label.trimEnd().length - 1;
30979
- return (0, import_react.createElement)(Text, { wrap: 'truncate-end' },
30980
- ...Array.from(label, (letter, index) => {
30981
- const outer = index < first || index > last;
30982
- const burst = animations && !settled && outer &&
30983
- (index === Math.floor(center - frame * 3) || index === Math.ceil(center + frame * 3));
30984
- return (0, import_react.createElement)(Text, {
30985
- key: index,
30986
- color: inkColor(settled ? outer ? palette.brandDeep : palette.brandBright : dscodeRippleTone(index, center, frame, palette)),
30987
- bold: burst || letter !== ' ' && (settled || Math.abs(Math.abs(index - center) - frame * 3) < 3)
30988
- }, burst ? '✦' : outer ? '─' : letter);
30989
- }));
30990
- }
30991
30953
  function EffortPanel(props) {
30992
30954
  const reasoning = props.row.reasoning;
30993
30955
  const ids = reasoning?.efforts.map(effort => effort.id) ?? [];
@@ -31969,7 +31931,8 @@ const SYNCHRONIZED_UPDATE_END = "\x1B[?2026l";
31969
31931
  /** One source of truth for TUI-owned slash commands in completion and `/help`. */
31970
31932
  const LOCAL_COMMANDS = [
31971
31933
  { label: "/email", description: "browse email and steer into this session" },
31972
- { label: "/login", description: "save a DeepSeek API key locally" },
31934
+ { label: "/login", description: "save a provider API key locally (/login [deepseek|openrouter])" },
31935
+ { label: "/provider", description: "switch between DeepSeek and OpenRouter" },
31973
31936
  // dscode: startup command discovery
31974
31937
  {"label":"/status","description":"session, model, permissions and usage"},
31975
31938
  {"label":"/doctor","description":"read-only runtime diagnostics"},
@@ -32044,10 +32007,6 @@ const LOCAL_COMMANDS = [
32044
32007
  label: "/verbose",
32045
32008
  description: "toggle thinking and tool call details in the chat"
32046
32009
  },
32047
- {
32048
- label: "/mouse",
32049
- description: "toggle mouse capture: off to select and copy text, on for wheel scrolling"
32050
- },
32051
32010
  {
32052
32011
  label: "/language",
32053
32012
  description: "show or set the interface language: en, zh-CN, zh-TW, ja, ko, es"
@@ -32415,7 +32374,7 @@ function Header({ cwd = "", model = "", effort = "", animated = false }) {
32415
32374
  if (!full) return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1 },
32416
32375
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
32417
32376
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "❄ DSCODE"),
32418
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.7.1")),
32377
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.7.3")),
32419
32378
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(modelName + " · " + effortName, width)),
32420
32379
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" }, welcomePath(cwd, width)));
32421
32380
  return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1 },
@@ -32426,7 +32385,7 @@ function Header({ cwd = "", model = "", effort = "", animated = false }) {
32426
32385
  (0, import_react.createElement)(Box, { flexDirection: "column", width: detailsWidth, marginTop: 2 },
32427
32386
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().text), bold: true }, "DSCODE"),
32428
32387
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandDeep) }, "────────────"),
32429
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.7.1"),
32388
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.7.3"),
32430
32389
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.model"), 9) + modelName, detailsWidth)),
32431
32390
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.effort"), 9) + effortName, detailsWidth)),
32432
32391
  (0, import_react.createElement)(Text, null, " "),
@@ -32637,7 +32596,7 @@ function deepseekWaveHues(tier) {
32637
32596
  function StatusLine({ facts, stats, busy, columns, items }) {
32638
32597
  const [, refreshMetrics] = (0, import_react.useState)(0);
32639
32598
  (0, import_react.useEffect)(() => { const timer = setInterval(() => refreshMetrics(n => n + 1), 1000); return () => clearInterval(timer); }, []);
32640
- facts = { ...facts, telemetry: columns >= 48 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.min(columns - 8, Math.max(40, Math.floor(columns * 0.8) - 4))), dscodeLocale) : "" };
32599
+ facts = { ...facts, telemetry: columns >= 48 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.min(columns - 8, Math.max(40, Math.floor(columns * 0.8) - 4))), dscodeFooterHeader(facts, stats), dscodeLocale) : "" };
32641
32600
  const layout = (0, import_react.useMemo)(() => layoutStatusBar(facts, stats, Math.max(8, columns - 2), {
32642
32601
  busy,
32643
32602
  items,
@@ -32697,7 +32656,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32697
32656
  return (0, import_react.createElement)(Box, {
32698
32657
  paddingLeft: 2 + indent,
32699
32658
  width: columns, // dscode-footer-width
32700
- justifyContent: rightParts.length > 0 ? (leftParts.length > 0 ? "space-between" : "flex-end") : void 0 // dscode-footer-align
32659
+ justifyContent: rightParts.length > 0 ? "space-between" : void 0
32701
32660
  }, (0, import_react.createElement)(Text, { wrap: "truncate-end" }, ...leftParts), rightParts.length > 0 ? (0, import_react.createElement)(Text, { wrap: "truncate-end" }, ...rightParts) : void 0);
32702
32661
  };
32703
32662
  const row2Present = layout.row2.left.length > 0 || layout.row2.right.length > 0;
@@ -33496,53 +33455,89 @@ function ProviderPanel({ directory, error, authorizations, authorizationError, o
33496
33455
  }, truncateColumns("↑↓ move · enter configure · l login · o logout · d remove key · x remove provider · r retry · esc back", viewport.contentColumns)));
33497
33456
  }
33498
33457
  // dscode-login-v1
33499
- function DscodeLoginPanel({ load, save, done, back }) {
33500
- const [draft, setDraft] = (0, import_react.useState)("");
33501
- const [target, setTarget] = (0, import_react.useState)(void 0);
33502
- const [error, setError] = (0, import_react.useState)("");
33503
- const [busy, setBusy] = (0, import_react.useState)(false);
33504
- const saving = (0, import_react.useRef)(false);
33505
- (0, import_react.useEffect)(() => {
33506
- let active = true;
33507
- Promise.resolve().then(() => load()).then(directory => {
33508
- if (!active) return;
33509
- const row = directory.rows.find(row => row.provider === "deepseek-official");
33510
- if (!row || !save) { setError("DeepSeek credential storage is unavailable."); return; }
33511
- if (row.credential?.kind !== "facts") { setError("Could not read credential status. Check local file permissions."); return; }
33512
- if (!row.credential.writable) { setError("DEEPSEEK_API_KEY is set by your environment. Remove it and restart to use /login."); return; }
33513
- setTarget(row);
33514
- }, () => { if (active) setError("Could not load DeepSeek credential settings."); });
33515
- return () => { active = false; };
33516
- }, [load, save]);
33517
- useStableInput((input, key) => {
33518
- if (saving.current) return;
33519
- if (key.escape || key.ctrl && input === "c") { setDraft(""); back(); return; }
33520
- if (!target) return;
33521
- if (key.return) {
33522
- const raw = draft.trim();
33523
- if (!raw || /[\s\x00-\x1f\x7f-\uffff]/.test(raw) || ENV_ASSIGNMENT.test(raw) || hasWrappingQuotes(raw)) {
33524
- setError("Paste only the API key, without quotes, spaces or an environment-variable name."); return;
33525
- }
33526
- saving.current = true; setBusy(true); setError(""); setDraft("");
33527
- Promise.resolve().then(() => save(target, raw)).then(done, () => {
33528
- saving.current = false; setBusy(false);
33529
- setError("Could not save the API key. Check file permissions and available disk space, then paste again.");
33530
- });
33531
- return;
33532
- }
33533
- if (key.ctrl && input === "u") { setDraft(""); setError(""); return; }
33534
- if (key.backspace || key.delete) { setDraft(current => [...current].slice(0, -1).join("")); return; }
33535
- if (key.ctrl || key.meta || !input) return;
33536
- const pasted = stripPasteMarkers(input);
33537
- setDraft(current => (current + pasted).slice(0, 4096));
33538
- setError("");
33539
- });
33540
- return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2 },
33541
- (0, import_react.createElement)(Text, { bold: true }, "DeepSeek login"),
33542
- (0, import_react.createElement)(Text, { dimColor: true }, "Saved on this Mac in ~/.dscode/credentials.yaml"),
33543
- (0, import_react.createElement)(Text, null, busy ? "Saving…" : target ? "API key › " + (draft ? "••••••••" : "paste your key") : error ? "" : "Loading…"),
33544
- error ? (0, import_react.createElement)(Text, { color: "red" }, error) : void 0,
33545
- (0, import_react.createElement)(Text, { dimColor: true }, "Enter save · Esc cancel · Ctrl+U clear"));
33458
+ function DscodeLoginPanel({ provider, load, save, done, back }) {
33459
+ const spec = dscodeProviderSpec(provider) ?? DSCODE_PROVIDERS[0];
33460
+ const [draft, setDraft] = (0, import_react.useState)("");
33461
+ const [target, setTarget] = (0, import_react.useState)(void 0);
33462
+ const [error, setError] = (0, import_react.useState)("");
33463
+ const [busy, setBusy] = (0, import_react.useState)(false);
33464
+ const saving = (0, import_react.useRef)(false);
33465
+ (0, import_react.useEffect)(() => {
33466
+ let active = true;
33467
+ Promise.resolve().then(() => load()).then(directory => {
33468
+ if (!active) return;
33469
+ const row = directory.rows.find(row => row.provider === spec.id);
33470
+ if (!row || !save) { setError(spec.name + " credential storage is unavailable."); return; }
33471
+ if (row.credential?.kind === "error") { setError("Could not read credential status. Check local file permissions."); return; }
33472
+ if (row.credential?.kind === "facts" && !row.credential.writable) { setError(spec.credentialRef + " is set by your environment. Remove it and restart to use /login."); return; }
33473
+ setTarget(row);
33474
+ }, () => { if (active) setError("Could not load " + spec.name + " credential settings."); });
33475
+ return () => { active = false; };
33476
+ }, [spec.id]);
33477
+ useStableInput((input, key) => {
33478
+ if (saving.current) return;
33479
+ if (key.escape || key.ctrl && input === "c") { setDraft(""); back(); return; }
33480
+ if (!target) return;
33481
+ if (key.return) {
33482
+ const raw = draft.trim();
33483
+ if (!raw || /[\s\x00-\x1f\x7f-￿]/.test(raw) || ENV_ASSIGNMENT.test(raw) || hasWrappingQuotes(raw)) {
33484
+ setError("Paste only the API key, without quotes, spaces or an environment-variable name."); return;
33485
+ }
33486
+ saving.current = true; setBusy(true); setError(""); setDraft("");
33487
+ Promise.resolve().then(() => save(target, raw)).then(done, () => {
33488
+ saving.current = false; setBusy(false);
33489
+ setError("Could not save the API key. Check file permissions and available disk space, then paste again.");
33490
+ });
33491
+ return;
33492
+ }
33493
+ if (key.ctrl && input === "u") { setDraft(""); setError(""); return; }
33494
+ if (key.backspace || key.delete) { setDraft(current => [...current].slice(0, -1).join("")); return; }
33495
+ if (key.ctrl || key.meta || !input) return;
33496
+ const pasted = stripPasteMarkers(input);
33497
+ setDraft(current => (current + pasted).slice(0, 4096));
33498
+ setError("");
33499
+ });
33500
+ return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2 },
33501
+ (0, import_react.createElement)(Text, { bold: true }, spec.name + " login"),
33502
+ (0, import_react.createElement)(Text, { dimColor: true }, "Saved on this Mac in ~/.dscode/credentials.yaml"),
33503
+ (0, import_react.createElement)(Text, null, busy ? "Saving…" : target ? "API key › " + (draft ? "••••••••" : "paste your key") : error ? "" : "Loading…"),
33504
+ error ? (0, import_react.createElement)(Text, { color: "red" }, error) : void 0,
33505
+ (0, import_react.createElement)(Text, { dimColor: true }, "Enter save · Esc cancel · Ctrl+U clear"));
33506
+ }
33507
+ function DscodeProviderPanel({ current, load, choose, back }) {
33508
+ const [directory, setDirectory] = (0, import_react.useState)(void 0);
33509
+ const [failed, setFailed] = (0, import_react.useState)(false);
33510
+ const [cursor, setCursor] = (0, import_react.useState)(() => Math.max(0, DSCODE_PROVIDERS.findIndex(provider => provider.id === current)));
33511
+ (0, import_react.useEffect)(() => {
33512
+ let active = true;
33513
+ Promise.resolve().then(() => load()).then(loaded => { if (active) setDirectory(loaded); }, () => { if (active) setFailed(true); });
33514
+ return () => { active = false; };
33515
+ }, []);
33516
+ useStableInput((input, key) => {
33517
+ const count = DSCODE_PROVIDERS.length;
33518
+ if (key.escape || key.ctrl && input === "c" || input === "q") { back(); return; }
33519
+ if (key.upArrow || input === "k") { setCursor(index => (index + count - 1) % count); return; }
33520
+ if (key.downArrow || input === "j") { setCursor(index => (index + 1) % count); return; }
33521
+ if (key.return) { back(); choose(DSCODE_PROVIDERS[cursor].id); }
33522
+ });
33523
+ const status = provider => {
33524
+ if (failed) return "status unavailable";
33525
+ if (directory === void 0) return "…";
33526
+ const row = directory.rows.find(row => row.provider === provider.id);
33527
+ const state = dscodeCredentialState(row);
33528
+ if (state === "saved") return "key saved";
33529
+ if (state === "env") return "key from " + provider.credentialRef;
33530
+ if (state === "readonly") return provider.credentialRef + " is empty";
33531
+ if (state === "error") return "credential status unavailable";
33532
+ if (state === "unavailable") return "unavailable in this profile";
33533
+ return row?.configured === true || provider.id === "deepseek-official" ? "needs an API key" : "not set up · Enter sets it up";
33534
+ };
33535
+ return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2 },
33536
+ (0, import_react.createElement)(Text, { bold: true }, "Provider"),
33537
+ (0, import_react.createElement)(Text, { dimColor: true, wrap: "truncate-end" }, "The next step uses the chosen provider; /model picks among its models."),
33538
+ ...DSCODE_PROVIDERS.map((provider, index) => (0, import_react.createElement)(Text, { key: provider.id, bold: index === cursor, wrap: "truncate-end" },
33539
+ (index === cursor ? "› " : " ") + (provider.id === current ? "● " : "○ ") + provider.name.padEnd(11) + provider.id + " · " + status(provider))),
33540
+ (0, import_react.createElement)(Text, { dimColor: true }, "↑↓ choose · Enter switch · Esc cancel"));
33546
33541
  }
33547
33542
 
33548
33543
  function ProviderSetupPanel({ target, save, saveCredential, discover, effortDonors, done, back, onExit }) {
@@ -34672,7 +34667,7 @@ function CompletionMenu({ active, mention, index, rows, error }) {
34672
34667
  * While a modal (approval / question / model panel) owns the keys, the
34673
34668
  * box passes every key through untouched.
34674
34669
  */
34675
- function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openLanguage, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, readClipboardImage, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, onTranscriptScroll, sessionKey }) {
34670
+ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openProvider, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openLanguage, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, readClipboardImage, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, sessionKey }) {
34676
34671
  const columns = useStdout().stdout?.columns ?? 80;
34677
34672
  const inputTerminalRows = useStdout().stdout?.rows ?? 30;
34678
34673
  const dscodeImeStdout = useStdout().stdout;
@@ -35146,7 +35141,6 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35146
35141
  return;
35147
35142
  }
35148
35143
  ctrlCArmedRef.current = false;
35149
- if (key.pageUp || key.pageDown) { onTranscriptScroll?.(key.pageUp ? 1 : -1, true); return; }
35150
35144
  if (preparingImages) {
35151
35145
  if (key.escape || key.ctrl && input === "c") cancelImageSubmission();
35152
35146
  return;
@@ -35236,9 +35230,19 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35236
35230
  valueRef.current = ""; cursorRef.current = 0;
35237
35231
  setValue(""); setCursor(0); setCompletionIndex(0); setDismissedMenuValue(void 0);
35238
35232
  recall.current = beginRecall(recallSpace, "");
35239
- if (trimmed !== "/login") { notify("Use /login alone, then paste the key in the private input.", "warning"); return; }
35233
+ const dscodeLoginTarget = dscodeProviderArgument(trimmed.slice(6));
35234
+ if (dscodeLoginTarget === null) { notify("Use /login, /login deepseek or /login openrouter, then paste the key in the private input.", "warning"); return; }
35240
35235
  if (busy) { notify("Stop the running turn before /login.", "warning"); return; }
35241
- openLogin(); return;
35236
+ openLogin(dscodeLoginTarget); return;
35237
+ }
35238
+ if (trimmed === "/provider" || /^\/provider\s/.test(trimmed)) {
35239
+ valueRef.current = ""; cursorRef.current = 0;
35240
+ setValue(""); setCursor(0); setCompletionIndex(0); setDismissedMenuValue(void 0);
35241
+ recall.current = beginRecall(recallSpace, "");
35242
+ const dscodeProviderTarget = dscodeProviderArgument(trimmed.slice(9));
35243
+ if (dscodeProviderTarget === null) { notify("Usage: /provider [deepseek|openrouter]", "warning"); return; }
35244
+ if (busy) { notify("Stop the running turn before /provider.", "warning"); return; }
35245
+ openProvider(dscodeProviderTarget); return;
35242
35246
  }
35243
35247
  if (draftImagesRef.current.length > 0 || draftFilesRef.current.length > 0) {
35244
35248
  if (isSlashLine(text)) notify("commands cannot carry attachments; the line will be sent to the model as a prompt", "warning");
@@ -35408,12 +35412,6 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35408
35412
  toggleReasoning();
35409
35413
  return;
35410
35414
  }
35411
- if (text === "/mouse") {
35412
- const enabled = dscodeSetMouse(!dscodeMouseEnabled);
35413
- dscodeSaveFlag("mouse", enabled);
35414
- notify(dscodeT(enabled ? "mouse.on" : "mouse.off"));
35415
- return;
35416
- }
35417
35415
  if (text === "/language" || text.startsWith("/language ")) {
35418
35416
  const wanted = text.slice(9).trim();
35419
35417
  if (!wanted) { openLanguage(); return; }
@@ -36119,6 +36117,19 @@ function App(props) {
36119
36117
  rows: appStdout?.rows ?? 30
36120
36118
  }));
36121
36119
  const terminalSizeRef = (0, import_react.useRef)(terminalSize);
36120
+ const settledRowsCache = (0, import_react.useRef)(void 0);
36121
+ const settledRows = (0, import_react.useMemo)(() => {
36122
+ const result = computeSettledRows(settledRowsCache.current, view.entries, settled, showReasoning, props.resumed, refreshEpoch, terminalSize.columns, SETTLED_ROW_CAP, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel, animated: animations });
36123
+ settledRowsCache.current = result.cache;
36124
+ return result.cache.flat;
36125
+ }, [
36126
+ view.entries,
36127
+ settled,
36128
+ showReasoning,
36129
+ props.resumed,
36130
+ refreshEpoch,
36131
+ terminalSize.columns
36132
+ ]);
36122
36133
  const synchronizedReplayPending = (0, import_react.useRef)(false);
36123
36134
  (0, import_react.useEffect)(() => {
36124
36135
  if (appStdout === void 0) return;
@@ -36156,60 +36167,21 @@ function App(props) {
36156
36167
  setMenuRows((current) => current === rows ? current : rows);
36157
36168
  }, []);
36158
36169
  const composerEditorCap = composerMaxRows(terminalRows);
36159
- const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !languageOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
36160
- const welcomeFull = terminalRows >= 24 && terminalColumns >= 64;
36161
- const welcomeMaxRows = welcomeFull ? terminalRows >= 26 ? 14 : 13 : terminalRows >= 10 ? 4 : 1;
36162
- // The nine fixed rows belong to the composer, footer and their gutters.
36163
- const transcriptCapacity = transcriptVisible ? Math.max(0, terminalRows - 9 - composerGutterRows - (composerRows - 1) - menuRows) : 0;
36164
- const streamingActive = view.streaming !== "";
36165
- const deepDivingVisible = busy;
36166
- const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2), showReasoning)), [
36167
- view.entries,
36168
- settled,
36169
- terminalColumns,
36170
- showReasoning
36171
- ]);
36172
- const allSettledLines = (0, import_react.useMemo)(() => visibleSettledLines(view.entries, settled, Infinity, terminalColumns, showReasoning, settledEntryLines), [settled, view.entries[settled - 1], terminalColumns, showReasoning]);
36173
- const settledTail = allSettledLines.slice(-transcriptCapacity);
36174
- const streamingDemand = streamingActive ? textLines(view.streaming.slice(-Math.max(1, terminalColumns * transcriptCapacity)), Math.max(10, terminalColumns - 2)).length : 0;
36175
- const demand = settledTail.length + allLiveLines.length + streamingDemand + (busy ? 1 : 0) + (agentRows.length > 0 ? 1 : 0);
36176
- const welcomeRows = welcomeVisibleRows(transcriptCapacity, welcomeMaxRows, demand, transcriptVisible);
36177
- const settledBudget = transcriptVisible ? Math.max(0, transcriptCapacity - welcomeRows) : 0;
36178
- const liveDemand = allLiveLines.length + streamingDemand;
36179
- const liveCap = busy || streamingActive ? Math.max(1, Math.floor(settledBudget * 2 / 3)) : 0;
36180
- const liveRows = Math.min(liveDemand, liveCap);
36181
- const settledViewportRows = Math.max(0, settledBudget - liveRows);
36182
- const [scrollOffset, setScrollOffset] = (0, import_react.useState)(0);
36183
- const previousHistory = (0, import_react.useRef)({ sessionKey: props.sessionKey, count: allSettledLines.length });
36184
- const sameHistory = previousHistory.current.sessionKey === props.sessionKey;
36185
- const growth = sameHistory ? Math.max(0, allSettledLines.length - previousHistory.current.count) : 0;
36186
- const maxScrollOffset = Math.max(0, allSettledLines.length - settledViewportRows);
36187
- const effectiveScrollOffset = sameHistory ? Math.min(maxScrollOffset, scrollOffset === 0 ? 0 : scrollOffset + growth) : 0;
36188
- (0, import_react.useEffect)(() => {
36189
- previousHistory.current = { sessionKey: props.sessionKey, count: allSettledLines.length };
36190
- if (scrollOffset !== effectiveScrollOffset) setScrollOffset(effectiveScrollOffset);
36191
- }, [props.sessionKey, allSettledLines.length, effectiveScrollOffset]);
36192
- const scrollTranscript = (direction, page = false) => {
36193
- if (!transcriptVisible || settledViewportRows <= 0) return;
36194
- const step = page ? Math.max(1, settledViewportRows - 1) : 1;
36195
- const amount = direction * step;
36196
- if (amount === 0) return;
36197
- setScrollOffset(current => Math.max(0, Math.min(maxScrollOffset, (current === scrollOffset ? effectiveScrollOffset : current) + amount)));
36198
- };
36199
- const scrollRef = (0, import_react.useRef)(scrollTranscript);
36200
- scrollRef.current = scrollTranscript;
36201
- (0, import_react.useEffect)(() => {
36202
- const listener = delta => scrollRef.current(delta);
36203
- dscodeWheelListeners.add(listener);
36204
- return () => dscodeWheelListeners.delete(listener);
36205
- }, []);
36206
- const renderedSettled = transcriptWindow(allSettledLines, settledViewportRows, effectiveScrollOffset);
36207
- const dynamicRows = Math.max(0, settledBudget - settledViewportRows);
36208
- const liveBudget = dynamicRows === 0 ? 0 : streamingActive ? Math.max(0, dynamicRows - Math.min(streamingDemand, dynamicRows)) : dynamicRows;
36170
+ const MENU_RESERVE_ROWS = 5;
36171
+ const dynamicRows = Math.max(1, terminalRows - 8 - MENU_RESERVE_ROWS - composerGutterRows - (composerRows - 1) - Math.max(0, menuRows - MENU_RESERVE_ROWS));
36172
+ const streamingActive = view.streaming !== "" || showReasoning && view.streamingReasoning !== "";
36173
+ const deepDivingVisible = busy;
36174
+ const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2), showReasoning)), [
36175
+ view.entries,
36176
+ settled,
36177
+ terminalColumns,
36178
+ showReasoning
36179
+ ]);
36180
+ const liveBudget = busy || streamingActive ? Math.max(1, Math.floor(dynamicRows / 3)) : Math.max(0, dynamicRows - (deepDivingVisible ? 1 : 0));
36209
36181
  const visibleLiveLines = liveBudget === 0 ? [] : allLiveLines.slice(-liveBudget);
36210
36182
  const streamRows = Math.max(1, dynamicRows - visibleLiveLines.length);
36211
- const reasoningRows = 0;
36212
- const answerRows = view.streaming === "" || dynamicRows === 0 ? 0 : Math.max(1, streamRows - reasoningRows);
36183
+ const reasoningRows = !showReasoning || view.streamingReasoning === "" || view.streaming !== "" ? 0 : streamRows;
36184
+ const answerRows = view.streaming === "" ? 0 : Math.max(1, streamRows - reasoningRows);
36213
36185
  const liveAudit = clampLiveAllocation({
36214
36186
  live: visibleLiveLines.length,
36215
36187
  reasoning: reasoningRows,
@@ -36222,7 +36194,7 @@ function App(props) {
36222
36194
  const auditedLiveLines = liveAudit.allocation.live === visibleLiveLines.length ? visibleLiveLines : visibleLiveLines.slice(-liveAudit.allocation.live);
36223
36195
  const auditedReasoningRows = liveAudit.allocation.reasoning;
36224
36196
  const auditedAnswerRows = liveAudit.allocation.answer;
36225
-
36197
+ const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !languageOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
36226
36198
  const modalVisible = emailOpen || modelOpen || helpOpen || modeOpen || permissionOpen || resumeOpen || pluginOpen || jobsOpen || statuslineOpen || themeOpen || languageOpen || historyOpen || agentsOpen || subagentOpen || todosOpen || verboseOpen && !approvalPending && !questionPending || diffView !== void 0 || approvalPending || questionPending;
36227
36199
  const closeInspector = (0, import_react.useCallback)(() => {
36228
36200
  setVerboseOpen(false);
@@ -36239,6 +36211,15 @@ function App(props) {
36239
36211
  synchronizedReplayPending.current = false;
36240
36212
  appStdout.write(SYNCHRONIZED_UPDATE_END);
36241
36213
  }, [appStdout, refreshEpoch]);
36214
+ const settledNeedsTrim = settledRowsCache.current?.needsTrim === true;
36215
+ (0, import_react.useEffect)(() => {
36216
+ if (!settledNeedsTrim || busy || streamingActive) return;
36217
+ refreshScreen();
36218
+ }, [
36219
+ settledNeedsTrim,
36220
+ busy,
36221
+ streamingActive
36222
+ ]);
36242
36223
  const sessionHasImages = (0, import_react.useMemo)(() => view.entries.some((entry) => (entry.kind === "user" || entry.kind === "pending") && (entry.images?.length ?? 0) > 0), [view.entries]);
36243
36224
  /** Apply one /model pick: record the selection, close the panel, report via notice. */
36244
36225
  const applyModel = (row, effortId) => {
@@ -36258,6 +36239,27 @@ function App(props) {
36258
36239
  notify(`model switch failed: ${error instanceof Error ? error.message : String(error)}`, "error");
36259
36240
  }
36260
36241
  };
36242
+ // dscode-provider-switch
36243
+ const dscodeSwitchProvider = (provider) => {
36244
+ const spec = dscodeProviderSpec(provider);
36245
+ if (props.loadModelProviders === void 0) { notify("provider switching is unavailable in this profile", "warning"); return; }
36246
+ Promise.resolve().then(async () => {
36247
+ if (props.dscodeEnsureProviderRoute !== void 0 && await props.dscodeEnsureProviderRoute(provider)) reloadModelSurfaces();
36248
+ const providers = await props.loadModelProviders();
36249
+ const state = dscodeCredentialState(providers.rows.find((row) => row.provider === provider));
36250
+ if (state === "unavailable") { notify(spec.name + " is unavailable in this profile", "error"); return; }
36251
+ if (state === "missing") {
36252
+ setProviderOpen(false); setEffortFor(void 0);
36253
+ setProviderAction({ kind: "dscode-key", provider, then: () => dscodeSwitchProvider(provider) }); setModelOpen(true);
36254
+ return;
36255
+ }
36256
+ if (state === "error" || state === "readonly") notify("could not confirm the " + spec.name + " API key; requests may fail until /login " + provider + " succeeds", "warning");
36257
+ const models = await dscodeWaitForModels(props.loadModels, provider);
36258
+ const pick = dscodePickModel(models.rows, provider, modelLabel, effortLabel);
36259
+ if (pick === void 0) { notify(spec.name + " serves no models yet — check /model", "error"); return; }
36260
+ applyModel(pick.row, pick.effort);
36261
+ }).catch((error) => notify("provider switch failed: " + (error instanceof Error ? error.message : String(error)), "error"));
36262
+ };
36261
36263
  const reloadModelSurfaces = () => {
36262
36264
  setModelLoadEpoch((epoch) => epoch + 1);
36263
36265
  };
@@ -36300,10 +36302,21 @@ function App(props) {
36300
36302
  }, [providerDirectory, directory]);
36301
36303
  let modelSurface;
36302
36304
  if (modelOpen && !approvalPending && !questionPending) {
36303
- if (providerAction?.kind === "dscode-key") modelSurface = (0, import_react.createElement)(DscodeLoginPanel, {
36305
+ if (providerAction?.kind === "dscode-provider") modelSurface = (0, import_react.createElement)(DscodeProviderPanel, {
36306
+ current: dscodeProviderOfLabel(modelLabel),
36304
36307
  load: props.loadModelProviders,
36308
+ choose: dscodeSwitchProvider,
36309
+ back: closeModelSurface
36310
+ });
36311
+ else if (providerAction?.kind === "dscode-key") modelSurface = (0, import_react.createElement)(DscodeLoginPanel, {
36312
+ provider: providerAction.provider,
36313
+ load: async () => { await props.dscodeEnsureProviderRoute?.(providerAction.provider); return props.loadModelProviders(); },
36305
36314
  save: props.saveModelProviderCredential,
36306
- done: () => { closeModelSurface(); reloadModelSurfaces(); notify("DeepSeek API key saved locally; ready to use."); },
36315
+ done: () => {
36316
+ const then = providerAction.then;
36317
+ closeModelSurface(); reloadModelSurfaces();
36318
+ if (then !== void 0) then(); else notify(dscodeProviderSpec(providerAction.provider).name + " API key saved locally; ready to use.");
36319
+ },
36307
36320
  back: closeModelSurface
36308
36321
  });
36309
36322
  else if (providerAction?.kind === "login" && props.beginProviderAuthorization !== void 0 && props.cancelProviderAuthorization !== void 0 && props.openAuthorizationUrl !== void 0 && props.copyTextValue !== void 0) modelSurface = (0, import_react.createElement)(ProviderAuthorizationPanel, {
@@ -36471,15 +36484,14 @@ function App(props) {
36471
36484
  onClose: closeModelSurface
36472
36485
  });
36473
36486
  }
36474
- return (0, import_react.createElement)(Box, { flexDirection: "column", height: Math.max(1, terminalRows - 1) },
36475
- welcomeRows > 0 ? (0, import_react.createElement)(Box, { height: welcomeRows, overflowY: "hidden", flexDirection: "column", justifyContent: "flex-end", flexShrink: 0 },
36476
- (0, import_react.createElement)(Box, { flexShrink: 0 }, !emailOpen && terminalRows >= 10 ? (0, import_react.createElement)(Header, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel, animated: animations }) : (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "DSCODE"))) : void 0,
36477
- emailOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(DscodeEmailPanel, {
36487
+ return (0, import_react.createElement)(Box, { flexDirection: "column" }, (0, import_react.createElement)(MemoStaticTranscript, {
36488
+ key: refreshEpoch,
36489
+ items: settledRows
36490
+ }), emailOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(DscodeEmailPanel, {
36478
36491
  gmail, imap, columns: terminalColumns, rows: Math.max(3, terminalRows - 8 - composerGutterRows - composerRows),
36479
36492
  close: () => setEmailOpen(false),
36480
36493
  pick: mail => { props.steer(dscodeEmailPrompt(mail), [], props.sessionKey); setEmailOpen(false); }
36481
36494
  }) : void 0,
36482
- transcriptVisible && settledViewportRows > 0 ? (0, import_react.createElement)(StyledRows, { lines: renderedSettled }) : void 0,
36483
36495
  transcriptVisible ? (0, import_react.createElement)(Box, { flexDirection: "column" }, auditedLiveLines.length === 0 ? void 0 : (0, import_react.createElement)(StyledRows, { lines: auditedLiveLines }), view.streamingReasoning !== "" && auditedReasoningRows > 0 ? showReasoning ? (0, import_react.createElement)(StreamTail, {
36484
36496
  text: view.streamingReasoning,
36485
36497
  prefix: "✻ ",
@@ -36634,7 +36646,7 @@ function App(props) {
36634
36646
  setSubagentOpen(false);
36635
36647
  },
36636
36648
  close: () => setSubagentOpen(false)
36637
- }) : void 0, (0, import_react.createElement)(Box, { flexGrow: 1 }), notice === void 0 ? void 0 : (0, import_react.createElement)(NoticeLine, {
36649
+ }) : void 0, notice === void 0 ? void 0 : (0, import_react.createElement)(NoticeLine, {
36638
36650
  text: notice.text,
36639
36651
  tone: notice.tone,
36640
36652
  columns: terminalColumns
@@ -36656,9 +36668,14 @@ function App(props) {
36656
36668
  quit: props.quit,
36657
36669
  openEmail: () => setEmailOpen(true),
36658
36670
  emailFill, emailConsumed,
36659
- openLogin: () => {
36671
+ openLogin: (provider) => {
36672
+ setProviderOpen(false); setEffortFor(void 0);
36673
+ setProviderAction({ kind: "dscode-key", provider: provider ?? dscodeProviderOfLabel(modelLabel) }); setModelOpen(true);
36674
+ },
36675
+ openProvider: (provider) => {
36676
+ if (provider !== void 0) { dscodeSwitchProvider(provider); return; }
36660
36677
  setProviderOpen(false); setEffortFor(void 0);
36661
- setProviderAction({ kind: "dscode-key" }); setModelOpen(true);
36678
+ setProviderAction({ kind: "dscode-provider" }); setModelOpen(true);
36662
36679
  },
36663
36680
  openModel: () => {
36664
36681
  setDirectory(void 0);
@@ -36674,7 +36691,7 @@ function App(props) {
36674
36691
  },
36675
36692
  openEffort: () => {
36676
36693
  props.loadModels().then((loaded) => {
36677
- const [provider, model] = modelLabel.split("/");
36694
+ const { provider, model } = dscodeSplitModelLabel(modelLabel);
36678
36695
  const row = loaded.rows.find((candidate) => candidate.provider === provider && candidate.model === model) ?? loaded.rows.find((candidate) => candidate.model === model && candidate.reasoning !== void 0) ?? loaded.rows.find((candidate) => candidate.model === model);
36679
36696
  if (row === void 0) {
36680
36697
  notify("current model is not in the catalog", "warning");
@@ -36780,8 +36797,7 @@ function App(props) {
36780
36797
  waveStyle,
36781
36798
  maxRows: composerEditorCap,
36782
36799
  onEditorRows: handleEditorRows,
36783
- onMenuRows: handleMenuRows,
36784
- onTranscriptScroll: scrollTranscript
36800
+ onMenuRows: handleMenuRows
36785
36801
  }), (0, import_react.createElement)(StatusLine, {
36786
36802
  facts: {
36787
36803
  fullSessionId: props.sessionKey,
@@ -37014,7 +37030,7 @@ function createSplitStdin(source) {
37014
37030
  stalePasteTimer.unref?.();
37015
37031
  };
37016
37032
  const onChunk = (chunk) => {
37017
- for (const unit of splitter.push(String(chunk))) if (!dscodeHandleMouseUnit(unit)) stream.write(unit);
37033
+ for (const unit of splitter.push(String(chunk))) stream.write(unit);
37018
37034
  if (splitter.openPaste()) armStalePasteTimer();
37019
37035
  else disarmStalePasteTimer();
37020
37036
  };
@@ -37058,8 +37074,7 @@ const internals = {
37058
37074
  const focusReporting = isVsCodeTerminalEnv();
37059
37075
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(true);
37060
37076
  try {
37061
- if (process.stdout.isTTY === true) process.stdout.write("\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H");
37062
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : "") + (process.stdout.isTTY === true && dscodeMouseEnabled ? DSCODE_MOUSE_ENABLE : ""));
37077
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : ""));
37063
37078
  const tuiStdin = createSplitStdin(process.stdin);
37064
37079
  const instance = render(element, {
37065
37080
  exitOnCtrlC: false,
@@ -37072,7 +37087,7 @@ const internals = {
37072
37087
  },
37073
37088
  unmount() {
37074
37089
  try {
37075
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_DISABLE : ""));
37090
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : ""));
37076
37091
  } finally {
37077
37092
  try {
37078
37093
  instance.unmount();
@@ -37084,7 +37099,6 @@ const internals = {
37084
37099
  }
37085
37100
  };
37086
37101
  } catch (error) {
37087
- if (process.stdout.isTTY === true) process.stdout.write(DSCODE_MOUSE_DISABLE);
37088
37102
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(false);
37089
37103
  throw error;
37090
37104
  }
@@ -39864,6 +39878,7 @@ async function run(ctx, startup, io) {
39864
39878
  interrupt,
39865
39879
  quit,
39866
39880
  loadModels: () => loadModelDirectory(ctx),
39881
+ dscodeEnsureProviderRoute: (provider) => dscodeEnsureProviderRoute(ctx.get("settings"), provider),
39867
39882
  loadModelProviders: () => loadProviderSettings(ctx),
39868
39883
  subscribeModelProviders: (listener) => subscribeProviderSettings(ctx, listener),
39869
39884
  saveModelProviderCredential: (target, key) => saveProviderCredential(ctx, target, key),