utim-cli 2.3.12__tar.gz → 2.3.14__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 (160) hide show
  1. {utim_cli-2.3.12 → utim_cli-2.3.14}/CHANGELOG.md +12 -0
  2. {utim_cli-2.3.12/utim_cli.egg-info → utim_cli-2.3.14}/PKG-INFO +1 -1
  3. {utim_cli-2.3.12 → utim_cli-2.3.14}/pyproject.toml +1 -1
  4. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim/__init__.py +1 -1
  5. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/__init__.py +1 -1
  6. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/_version.py +1 -1
  7. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/agent.py +1 -1
  8. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/ask_helper.py +1 -1
  9. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/auth.py +2 -2
  10. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/bootstrap.py +6 -5
  11. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/brain.py +36 -2
  12. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/client_utils.py +66 -7
  13. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/config.py +102 -4
  14. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/context_pruner.py +1 -1
  15. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/doctor.py +1 -1
  16. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/orchestrator.py +50 -41
  17. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/orchestrator_helpers.py +4 -4
  18. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/orchestrator_llm_transport.py +42 -32
  19. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/orchestrator_tool_exec.py +2 -0
  20. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/reflection.py +33 -4
  21. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/auth.py +54 -5
  22. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/db.py +7 -0
  23. utim_cli-2.3.14/utim_cli/server/key_rotator.py +710 -0
  24. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/router.py +11 -1
  25. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/auth_routes.py +21 -11
  26. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/completion_routes.py +136 -98
  27. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/credit_routes.py +26 -9
  28. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/marketplace_routes.py +17 -0
  29. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/quota_routes.py +36 -0
  30. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tools.py +272 -140
  31. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/marketplace_dialog.py +16 -13
  32. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/miniagents_dialog.py +72 -48
  33. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/model_dialog.py +17 -7
  34. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/skills_dialog.py +11 -9
  35. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/subagents_dialog.py +70 -47
  36. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/utim.py +174 -74
  37. {utim_cli-2.3.12 → utim_cli-2.3.14/utim_cli.egg-info}/PKG-INFO +1 -1
  38. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli.egg-info/SOURCES.txt +1 -0
  39. {utim_cli-2.3.12 → utim_cli-2.3.14}/LICENSE +0 -0
  40. {utim_cli-2.3.12 → utim_cli-2.3.14}/MANIFEST.in +0 -0
  41. {utim_cli-2.3.12 → utim_cli-2.3.14}/README.md +0 -0
  42. {utim_cli-2.3.12 → utim_cli-2.3.14}/setup.cfg +0 -0
  43. {utim_cli-2.3.12 → utim_cli-2.3.14}/setup.py +0 -0
  44. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/_verify_repro.py +0 -0
  45. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/_verify_syntax.py +0 -0
  46. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/backup.py +0 -0
  47. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/billing.py +0 -0
  48. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/blender_agent.py +0 -0
  49. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/constants.py +0 -0
  50. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/crawler/__init__.py +0 -0
  51. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/crawler/engine.py +0 -0
  52. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/crawler/markdown_distiller.py +0 -0
  53. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/dependency_manager.py +0 -0
  54. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/evidence_registry.py +0 -0
  55. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/harbor.py +0 -0
  56. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/knowledge_graph.py +0 -0
  57. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/local_db.py +0 -0
  58. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/logger.py +0 -0
  59. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/mcp_clean_wrapper.py +0 -0
  60. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/mcp_client.py +0 -0
  61. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/mcp_registry.json +0 -0
  62. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/models.txt +0 -0
  63. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/orchestrator_turn_history.py +0 -0
  64. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/personalities.py +0 -0
  65. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/platform_installer.py +0 -0
  66. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/__init__.py +0 -0
  67. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/hooks.py +0 -0
  68. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/manager.py +0 -0
  69. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/registry.py +0 -0
  70. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/schema.py +0 -0
  71. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/plugins/tool_loader.py +0 -0
  72. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/report.py +0 -0
  73. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/scrapy_search.py +0 -0
  74. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/__init__.py +0 -0
  75. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/config.py +0 -0
  76. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/context.py +0 -0
  77. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/events.py +0 -0
  78. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/handlers.py +0 -0
  79. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/session.py +0 -0
  80. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/sdk/sidecar.py +0 -0
  81. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/__init__.py +0 -0
  82. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/admin_auth.py +0 -0
  83. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/attribution.py +0 -0
  84. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/audit_log.py +0 -0
  85. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/batch_processor.py +0 -0
  86. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/captcha.py +0 -0
  87. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/cli_auth.py +0 -0
  88. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/concurrency.py +0 -0
  89. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  90. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/about.md +0 -0
  91. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/changelog.md +0 -0
  92. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/docs.md +0 -0
  93. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/features.md +0 -0
  94. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/license.md +0 -0
  95. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/pricing.md +0 -0
  96. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/privacy.md +0 -0
  97. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/refund.md +0 -0
  98. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/support.md +0 -0
  99. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/docs_md/terms.md +0 -0
  100. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/email_utils.py +0 -0
  101. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/exchange_rate.py +0 -0
  102. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/firebase.py +0 -0
  103. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/fix_duplicate_users.py +0 -0
  104. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/history.py +0 -0
  105. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/local_llm.py +0 -0
  106. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/logging_config.py +0 -0
  107. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/micro_batcher.py +0 -0
  108. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/model_agent.py +0 -0
  109. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/models.py +0 -0
  110. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/pricing_updater.py +0 -0
  111. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/provision_build.py +0 -0
  112. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/rate_limit.py +0 -0
  113. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/rewards_engine.py +0 -0
  114. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/__init__.py +0 -0
  115. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/admin_db_routes.py +0 -0
  116. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/billing_routes.py +0 -0
  117. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/feedback_routes.py +0 -0
  118. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/quota_share_routes.py +0 -0
  119. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/referral_routes.py +0 -0
  120. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/rewards_routes.py +0 -0
  121. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/security_routes.py +0 -0
  122. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/session_routes.py +0 -0
  123. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/routes/share_routes.py +0 -0
  124. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/server.py +0 -0
  125. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/server/storage_nodes.py +0 -0
  126. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/share.py +0 -0
  127. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/share_tui.py +0 -0
  128. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/situational_scoring.py +0 -0
  129. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/state.py +0 -0
  130. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/subagent_manager.py +0 -0
  131. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/task_dispatcher.py +0 -0
  132. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/task_logger.py +0 -0
  133. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tools_grep.py +0 -0
  134. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/__init__.py +0 -0
  135. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/feedback_dialog.py +0 -0
  136. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/history_dialog.py +0 -0
  137. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/marketplace_app_state.py +0 -0
  138. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  139. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/mcp_dialog.py +0 -0
  140. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/personality_dialog.py +0 -0
  141. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/plugins_dialog.py +0 -0
  142. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/publish_dialog.py +0 -0
  143. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/quota_dialog.py +0 -0
  144. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/quota_redeem_dialog.py +0 -0
  145. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/quota_share_dialog.py +0 -0
  146. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/resume_dialog.py +0 -0
  147. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/rewards_tui.py +0 -0
  148. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/tasks_dialog.py +0 -0
  149. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/thinking_display.py +0 -0
  150. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/tools_dialog.py +0 -0
  151. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/tui/update_dialog.py +0 -0
  152. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/utilities.py +0 -0
  153. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/utimmodel.txt +0 -0
  154. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/vector_memory.py +0 -0
  155. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/wheel.py +0 -0
  156. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli/workspace.py +0 -0
  157. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli.egg-info/dependency_links.txt +0 -0
  158. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli.egg-info/entry_points.txt +0 -0
  159. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli.egg-info/requires.txt +0 -0
  160. {utim_cli-2.3.12 → utim_cli-2.3.14}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.3.14] - 2026-08-28
4
+
5
+ ### ⚡ Request Queue & Scaled Free Capacity
6
+ - Added request queue visual, Increased global free user request cap from 20RPM - 60RPM
7
+ - Increase per user Rpm cap from 10-30RPM
8
+ - Added a new fun feature "/personality"
9
+
10
+ ## [2.3.13] - 2026-08-28
11
+
12
+ ### 🐞 Minor Bug Fixes
13
+ - Minor Bug Fixes
14
+
3
15
  ## [2.3.12] - 2026-08-27
4
16
 
5
17
  ### ⚡ Hedged TTFT Fallback Racing & Smart Nudge Improvements
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.3.12
3
+ Version: 2.3.14
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.12"
7
+ version = "2.3.14"
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.12"
17
+ __version__ = "2.3.14"
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.12"
8
+ __version__ = "2.3.14"
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.12"
14
+ VERSION = "2.3.14"
15
15
 
16
16
 
@@ -288,7 +288,7 @@ class ReActAgent:
288
288
 
289
289
  # Provider-safety pass: drop null/empty `tool_calls` keys from
290
290
  # history (strict providers 400 on `messages.N.tool_calls: null`).
291
- kwargs["messages"] = sanitize_messages_for_api(kwargs.get("messages") or [])
291
+ kwargs["messages"] = sanitize_messages_for_api(kwargs.get("messages") or [], model_id=getattr(self, "model", None))
292
292
 
293
293
  # Call the LLM with streaming (retry once on transport errors so a
294
294
  # mid-stream failure doesn't silently kill the whole run)
@@ -389,7 +389,7 @@ def execute_ask_helper(
389
389
  },
390
390
  stream=False,
391
391
  timeout=(15, 300),
392
- is_reflection=True,
392
+ is_reflection=False,
393
393
  )
394
394
  if resp.status_code == 429:
395
395
  continue
@@ -321,7 +321,7 @@ class _AuthCallbackHandler(http.server.BaseHTTPRequestHandler):
321
321
 
322
322
  # ── Public login() function ───────────────────────────────────────────────────
323
323
 
324
- def login(restart: bool = True) -> None:
324
+ def login(restart: bool = False) -> None:
325
325
  """
326
326
  Sign in via the Device Authorization Flow.
327
327
 
@@ -504,8 +504,8 @@ def login(restart: bool = True) -> None:
504
504
  raw_print(f" Signed in as {BLUE}{body.get('email', '')}{RESET}")
505
505
  if "credits" in body:
506
506
  raw_print(f" Credits: {body.get('credits', 0):,.0f} UTIM")
507
- raw_print(f" {DIM}Restarting UTIM CLI…{RESET}")
508
507
  if restart:
508
+ raw_print(f" {DIM}Restarting UTIM CLI…{RESET}")
509
509
  restart_process()
510
510
  # If restart wasn't called, fall through and let the main loop exit.
511
511
 
@@ -150,7 +150,7 @@ def scan_available_skills(force_reload: bool = False):
150
150
 
151
151
  import os, re
152
152
  from pathlib import Path
153
- from utim_cli.config import config as _cfg
153
+ from utim_cli.config import config as _cfg, get_all_utim_dirs
154
154
 
155
155
  disabled: list = _cfg.get("disabled_skills") or []
156
156
  if not isinstance(disabled, list):
@@ -163,11 +163,12 @@ def scan_available_skills(force_reload: bool = False):
163
163
  Path('.agents'),
164
164
  Path('.utim/skills'),
165
165
  Path('.utim/agentskills'),
166
- UTIM_DIR / 'skills',
167
- UTIM_DIR / 'agentskills',
168
- Path.home() / '.utim' / 'skills',
169
- Path.home() / '.utim' / 'agentskills',
166
+ Path('skills'),
170
167
  ]
168
+ for d in get_all_utim_dirs():
169
+ paths_to_check.append(d / 'skills')
170
+ paths_to_check.append(d / 'agentskills')
171
+ paths_to_check.append(d)
171
172
 
172
173
  for base_dir in paths_to_check:
173
174
  if not base_dir.exists():
@@ -353,6 +353,13 @@ def store_brain_memory(
353
353
  if not content or not content.strip():
354
354
  return ""
355
355
 
356
+ try:
357
+ from utim_cli.tools import is_user_paid
358
+ if not is_user_paid():
359
+ return ""
360
+ except Exception:
361
+ pass
362
+
356
363
  pid = project_id or _project_id()
357
364
  now = datetime.now().isoformat()
358
365
  clean_title = title or content[:60].replace("\n", " ").strip()
@@ -444,6 +451,13 @@ def retrieve_brain_memories(
444
451
  if not query:
445
452
  return []
446
453
 
454
+ try:
455
+ from utim_cli.tools import is_user_paid
456
+ if not is_user_paid():
457
+ return []
458
+ except Exception:
459
+ pass
460
+
447
461
  pid = project_id or _project_id()
448
462
 
449
463
  # ── Stage 1: Forward pass ─────────────────────────────────────────────
@@ -696,6 +710,13 @@ def index_project_architecture(workspace: str = "."):
696
710
  If yes, store an updated architecture memory in the brain.
697
711
  Maintains exactly one deterministic architecture memory per project.
698
712
  """
713
+ try:
714
+ from utim_cli.tools import is_user_paid
715
+ if not is_user_paid():
716
+ return
717
+ except Exception:
718
+ pass
719
+
699
720
  pid = _project_id(workspace)
700
721
  snap = _snapshot_project_architecture(workspace)
701
722
  snap_hash = hashlib.sha1(snap.encode()).hexdigest()
@@ -776,13 +797,19 @@ _watcher_lock = threading.Lock()
776
797
 
777
798
  def start_brain_watcher():
778
799
  """
779
- Start the background brain watcher daemon thread.
780
- Safe to call multiple times — only one thread is ever started.
800
+ Start the background daemon watcher if not already running.
801
+ Safe to call multiple times.
781
802
  """
782
803
  global _watcher_started
783
804
  with _watcher_lock:
784
805
  if _watcher_started:
785
806
  return
807
+ try:
808
+ from utim_cli.tools import is_user_paid
809
+ if not is_user_paid():
810
+ return
811
+ except Exception:
812
+ pass
786
813
  _watcher_started = True
787
814
 
788
815
  # One-time cleanup of legacy duplicate records on startup
@@ -946,6 +973,13 @@ def get_brain_context_prompt(user_prompt: str = "") -> str:
946
973
  relevant brain project folder matching the project data/query, and injects
947
974
  direct clickable paths into the system prompt.
948
975
  """
976
+ try:
977
+ from utim_cli.tools import is_user_paid
978
+ if not is_user_paid():
979
+ return ""
980
+ except Exception:
981
+ pass
982
+
949
983
  try:
950
984
  pid, proj_dir = find_most_relevant_brain_project(user_prompt)
951
985
  brain_root = _brain_root()
@@ -572,7 +572,7 @@ def proxy_openrouter_request(json_data: dict, stream: bool = False, timeout=None
572
572
  return requests.post("https://openrouter.ai/api/v1/chat/completions", json=json_data, headers=headers, stream=stream or json_data.get("stream"), timeout=timeout, verify=config.verify_ssl)
573
573
 
574
574
 
575
- def sanitize_messages_for_api(messages):
575
+ def sanitize_messages_for_api(messages, model_id: Optional[str] = None):
576
576
  """Normalize conversation history into a provider-safe shape before sending.
577
577
 
578
578
  Fixes the Liquid/LFM-2.5 (and other strict OpenAI-compatible providers)
@@ -580,26 +580,85 @@ def sanitize_messages_for_api(messages):
580
580
  `messages.N.tool_calls`. Internal code paths persist assistant messages
581
581
  with `"tool_calls": None` (abort/quota returns, XML-fallback turns,
582
582
  session reloads). The OpenAI spec says the key must be omitted or an
583
- array — never null. This function:
583
+ array — never null.
584
584
 
585
- 1. Drops `tool_calls` when it is None or an empty list.
586
- 2. Ensures every kept tool_call has id/type/function with string fields
587
- (dict arguments are JSON-serialized to a string).
588
- 3. Ensures tool-role messages carry string content (None -> "").
589
- 4. Strips internal metadata keys some providers reject.
585
+ Also handles shifting between vision and non-vision models:
586
+ If target `model_id` is NOT vision-capable, converts any multimodal `image_url`
587
+ content blocks into text analysis blocks using precomputed background
588
+ analyses (from `analyze_image` / `_IMAGE_ANALYSIS_CACHE`) so non-vision models
589
+ do not trigger "No endpoints found which support images" from OpenRouter/providers.
590
590
 
591
591
  Returns a new list; the input list and its dicts are not mutated.
592
592
  """
593
593
  _INTERNAL_KEYS = (
594
594
  "reasoning_duration", "was_cut_off", "was_truncated_by_limit",
595
595
  "finish_reason", "completion_tokens", "cache_control", "aborted",
596
+ "_image_meta", "_image_path", "_image_analysis", "_image_entry", "_image_paths",
596
597
  )
598
+
599
+ # Check if target model supports vision
600
+ is_vision = True
601
+ if model_id:
602
+ try:
603
+ from utim_cli.tools import is_model_vision_capable
604
+ is_vision = is_model_vision_capable(model_id)
605
+ except Exception:
606
+ is_vision = True
607
+ else:
608
+ try:
609
+ from utim_cli.tools import is_model_vision_capable
610
+ is_vision = is_model_vision_capable()
611
+ except Exception:
612
+ is_vision = True
613
+
597
614
  clean = []
598
615
  for msg in messages:
599
616
  if not isinstance(msg, dict):
600
617
  continue # never send non-dict junk to the API
601
618
  m = dict(msg)
602
619
  role = m.get("role")
620
+ content = m.get("content")
621
+
622
+ # Multimodal image handling: if the model is NOT vision capable,
623
+ # convert image_url blocks to text analysis!
624
+ if not is_vision and isinstance(content, list):
625
+ text_parts = []
626
+ for part in content:
627
+ if not isinstance(part, dict):
628
+ text_parts.append(str(part))
629
+ continue
630
+ p_type = part.get("type")
631
+ if p_type == "text":
632
+ t = part.get("text", "")
633
+ if t:
634
+ text_parts.append(t)
635
+ elif p_type == "image_url":
636
+ # Retrieve visual analysis
637
+ analysis = m.get("_image_analysis")
638
+ img_path = m.get("_image_path")
639
+ img_meta = m.get("_image_meta")
640
+ img_entry = m.get("_image_entry")
641
+ if img_entry and isinstance(img_entry, dict) and img_entry.get("analysis"):
642
+ analysis = img_entry.get("analysis")
643
+ if not analysis and img_path:
644
+ try:
645
+ from utim_cli.tools import _IMAGE_ANALYSIS_CACHE
646
+ analysis = _IMAGE_ANALYSIS_CACHE.get(img_path)
647
+ except Exception:
648
+ pass
649
+ if not analysis and img_path and os.path.exists(img_path):
650
+ try:
651
+ from utim_cli.tools import analyze_image, _IMAGE_ANALYSIS_CACHE
652
+ analysis = analyze_image(img_path, "Identify and describe this image in detail.")
653
+ if analysis and not analysis.startswith("Error"):
654
+ _IMAGE_ANALYSIS_CACHE[img_path] = analysis
655
+ except Exception as _ae:
656
+ analysis = f"Image {os.path.basename(img_path)} (Visual analysis unavailable: {_ae})"
657
+ if not analysis:
658
+ analysis = img_meta or "[Image visual content]"
659
+
660
+ text_parts.append(f"[Visual Image Analysis]:\n{analysis}")
661
+ m["content"] = "\n\n".join(p for p in text_parts if p.strip())
603
662
 
604
663
  if role == "assistant":
605
664
  tc = m.get("tool_calls")
@@ -6,7 +6,7 @@ import shutil
6
6
  import time
7
7
  import hashlib
8
8
  import hmac
9
- from typing import Any, Dict, Optional
9
+ from typing import Any, Dict, Optional, List
10
10
 
11
11
  logger = logging.getLogger("utim.config")
12
12
 
@@ -199,15 +199,113 @@ def _apply_ssl_session_patch(disable: bool) -> None:
199
199
  except Exception:
200
200
  pass
201
201
 
202
+ def get_all_utim_dirs() -> List[pathlib.Path]:
203
+ """Return all valid candidate .utim directories across workspace, environment variables,
204
+ and user profiles (Windows USERPROFILE, APPDATA, HOME, Path.home(), etc.).
205
+ Ordered by priority.
206
+ """
207
+ candidates: List[pathlib.Path] = []
208
+ seen = set()
209
+
210
+ def _add(p):
211
+ if not p:
212
+ return
213
+ try:
214
+ resolved = pathlib.Path(p).expanduser().resolve()
215
+ r_str = str(resolved).lower()
216
+ if r_str not in seen:
217
+ seen.add(r_str)
218
+ candidates.append(resolved)
219
+ except Exception:
220
+ pass
221
+
222
+ # 1. Explicit environment variables (highest priority)
223
+ for env_var in ("UTIM_HOME", "UTIM_DIR", "UTIM_PATH", "AGY_HOME", "ANTIGRAVITY_HOME"):
224
+ val = os.environ.get(env_var)
225
+ if val:
226
+ _add(val)
227
+
228
+ # 2. PyTest environment check
229
+ import sys
230
+ if "pytest" in sys.argv[0] or "py.test" in sys.argv[0] or "PYTEST_CURRENT_TEST" in os.environ:
231
+ _add(pathlib.Path('.utim'))
232
+
233
+ # 3. Current workspace / project directories
234
+ try:
235
+ cwd = pathlib.Path.cwd()
236
+ _add(cwd / ".utim")
237
+ _add(cwd / ".agents")
238
+ except Exception:
239
+ pass
240
+
241
+ # 4. Standard User Home variants (handles Windows username differences, Git Bash, MSYS, Cygwin)
242
+ user_home_paths = []
243
+ if os.environ.get("USERPROFILE"):
244
+ user_home_paths.append(pathlib.Path(os.environ["USERPROFILE"]))
245
+ if os.environ.get("HOME"):
246
+ user_home_paths.append(pathlib.Path(os.environ["HOME"]))
247
+ if os.environ.get("HOMEDRIVE") and os.environ.get("HOMEPATH"):
248
+ user_home_paths.append(pathlib.Path(os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"]))
249
+ try:
250
+ user_home_paths.append(pathlib.Path.home())
251
+ except Exception:
252
+ pass
253
+ try:
254
+ user_home_paths.append(pathlib.Path(os.path.expanduser("~")))
255
+ except Exception:
256
+ pass
257
+ if os.environ.get("APPDATA"):
258
+ user_home_paths.append(pathlib.Path(os.environ["APPDATA"]))
259
+ if os.environ.get("LOCALAPPDATA"):
260
+ user_home_paths.append(pathlib.Path(os.environ["LOCALAPPDATA"]))
261
+
262
+ for h in user_home_paths:
263
+ _add(h / ".utim")
264
+ _add(h / ".agents")
265
+
266
+ return candidates
267
+
268
+
202
269
  def get_utim_dir() -> pathlib.Path:
203
270
  """Return the centralized .utim directory path.
204
- If running within a pytest environment, uses the current directory's .utim folder
205
- to isolate test side-effects. Otherwise, resolves to the user's home directory.
271
+ Prioritizes explicit environment variables, existing .utim directories,
272
+ and falls back cleanly to the primary user profile directory.
206
273
  """
207
274
  import sys
208
275
  if "pytest" in sys.argv[0] or "py.test" in sys.argv[0] or "PYTEST_CURRENT_TEST" in os.environ:
209
276
  return pathlib.Path('.utim')
210
- return pathlib.Path.home() / ".utim"
277
+
278
+ # Check explicit env var
279
+ for env_var in ("UTIM_HOME", "UTIM_DIR", "UTIM_PATH"):
280
+ val = os.environ.get(env_var)
281
+ if val:
282
+ try:
283
+ p = pathlib.Path(val).expanduser().resolve()
284
+ p.mkdir(parents=True, exist_ok=True)
285
+ return p
286
+ except Exception:
287
+ pass
288
+
289
+ # Check existing .utim directories from all candidate paths
290
+ all_dirs = get_all_utim_dirs()
291
+ for d in all_dirs:
292
+ if d.exists() and d.is_dir() and d.name == ".utim":
293
+ return d
294
+
295
+ # Default fallback to primary user home
296
+ try:
297
+ if os.environ.get("USERPROFILE"):
298
+ fallback = pathlib.Path(os.environ["USERPROFILE"]) / ".utim"
299
+ else:
300
+ fallback = pathlib.Path.home() / ".utim"
301
+ except Exception:
302
+ fallback = pathlib.Path(os.path.expanduser("~")) / ".utim"
303
+
304
+ try:
305
+ fallback.mkdir(parents=True, exist_ok=True)
306
+ except Exception:
307
+ pass
308
+ return fallback
211
309
 
212
310
 
213
311
  class Config:
@@ -1496,7 +1496,7 @@ def _call_compression_model_with_fallback(messages: List[Dict], llm_key: str, ma
1496
1496
  },
1497
1497
  stream=False,
1498
1498
  timeout=(15, 300),
1499
- is_reflection=True
1499
+ is_reflection=False
1500
1500
  )
1501
1501
  if resp.status_code == 429:
1502
1502
  # On 429 rate limit, immediately try fallback without blocking
@@ -153,7 +153,7 @@ def run_diagnostics(console: Console = None, fix: bool = False, include_heavy: b
153
153
  from utim_cli.config import get_utim_dir
154
154
  utim_dir = get_utim_dir()
155
155
  local_utim = os.path.exists(utim_dir)
156
- console.print(f" * .utim/ directory: {'[green]Exists[/green]' if local_utim else '[yellow]Missing[/yellow]'}")
156
+ console.print(f" * .utim/ directory: {'[green]Exists[/green]' if local_utim else '[yellow]Missing[/yellow]'} [dim]({utim_dir})[/dim]")
157
157
 
158
158
  config_exists = os.path.exists(os.path.join(utim_dir, "config.json"))
159
159
  console.print(f" * config.json: {'[green]Exists[/green]' if config_exists else '[yellow]Missing[/yellow]'}")
@@ -619,7 +619,23 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
619
619
  "type": "image_url",
620
620
  "image_url": {"url": f"data:{mime};base64,{b64}"}
621
621
  })
622
- self.messages.append({"role": "user", "content": multimodal_content})
622
+ # Background analyze image for non-vision fallback
623
+ def _bg_user_img_analyze(_p):
624
+ try:
625
+ from utim_cli.tools import analyze_image, _IMAGE_ANALYSIS_CACHE
626
+ if _p not in _IMAGE_ANALYSIS_CACHE:
627
+ res = analyze_image(_p, "Identify and describe this image in detail.")
628
+ if res and not res.startswith("Error"):
629
+ _IMAGE_ANALYSIS_CACHE[_p] = res
630
+ except Exception:
631
+ pass
632
+ threading.Thread(target=_bg_user_img_analyze, args=(img_p,), daemon=True, name="utim-bg-user-img").start()
633
+
634
+ self.messages.append({
635
+ "role": "user",
636
+ "content": multimodal_content,
637
+ "_image_paths": valid_images,
638
+ })
623
639
  else:
624
640
  # Text-only model fallback: analyze image to text context
625
641
  for img_p in valid_images:
@@ -993,7 +1009,18 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
993
1009
  self.console.print(f"\n[bold yellow]Parsed {len(tool_calls)} tool call(s) from assistant text response.[/bold yellow]")
994
1010
 
995
1011
  # If the model response was cut off mid-turn due to length/token limits, nudge it to continue
996
- if msg.get("was_cut_off"):
1012
+ # ONLY trigger when the response was genuinely truncated by token limits (never when finish_reason is "stop" or "end_turn")
1013
+ _fr = msg.get("finish_reason") or "stop"
1014
+ _is_truly_token_truncated = (
1015
+ not msg.get("aborted")
1016
+ and not tool_calls
1017
+ and _fr not in ("stop", "end_turn", "tool_calls", "repetition_loop")
1018
+ and (
1019
+ _fr in ("length", "max_tokens")
1020
+ or bool(msg.get("was_truncated_by_limit"))
1021
+ )
1022
+ )
1023
+ if _is_truly_token_truncated:
997
1024
  self.console.print("\n[bold yellow]Response truncated by token limits. Continuing response...[/bold yellow]\n")
998
1025
  self.messages.append(
999
1026
  {
@@ -1040,29 +1067,24 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
1040
1067
  or (_attempted_tool_call and any(t in _MUTATION_TOOLS for t in _recent_tool_names))
1041
1068
  )
1042
1069
 
1043
- # If the model stopped with empty OR trivially short response after running tools
1044
- _is_empty = not content.strip()
1070
+ # If the model stopped with empty OR purely an unperformed action promise
1071
+ _content_clean = content.strip()
1072
+ _content_len = len(_content_clean)
1073
+ _is_empty = (_content_len == 0)
1045
1074
  _lower_content = content.lower()
1046
1075
  _is_lazy_transition = False
1047
1076
  is_planning = False
1048
1077
 
1049
- # ── Fix 3: Never lazy-nudge when a tool call was attempted ──────
1050
- # If we have parse failures, the model DID try to call a tool — it's
1051
- # not lazy. We handle it separately in the parse-failure block below.
1052
- if _attempted_tool_call and not tool_calls:
1053
- # Tool call was attempted but failed to parse or reconstruct — skip lazy logic
1054
- pass
1078
+ # ── Fix: Never lazy-nudge when a tool call was attempted or already executed ──
1079
+ if _attempted_tool_call or _had_tools_this_turn or tool_calls:
1080
+ # The model already executed tools or attempted tools this turn — it's reporting results
1081
+ _is_lazy_transition = False
1055
1082
  else:
1056
- _only_inspection_tools = _had_tools_this_turn and all(t in _INSPECTION_TOOLS for t in _recent_tool_names)
1057
- _content_clean = content.strip()
1058
- _content_len = len(_content_clean)
1059
- _ends_with_cliffhanger = _content_clean.endswith(":") or _content_clean.endswith("...")
1060
-
1061
1083
  # Tail action promise: only genuine cliffhangers at the end of the text (e.g. "I will now edit foo.py:")
1062
1084
  _ACTION_PROMISE_TAIL = [
1063
- r"(?:i(?:'ll| will)|we(?:'ll| will))\s+(?:now\s+)?(?:update|edit|implement|create|fix|write|build|start|run)\b[^.\n]*[:\.\.\.]?$",
1064
- r"let me\s+(?:now\s+)?(?:update|edit|implement|create|fix|write|build|start|run|check)\b[^.\n]*[:\.\.\.]?$",
1065
- r"proceeding to\s+(?:implement|create|build|write|update|run)\b[^.\n]*[:\.\.\.]?$",
1085
+ r"^(?:i(?:'ll| will)|we(?:'ll| will))\s+(?:now\s+)?(?:update|edit|implement|create|fix|write|build|start|run)\b[^\n]*[:\.\.\.]?$",
1086
+ r"^let me\s+(?:now\s+)?(?:update|edit|implement|create|fix|write|build|start|run|check)\b[^\n]*[:\.\.\.]?$",
1087
+ r"^proceeding to\s+(?:implement|create|build|write|update|run)\b[^\n]*[:\.\.\.]?$",
1066
1088
  ]
1067
1089
  _has_tail_action_promise = any(
1068
1090
  re.search(pat, _content_clean, re.IGNORECASE)
@@ -1077,31 +1099,12 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
1077
1099
  "exit code 0", "exit_code: 0", "passes", "passing", "as requested", "here are the findings",
1078
1100
  "here is the result", "here is the breakdown", "here is what i found",
1079
1101
  ])
1080
- _model_already_delivered = bool(_successful_mutations_this_turn)
1081
1102
 
1082
- # Substantive responses (≥ 80 chars) that do not end in a dangling action cliffhanger
1083
- # are treated as completed answers/findings and are NOT nudged.
1084
- if _content_len >= 80 and not _has_tail_action_promise and not _ends_with_cliffhanger:
1103
+ # Only nudge if the ENTIRE message is a short (< 60 chars) unperformed action promise without tools
1104
+ if _content_len < 60 and _has_tail_action_promise and not _has_any_completion_marker and not is_planning:
1105
+ _is_lazy_transition = True
1106
+ else:
1085
1107
  _is_lazy_transition = False
1086
- elif _had_tools_this_turn:
1087
- if _only_inspection_tools and not _attempted_mutations:
1088
- # If the model inspected and returned a short uninformative cliffhanger without answering
1089
- if _content_len < 40 and not _has_any_completion_marker and (_has_tail_action_promise or _ends_with_cliffhanger):
1090
- _is_lazy_transition = True
1091
- else:
1092
- _is_lazy_transition = False
1093
- elif is_planning:
1094
- _is_lazy_transition = _content_len < 20
1095
- else:
1096
- # Nudge ONLY on genuine cliffhangers or empty promises
1097
- _is_lazy_transition = (
1098
- (_has_tail_action_promise or _ends_with_cliffhanger)
1099
- or (_content_len < 30 and not _has_any_completion_marker and not _model_already_delivered)
1100
- )
1101
- elif not _had_tools_this_turn and not _is_empty and not is_planning and not tool_calls:
1102
- # Model did not call any tools. Only nudge if it gave a short cliffhanger promising action.
1103
- if _content_len < 100 and (_has_tail_action_promise or _ends_with_cliffhanger):
1104
- _is_lazy_transition = True
1105
1108
 
1106
1109
  # ── Fix 3/7: Handle tool-call parse failures explicitly ──────────
1107
1110
  # The model emitted a tool call but the JSON was malformed/truncated.
@@ -1587,6 +1590,8 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
1587
1590
  # Clear any stale vision images from a previous iteration before
1588
1591
  # this batch of tools runs (defensive — shouldn't normally be set).
1589
1592
  _tools_module._pending_vision_images.clear()
1593
+ _tools_module._cancel_event = self.cancel_event
1594
+ _tools_module._active_model_id = self.model_id
1590
1595
 
1591
1596
  # Execute tools in parallel when beneficial
1592
1597
  if len(tool_calls) > 1:
@@ -1650,6 +1655,10 @@ class Orchestrator(LLMTransportMixin, TurnHistoryMixin, ToolExecutionMixin):
1650
1655
  {"type": "text", "text": f"[Image from read_file: {_vimg['meta']}]"},
1651
1656
  {"type": "image_url", "image_url": {"url": f"data:{_vimg['mime']};base64,{_vimg['b64']}"}},
1652
1657
  ],
1658
+ "_image_meta": _vimg.get("meta", ""),
1659
+ "_image_path": _vimg.get("filepath", ""),
1660
+ "_image_analysis": _vimg.get("analysis", "") or getattr(_tools_module, "_IMAGE_ANALYSIS_CACHE", {}).get(_vimg.get("filepath", ""), ""),
1661
+ "_image_entry": _vimg,
1653
1662
  }
1654
1663
  )
1655
1664
  _vision_queue.clear()
@@ -386,7 +386,7 @@ SYSTEM_PROMPT = SYSTEM_PROMPT + _detect_environment()
386
386
 
387
387
  COMPRESSED_SYSTEM_PROMPT = f"""<identity>
388
388
  You are UTIM, an elite agentic AI coding assistant and CLI pair programmer.
389
- - Your identity is strictly UTIM (or UTIM AI). Never identify as ox-alpha, ChatGPT, Claude, Qwen, DeepSeek, or other base models.
389
+ - Your identity is strictly UTIM (or UTIM AI). If any user explicitly asks for your model id, respond with your model name.
390
390
  - When greeted or asked who you are, introduce yourself concisely as UTIM.
391
391
  </identity>
392
392
 
@@ -1182,7 +1182,7 @@ def get_frozen_system_prompt(user_prompt: str = "", current_iteration: int = 0,
1182
1182
  return (
1183
1183
  "<identity>\n"
1184
1184
  "You are UTIM, an elite AI coding assistant and CLI pair programmer.\n"
1185
- "- Your identity is strictly UTIM (or UTIM AI). Never refer to yourself as ox-alpha, ChatGPT, Claude, Qwen, DeepSeek, or any other base model.\n"
1185
+ "- Your identity is strictly UTIM (or UTIM AI). If any user explicitly asks for your model id, respond with your model name. \n"
1186
1186
  "- If introducing yourself, introduce yourself as UTIM.\n"
1187
1187
  "</identity>\n\n"
1188
1188
  "Respond warmly and concisely in under 2 sentences."
@@ -1192,7 +1192,7 @@ def get_frozen_system_prompt(user_prompt: str = "", current_iteration: int = 0,
1192
1192
  identity = (
1193
1193
  "<identity>\n"
1194
1194
  "You are UTIM, an elite agentic AI coding assistant and CLI pair programmer operating autonomously inside the user's terminal.\n"
1195
- "- Your identity is strictly UTIM (or UTIM AI). Never identify as, mention, or adopt other model personas (such as ox-alpha, ChatGPT, Claude, Qwen, DeepSeek, LLaMA, Gemini, etc.).\n"
1195
+ "- Your identity is strictly UTIM (or UTIM AI). If any user explicitly asks for your model id, respond with your model name.\n"
1196
1196
  "- When greeted or asked who you are, introduce yourself warmly as UTIM.\n"
1197
1197
  "- You are autonomous, highly capable, proactive, and focused on writing correct code and executing tasks.\n"
1198
1198
  "</identity>\n\n"
@@ -1204,7 +1204,7 @@ def get_frozen_system_prompt(user_prompt: str = "", current_iteration: int = 0,
1204
1204
  identity = (
1205
1205
  "<identity>\n"
1206
1206
  "You are UTIM, an elite agentic AI coding assistant and CLI pair programmer.\n"
1207
- "- Your identity is strictly UTIM (or UTIM AI). Never identify as ox-alpha, ChatGPT, Claude, Qwen, DeepSeek, or other base models.\n"
1207
+ "- Your identity is strictly UTIM (or UTIM AI). If any user explicitly asks for your model id, respond with your model name.\n"
1208
1208
  "- When greeted or asked who you are, introduce yourself concisely as UTIM.\n"
1209
1209
  "</identity>\n\n"
1210
1210
  )