utim-cli 2.3.24__tar.gz → 2.3.25__tar.gz

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 (173) hide show
  1. {utim_cli-2.3.24 → utim_cli-2.3.25}/CHANGELOG.md +5 -0
  2. {utim_cli-2.3.24/utim_cli.egg-info → utim_cli-2.3.25}/PKG-INFO +1 -1
  3. {utim_cli-2.3.24 → utim_cli-2.3.25}/pyproject.toml +1 -1
  4. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim/__init__.py +1 -1
  5. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/__init__.py +1 -1
  6. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_version.py +1 -1
  7. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_llm_transport.py +1 -44
  8. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_tool_exec.py +7 -0
  9. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/state.py +1 -0
  10. utim_cli-2.3.25/utim_cli/tui/compression_dialog.py +188 -0
  11. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utim.py +151 -166
  12. {utim_cli-2.3.24 → utim_cli-2.3.25/utim_cli.egg-info}/PKG-INFO +1 -1
  13. utim_cli-2.3.24/utim_cli/tui/compression_dialog.py +0 -230
  14. {utim_cli-2.3.24 → utim_cli-2.3.25}/LICENSE +0 -0
  15. {utim_cli-2.3.24 → utim_cli-2.3.25}/MANIFEST.in +0 -0
  16. {utim_cli-2.3.24 → utim_cli-2.3.25}/README.md +0 -0
  17. {utim_cli-2.3.24 → utim_cli-2.3.25}/setup.cfg +0 -0
  18. {utim_cli-2.3.24 → utim_cli-2.3.25}/setup.py +0 -0
  19. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_chip_test.py +0 -0
  20. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_verify_repro.py +0 -0
  21. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_verify_syntax.py +0 -0
  22. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_wizard_smoke_test.py +0 -0
  23. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/agent.py +0 -0
  24. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/ask_helper.py +0 -0
  25. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/auth.py +0 -0
  26. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/backup.py +0 -0
  27. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/billing.py +0 -0
  28. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/blender_agent.py +0 -0
  29. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/bootstrap.py +0 -0
  30. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/brain.py +0 -0
  31. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/builtin/skills/utim-guide/SKILL.md +0 -0
  32. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/client_utils.py +0 -0
  33. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/config.py +0 -0
  34. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/constants.py +0 -0
  35. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/context_pruner.py +0 -0
  36. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/__init__.py +0 -0
  37. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/engine.py +0 -0
  38. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/markdown_distiller.py +0 -0
  39. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/dependency_manager.py +0 -0
  40. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/doctor.py +0 -0
  41. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/evidence_registry.py +0 -0
  42. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/harbor.py +0 -0
  43. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/knowledge_graph.py +0 -0
  44. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/local_db.py +0 -0
  45. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/logger.py +0 -0
  46. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_clean_wrapper.py +0 -0
  47. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_client.py +0 -0
  48. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_registry.json +0 -0
  49. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/models.txt +0 -0
  50. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator.py +0 -0
  51. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_helpers.py +0 -0
  52. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_turn_history.py +0 -0
  53. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/personalities.py +0 -0
  54. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/platform_installer.py +0 -0
  55. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/__init__.py +0 -0
  56. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/hooks.py +0 -0
  57. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/loader.py +0 -0
  58. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/manager.py +0 -0
  59. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/registry.py +0 -0
  60. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/runtime.py +0 -0
  61. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/schema.py +0 -0
  62. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/tool_loader.py +0 -0
  63. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/reflection.py +0 -0
  64. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/report.py +0 -0
  65. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/scrapy_search.py +0 -0
  66. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/__init__.py +0 -0
  67. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/config.py +0 -0
  68. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/context.py +0 -0
  69. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/events.py +0 -0
  70. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/handlers.py +0 -0
  71. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/session.py +0 -0
  72. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/sidecar.py +0 -0
  73. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/__init__.py +0 -0
  74. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/admin_auth.py +0 -0
  75. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/attribution.py +0 -0
  76. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/audit_log.py +0 -0
  77. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/auth.py +0 -0
  78. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/batch_processor.py +0 -0
  79. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/captcha.py +0 -0
  80. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/cli_auth.py +0 -0
  81. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/concurrency.py +0 -0
  82. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/db.py +0 -0
  83. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  84. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/about.md +0 -0
  85. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/changelog.md +0 -0
  86. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/docs.md +0 -0
  87. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/features.md +0 -0
  88. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/license.md +0 -0
  89. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/pricing.md +0 -0
  90. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/privacy.md +0 -0
  91. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/refund.md +0 -0
  92. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/support.md +0 -0
  93. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/terms.md +0 -0
  94. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/email_utils.py +0 -0
  95. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/exchange_rate.py +0 -0
  96. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/firebase.py +0 -0
  97. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/fix_duplicate_users.py +0 -0
  98. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/history.py +0 -0
  99. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/key_rotator.py +0 -0
  100. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/local_llm.py +0 -0
  101. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/logging_config.py +0 -0
  102. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/micro_batcher.py +0 -0
  103. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/model_agent.py +0 -0
  104. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/models.py +0 -0
  105. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/plan_integrity.py +0 -0
  106. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/pricing_updater.py +0 -0
  107. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/provision_build.py +0 -0
  108. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/rate_limit.py +0 -0
  109. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/rewards_engine.py +0 -0
  110. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/router.py +0 -0
  111. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/__init__.py +0 -0
  112. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/admin_bonus_routes.py +0 -0
  113. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/admin_db_routes.py +0 -0
  114. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/auth_routes.py +0 -0
  115. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/billing_routes.py +0 -0
  116. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/completion_routes.py +0 -0
  117. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/credit_routes.py +0 -0
  118. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/feedback_routes.py +0 -0
  119. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/marketplace_routes.py +0 -0
  120. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/quota_routes.py +0 -0
  121. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/quota_share_routes.py +0 -0
  122. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/referral_routes.py +0 -0
  123. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/rewards_routes.py +0 -0
  124. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/security_routes.py +0 -0
  125. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/session_routes.py +0 -0
  126. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/share_routes.py +0 -0
  127. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/webhook_routes.py +0 -0
  128. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/security_alerts.py +0 -0
  129. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/server.py +0 -0
  130. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/storage_nodes.py +0 -0
  131. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/utils.py +0 -0
  132. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/share.py +0 -0
  133. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/share_tui.py +0 -0
  134. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/situational_scoring.py +0 -0
  135. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/subagent_manager.py +0 -0
  136. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/task_dispatcher.py +0 -0
  137. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/task_logger.py +0 -0
  138. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tools.py +0 -0
  139. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tools_grep.py +0 -0
  140. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/__init__.py +0 -0
  141. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/feedback_dialog.py +0 -0
  142. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/history_dialog.py +0 -0
  143. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_app_state.py +0 -0
  144. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_dialog.py +0 -0
  145. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  146. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/mcp_dialog.py +0 -0
  147. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/miniagents_dialog.py +0 -0
  148. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/model_dialog.py +0 -0
  149. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/personality_dialog.py +0 -0
  150. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/plugins_dialog.py +0 -0
  151. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/publish_dialog.py +0 -0
  152. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_dialog.py +0 -0
  153. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_redeem_dialog.py +0 -0
  154. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_share_dialog.py +0 -0
  155. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/resume_dialog.py +0 -0
  156. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/rewards_tui.py +0 -0
  157. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/skills_dialog.py +0 -0
  158. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/subagents_dialog.py +0 -0
  159. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/tasks_dialog.py +0 -0
  160. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/thinking_display.py +0 -0
  161. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/tools_dialog.py +0 -0
  162. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/update_dialog.py +0 -0
  163. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/update_checker.py +0 -0
  164. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utilities.py +0 -0
  165. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utimmodel.txt +0 -0
  166. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/vector_memory.py +0 -0
  167. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/wheel.py +0 -0
  168. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/workspace.py +0 -0
  169. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/SOURCES.txt +0 -0
  170. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/dependency_links.txt +0 -0
  171. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/entry_points.txt +0 -0
  172. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/requires.txt +0 -0
  173. {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.3.25] - 2026-09-07
4
+
5
+ ### 🛠️ Bug Fixes
6
+ - Fixed usage dialog glitches
7
+
3
8
  ## [2.3.24] - 2026-09-07
4
9
 
5
10
  ### ⚡ Performance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.3.24
3
+ Version: 2.3.25
4
4
  Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
5
5
  License: Emend AI Proprietary EULA
6
6
  Project-URL: Homepage, https://utim.dev
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "2.3.24"
7
+ version = "2.3.25"
8
8
 
9
9
  description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
10
10
  readme = "README.md"
@@ -14,7 +14,7 @@ Quickstart:
14
14
  try:
15
15
  from utim_cli._version import VERSION as __version__
16
16
  except ImportError:
17
- __version__ = "2.3.24"
17
+ __version__ = "2.3.25"
18
18
 
19
19
  # Developer-friendly core classes (similar to `import genai` / `import openai`)
20
20
  from utim_cli.sdk.session import UtimSession as Session
@@ -5,7 +5,7 @@ try:
5
5
  from utim_cli._version import VERSION as __version__
6
6
  except ImportError:
7
7
  # Fallback during first-install / editable-install edge cases
8
- __version__ = "2.3.24"
8
+ __version__ = "2.3.25"
9
9
 
10
10
 
11
11
  import builtins, os, pathlib, urllib.parse, re
@@ -11,6 +11,6 @@
11
11
  # from here so every surface stays in sync automatically.
12
12
  # ─────────────────────────────────────────────────────────────────────────────
13
13
 
14
- VERSION = "2.3.24"
14
+ VERSION = "2.3.25"
15
15
 
16
16
 
@@ -268,11 +268,6 @@ class LLMTransportMixin:
268
268
  if not race_candidates:
269
269
  return None
270
270
 
271
- if not silent:
272
- self.console.print(
273
- f"\n[dim yellow]↪ Primary model unavailable — racing {len(race_candidates)} fallback models for lowest latency response…[/dim yellow]\n"
274
- )
275
-
276
271
  winner_lock = threading.Lock()
277
272
  winner_model = [None]
278
273
  winner_result = [None]
@@ -1135,10 +1130,6 @@ class LLMTransportMixin:
1135
1130
  _exhausted_key = _api_key
1136
1131
 
1137
1132
  if _exhausted_key:
1138
- remaining = config.get_exhaustion_countdown(_exhausted_key)
1139
- remaining_str = f"{remaining // 3600}h {remaining % 3600 // 60}m" if remaining else "24h"
1140
- if not silent:
1141
- self.console.print(f"[dim]Skipping exhausted API key {_exhausted_key[:12]}... (cooldown: {remaining_str})[/dim]")
1142
1133
  raise _ServerUnavailableError(f"API key exhausted: {_exhausted_key[:12]}...")
1143
1134
 
1144
1135
  # Build auth headers or query params depending on provider
@@ -1610,17 +1601,8 @@ class LLMTransportMixin:
1610
1601
  _paid_slug = current_model.rsplit(":free", 1)[0]
1611
1602
  _remaining = models_to_try[model_idx + 1:]
1612
1603
  if _paid_slug not in _remaining:
1613
- if not silent:
1614
- self.console.print(
1615
- f"\n[dim yellow]↪ Free model rate-limited — trying paid version '{_paid_slug}'…[/dim yellow]"
1616
- )
1617
1604
  # Insert paid slug as the very next model, then break
1618
1605
  models_to_try[model_idx + 1:model_idx + 1] = [_paid_slug]
1619
- else:
1620
- if not silent:
1621
- self.console.print(
1622
- f"\n[dim yellow]↪ Free model rate-limited — paid version already in queue, skipping…[/dim yellow]"
1623
- )
1624
1606
  break # inner attempt loop → outer loop picks up injected paid slug
1625
1607
  # Non-free model: nothing to fall back to — show message and return
1626
1608
  rate_limit_display = (
@@ -1641,21 +1623,9 @@ class LLMTransportMixin:
1641
1623
  "tool_calls": None,
1642
1624
  }, False
1643
1625
  elif _is_provider_upstream:
1644
- if not silent:
1645
- self.console.print(
1646
- f"\n[dim yellow]↪ {_friendly_name}'s provider is having trouble — "
1647
- f"switching to the next available model…[/dim yellow]"
1648
- )
1649
1626
  raise RuntimeError(f"__PROVIDER_ERROR__:{current_model}")
1650
1627
  else:
1651
- # Unknown error: show a concise message, not the raw blob
1652
- _short = err_msg.split("{'error'")[0].strip() or err_msg[:120]
1653
- if not silent:
1654
- self.console.print(
1655
- f"\n[bold red]Model error on {_friendly_name}: {_short}[/bold red]\n"
1656
- f"[dim]Trying the next available model…[/dim]"
1657
- )
1658
- raise RuntimeError(f"Server completion error ({current_model}): {err_msg}")
1628
+ raise RuntimeError(f"Server completion error ({current_model}): {err_msg}")
1659
1629
  if "finish_reason" in chunk and chunk["finish_reason"]:
1660
1630
  _stream_finish_reason = chunk["finish_reason"]
1661
1631
  elif not _stream_finish_reason:
@@ -2188,24 +2158,11 @@ class LLMTransportMixin:
2188
2158
  _paid_slug = current_model.rsplit(":free", 1)[0]
2189
2159
  _remaining = models_to_try[model_idx + 1:]
2190
2160
  if _paid_slug not in _remaining:
2191
- if not silent:
2192
- self.console.print(
2193
- f"\n[dim yellow]↪ Free model rate-limited — trying paid version '{_paid_slug}'…[/dim yellow]"
2194
- )
2195
2161
  # Insert paid slug as the very next model, then break
2196
2162
  models_to_try[model_idx + 1:model_idx + 1] = [_paid_slug]
2197
- else:
2198
- if not silent:
2199
- self.console.print(
2200
- f"\n[dim yellow]↪ Free model rate-limited — paid version already in queue, skipping…[/dim yellow]"
2201
- )
2202
2163
  break # inner attempt loop → outer loop picks up injected paid slug
2203
2164
  # If primary model is rate-limited and fallback models exist, race them
2204
2165
  if current_is_primary and fallback_list:
2205
- if not silent:
2206
- self.console.print(
2207
- f"\n[dim yellow]⏳ Model '{current_model}' is temporarily rate-limited by the provider — racing fallback models…[/dim yellow]\n"
2208
- )
2209
2166
  raise _ServerUnavailableError(f"Rate-limited on model '{current_model}'")
2210
2167
 
2211
2168
  # Non-free model with no fallback: show message and return
@@ -659,6 +659,13 @@ class ToolExecutionMixin:
659
659
  self.console.print(bl)
660
660
  rendered_text = capture.get()
661
661
  if print_to_console and getattr(self, "_subagent_depth", 0) == 0:
662
+ try:
663
+ from utim_cli.state import STATE
664
+ if STATE.get("dialog_active"):
665
+ self.console.print(rendered_text, end="", markup=False, highlight=False)
666
+ return rendered_text
667
+ except Exception:
668
+ pass
662
669
  if getattr(self.console, "file", None) is not sys.stdout and getattr(self.console, "file", None) is not sys.__stdout__:
663
670
  # IMPORTANT: rendered_text is a *plain string* that was already
664
671
  # produced by Rich (via capture.get()). It can legitimately
@@ -13,4 +13,5 @@ STATE = {
13
13
  "tools_expanded": False, # Toggle global tool results inline expand/collapse
14
14
  "thinking_topic": "", # Dynamic topic for the spinner
15
15
  "is_verified": False, # True if user has entered the secret code in this session
16
+ "dialog_active": False, # True when any modal/dialog is full screen or running in terminal
16
17
  }
@@ -0,0 +1,188 @@
1
+ """
2
+ /compression -- Interactive context compression level picker.
3
+
4
+ Five levels from Very Low (almost verbatim, costs more) to Very High
5
+ (aggressive summarisation, saves tokens but risks forgetting old context).
6
+ The chosen level is persisted in config and immediately applied to
7
+ context_pruner.build_compacted_history_message on the next turn.
8
+ """
9
+
10
+ import re
11
+ from typing import Tuple
12
+
13
+ # -- Level definitions ---------------------------------------------------------
14
+ # Each entry: (id, label, oldest_keep_ratio, newest_keep_ratio, merge_count, warning)
15
+ COMPRESSION_LEVELS = [
16
+ (
17
+ "very_low",
18
+ "Very Low",
19
+ 0.50, 0.95,
20
+ 0,
21
+ "Can cost more because most of the context is kept; model has very low risk of forgetting.",
22
+ "yellow",
23
+ ),
24
+ (
25
+ "low",
26
+ "Low",
27
+ 0.25, 0.85,
28
+ 2,
29
+ "Mild compression on older turns; keeps context intact with minimal forgetting risk.",
30
+ "dim",
31
+ ),
32
+ (
33
+ "medium",
34
+ "Medium [default]",
35
+ 0.07, 0.70,
36
+ 5,
37
+ "Balanced progressive compression (oldest ~7% to newest ~70% kept); merges turns 1-5.",
38
+ "dim",
39
+ ),
40
+ (
41
+ "high",
42
+ "High",
43
+ 0.03, 0.50,
44
+ 8,
45
+ "Aggressive compression; saves more tokens but might lose details from earlier turns.",
46
+ "yellow",
47
+ ),
48
+ (
49
+ "very_high",
50
+ "Very High",
51
+ 0.01, 0.30,
52
+ 12,
53
+ "Maximum compression; saves tokens, but might lose bit of context from previous turns when session is running long.",
54
+ "red",
55
+ ),
56
+ ]
57
+
58
+ LEVEL_IDS = [l[0] for l in COMPRESSION_LEVELS]
59
+ DEFAULT_LEVEL = "medium"
60
+
61
+
62
+ def get_compression_level() -> str:
63
+ """Return the currently persisted compression level id (defaults to 'medium')."""
64
+ try:
65
+ from utim_cli.config import config
66
+ val = config.get("compression_level") or DEFAULT_LEVEL
67
+ return val if val in LEVEL_IDS else DEFAULT_LEVEL
68
+ except Exception:
69
+ return DEFAULT_LEVEL
70
+
71
+
72
+ def set_compression_level(level_id: str) -> None:
73
+ """Persist the chosen compression level and notify context_pruner."""
74
+ try:
75
+ from utim_cli.config import config
76
+ config.set("compression_level", level_id)
77
+ except Exception:
78
+ pass
79
+ try:
80
+ from utim_cli.context_pruner import apply_compression_level
81
+ apply_compression_level(level_id)
82
+ except Exception:
83
+ pass
84
+
85
+
86
+ def get_level_params(level_id: str) -> Tuple[float, float, int]:
87
+ """Return (oldest_ratio, newest_ratio, merge_count) for a level id."""
88
+ for lid, _, oldest, newest, merge, _, _ in COMPRESSION_LEVELS:
89
+ if lid == level_id:
90
+ return oldest, newest, merge
91
+ return 0.07, 0.70, 5 # medium fallback
92
+
93
+
94
+ # -- TUI dialog ----------------------------------------------------------------
95
+
96
+ def _dialog_compression(orchestrator=None) -> None:
97
+ """Open the interactive compression level picker using UTIM's full-screen list dialog."""
98
+ from utim_cli.utim import console, _run_list_dialog
99
+
100
+ current_level = get_compression_level()
101
+
102
+ # Build options list from COMPRESSION_LEVELS
103
+ options = []
104
+ for lid, label, oldest, newest, merge, warning, warn_color in COMPRESSION_LEVELS:
105
+ options.append({
106
+ "id": lid,
107
+ "label": label,
108
+ "oldest": oldest,
109
+ "newest": newest,
110
+ "merge": merge,
111
+ "warning": warning,
112
+ "warn_color": warn_color,
113
+ })
114
+ options.append({
115
+ "id": "cancel",
116
+ "label": "↩ Cancel",
117
+ "warning": "Exit without changing compression level",
118
+ })
119
+
120
+ def _render_row(i: int, row: dict, sel: bool):
121
+ bg = "bg:#313244 fg:#ffffff bold" if sel else ""
122
+ if row.get("id") == "cancel":
123
+ return [
124
+ (bg or "class:dim", " ➔ " if sel else " "),
125
+ (bg or "bold #f38ba8", f"{row['label']}\n"),
126
+ (bg or "class:dim", f" {row['warning']}\n"),
127
+ ]
128
+
129
+ is_current = (row["id"] == current_level)
130
+ badge = "● ACTIVE " if is_current else "○ "
131
+ badge_style = bg or ("bold #a6e3a1" if is_current else "class:dim")
132
+
133
+ oldest_pct = int(row["oldest"] * 100)
134
+ newest_pct = int(row["newest"] * 100)
135
+ ratio_str = f"oldest {oldest_pct}% → newest {newest_pct}% kept"
136
+
137
+ warn_color = row.get("warn_color", "dim")
138
+ if warn_color == "red":
139
+ warn_style = bg or "bold #ff5555"
140
+ elif warn_color == "yellow":
141
+ warn_style = bg or "#ffb86c"
142
+ else:
143
+ warn_style = bg or "class:dim"
144
+ icon = "⚠ " if warn_color in ("red", "yellow") else "ℹ "
145
+
146
+ if row.get("merge", 0) > 0:
147
+ merge_txt = f"Merges oldest {row['merge']} turns into one block"
148
+ else:
149
+ merge_txt = "No turn merging (all turns kept individually)"
150
+
151
+ fragments = [
152
+ (badge_style, f" {badge}"),
153
+ (bg or "bold #42bcf5", f"{row['label']:<20}"),
154
+ (bg or "class:dim", f" [{ratio_str}]\n"),
155
+ (bg or "#a6adc8", f" • {merge_txt}\n"),
156
+ (warn_style, f" {icon}{row['warning']}\n"),
157
+ ]
158
+ return fragments
159
+
160
+ action, idx = _run_list_dialog(
161
+ options,
162
+ _render_row,
163
+ title="❯ Context Compression Level Picker",
164
+ legend=(
165
+ "↑↓ Navigate • Enter Apply • Esc / q Cancel • "
166
+ "Currently active level marked [bold #a6e3a1]● ACTIVE[/bold #a6e3a1]"
167
+ ),
168
+ )
169
+
170
+ if action != "select" or idx is None:
171
+ try:
172
+ console.print("\n [dim]✗ Cancelled. No changes made.[/dim]\n")
173
+ except Exception:
174
+ pass
175
+ return
176
+
177
+ chosen = options[idx]
178
+ if chosen["id"] == "cancel":
179
+ console.print("\n [dim]✗ Cancelled. No changes made.[/dim]\n")
180
+ return
181
+
182
+ set_compression_level(chosen["id"])
183
+
184
+ console.print(
185
+ f"\n [bold #42bcf5]✓ Compression level set to[/bold #42bcf5] "
186
+ f"[bold white]{chosen['label']}[/bold white] "
187
+ f"[dim](takes effect on next context window build)[/dim]\n"
188
+ )