utim-cli 2.1.0__tar.gz → 2.1.2__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 (118) hide show
  1. {utim_cli-2.1.0 → utim_cli-2.1.2}/CHANGELOG.md +14 -0
  2. utim_cli-2.1.2/LICENSE +26 -0
  3. {utim_cli-2.1.0/utim_cli.egg-info → utim_cli-2.1.2}/PKG-INFO +2 -1
  4. {utim_cli-2.1.0 → utim_cli-2.1.2}/pyproject.toml +4 -1
  5. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/__init__.py +1 -1
  6. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_version.py +1 -1
  7. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/blender_agent.py +2 -1
  8. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/client_utils.py +20 -3
  9. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/config.py +4 -2
  10. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/constants.py +2 -0
  11. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/context_pruner.py +7 -3
  12. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/orchestrator.py +35 -30
  13. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/reflection.py +2 -1
  14. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/auth.py +485 -483
  15. utim_cli-2.1.2/utim_cli/server/batch_processor.py +119 -0
  16. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/db.py +25 -0
  17. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/email_utils.py +32 -84
  18. utim_cli-2.1.2/utim_cli/server/fix_duplicate_users.py +91 -0
  19. utim_cli-2.1.2/utim_cli/server/micro_batcher.py +95 -0
  20. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/models.py +6 -10
  21. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/router.py +32 -35
  22. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/__init__.py +1 -0
  23. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/auth_routes.py +6 -3
  24. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/completion_routes.py +67 -3
  25. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/quota_routes.py +1604 -1595
  26. utim_cli-2.1.2/utim_cli/server/routes/share_routes.py +263 -0
  27. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/server.py +4 -0
  28. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/storage_nodes.py +4 -0
  29. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/share.py +245 -92
  30. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/share_tui.py +1115 -792
  31. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/situational_scoring.py +5 -2
  32. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tools.py +383 -131
  33. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/feedback_dialog.py +433 -419
  34. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/mcp_dialog.py +44 -0
  35. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/model_dialog.py +11 -4
  36. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/utim.py +68 -6
  37. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/vector_memory.py +10 -1
  38. {utim_cli-2.1.0 → utim_cli-2.1.2/utim_cli.egg-info}/PKG-INFO +2 -1
  39. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/SOURCES.txt +3 -0
  40. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/entry_points.txt +1 -0
  41. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/requires.txt +1 -0
  42. utim_cli-2.1.0/LICENSE +0 -21
  43. utim_cli-2.1.0/utim_cli/server/routes/share_routes.py +0 -216
  44. {utim_cli-2.1.0 → utim_cli-2.1.2}/MANIFEST.in +0 -0
  45. {utim_cli-2.1.0 → utim_cli-2.1.2}/README.md +0 -0
  46. {utim_cli-2.1.0 → utim_cli-2.1.2}/setup.cfg +0 -0
  47. {utim_cli-2.1.0 → utim_cli-2.1.2}/setup.py +0 -0
  48. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_check_obj.py +0 -0
  49. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_mcp.py +0 -0
  50. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_plan.py +0 -0
  51. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_tool.py +0 -0
  52. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_patch_multimodal.py +0 -0
  53. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_test_regex.py +0 -0
  54. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/agent.py +0 -0
  55. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/auth.py +0 -0
  56. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/backup.py +0 -0
  57. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/billing.py +0 -0
  58. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/bootstrap.py +0 -0
  59. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/doctor.py +0 -0
  60. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/harbor.py +0 -0
  61. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/knowledge_graph.py +0 -0
  62. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/local_db.py +0 -0
  63. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/logger.py +0 -0
  64. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_clean_wrapper.py +0 -0
  65. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_client.py +0 -0
  66. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_registry.json +0 -0
  67. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/report.py +0 -0
  68. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/scrapy_search.py +0 -0
  69. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/__init__.py +0 -0
  70. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/attribution.py +0 -0
  71. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/audit_log.py +0 -0
  72. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/captcha.py +0 -0
  73. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/cli_auth.py +0 -0
  74. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  75. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/about.md +0 -0
  76. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/changelog.md +0 -0
  77. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/docs.md +0 -0
  78. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/features.md +0 -0
  79. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/license.md +0 -0
  80. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/pricing.md +0 -0
  81. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/privacy.md +0 -0
  82. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/refund.md +0 -0
  83. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/support.md +0 -0
  84. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/terms.md +0 -0
  85. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/exchange_rate.py +0 -0
  86. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/firebase.py +0 -0
  87. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/history.py +0 -0
  88. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/logging_config.py +0 -0
  89. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/pricing_updater.py +0 -0
  90. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/provision_build.py +0 -0
  91. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/rate_limit.py +0 -0
  92. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/credit_routes.py +0 -0
  93. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/feedback_routes.py +0 -0
  94. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/marketplace_routes.py +0 -0
  95. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/quota_share_routes.py +0 -0
  96. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/referral_routes.py +0 -0
  97. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/security_routes.py +0 -0
  98. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/session_routes.py +0 -0
  99. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/state.py +0 -0
  100. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/__init__.py +0 -0
  101. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/history_dialog.py +0 -0
  102. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_app_state.py +0 -0
  103. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_dialog.py +0 -0
  104. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  105. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/miniagents_dialog.py +0 -0
  106. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/publish_dialog.py +0 -0
  107. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_dialog.py +0 -0
  108. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_redeem_dialog.py +0 -0
  109. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_share_dialog.py +0 -0
  110. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/resume_dialog.py +0 -0
  111. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/skills_dialog.py +0 -0
  112. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/subagents_dialog.py +0 -0
  113. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/tools_dialog.py +0 -0
  114. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/update_dialog.py +0 -0
  115. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/utilities.py +0 -0
  116. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/workspace.py +0 -0
  117. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/dependency_links.txt +0 -0
  118. {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.2] - 2026-08-10
4
+
5
+ ### 🛠️ Fixes
6
+ - **Fixed `grep_search` auto-regex promotion bug**: Queries containing regex metacharacters (`|`, `\`, `*`, etc.) were silently auto-promoted to regex mode even when `is_regex=False`, causing literal searches to fail with "No matches found." Now `is_regex=False` always means literal search, matching the documented "Literal-first semantics."
7
+ - **Made ripgrep an automatic dependency**: Added `ripgrep>=0.13.0` to core dependencies in `pyproject.toml` and `requirements.txt`. The `_grep_find_ripgrep()` function now falls back to the `ripgrep` PyPI package (which bundles a prebuilt binary) when ripgrep isn't found on the system PATH, ensuring fast searches work out of the box.
8
+ - **Increased context compression interval from 25 to 35 iterations**: Reduced compression frequency to conserve context resources and improve performance during long agent sessions.
9
+ - **Reduced batch poll interval from 5s to 0.3s**: Improved responsiveness of batch status checks in `BatchAPIProcessor.poll_and_retrieve_results()`.
10
+ - **Cleaned up network error messages in feedback dialog**: Stripped verbose `HTTPSConnectionPool` technical details from error messages, now showing clean messages like "Connection timed out. Please check your network and try again."
11
+
12
+ ## [2.1.1] - 2026-08-06
13
+
14
+ ### 🛠️ Fixes
15
+ - **Enhanced `grep_search` reliability**: Fixed broken auto-regex promotion, ripgrep flag bugs (`-max-count` → `--max-count`), single-file `--with-filename` handling, Windows drive-letter colon parsing, and fallback file-list mode counting. Added dual-engine architecture (cached ripgrep fast-path + multithreaded Python fallback), binary-file sniffing, symlink-cycle protection, expanded noise filtering, and multi-encoding support.
16
+
3
17
  ## [2.1.0] - 2026-08-05
4
18
 
5
19
  ### ⚡ Performance
utim_cli-2.1.2/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ UTIM PROPRIETARY SOFTWARE END USER LICENSE AGREEMENT (EULA)
2
+
3
+ Copyright (c) 2026 Emend AI. All Rights Reserved.
4
+
5
+ IMPORTANT: READ THIS LICENSE AGREEMENT CAREFULLY BEFORE DOWNLOADING, INSTALLING, OR USING THE UTIM CLI SOFTWARE.
6
+
7
+ 1. PROPRIETARY SOFTWARE
8
+ UTIM CLI ("Software") is proprietary commercial software developed and owned exclusively by Emend AI ("Licensor"). The Software is licensed, not sold, to you ("Licensee") under the terms and conditions of this Agreement.
9
+
10
+ 2. LICENSE GRANT
11
+ Subject to compliance with this Agreement and payment of applicable subscription or usage fees:
12
+ - Free Tier License: Licensor grants Licensee a limited, non-exclusive, non-transferable, revocable license to install and use the Software for personal, open-source learning, and non-commercial evaluation purposes.
13
+ - Commercial Tier License: Paid subscribers (Hobby, Pro, Max, Ultimate Tiers) or users with active Bring Your Own Key (BYOK) configurations are granted a non-exclusive, non-transferable license to use the Software for commercial software engineering, corporate repositories, and professional client work.
14
+
15
+ 3. RESTRICTIONS
16
+ Licensee shall NOT:
17
+ - Reverse engineer, decompile, disassemble, modify, or attempt to discover the source code or underlying client orchestrator mechanisms of compiled Software binaries.
18
+ - Circumvent rate limits, credit accounting controls, or 5-hour quota refill mechanisms.
19
+ - Sublicense, resell, rent, lease, or distribute the Software to third parties without prior written authorization.
20
+ - Create duplicate or automated accounts to harvest free promotional credit allocations.
21
+
22
+ 4. INTELLECTUAL PROPERTY
23
+ All title, ownership, brand trademarks, domain adaptation engines, AST parsing logic, and intellectual property rights in and to the Software remain the exclusive property of Emend AI. Licensee retains 100% full ownership of all user prompts submitted and code files generated in their local workspaces.
24
+
25
+ 5. NO WARRANTY & LIMITATION OF LIABILITY
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL EMEND AI OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES (INCLUDING DATA LOSS, REPOSITORY CORRUPTION, OR API BILLING COSTS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://utim.dev
@@ -31,6 +31,7 @@ Requires-Dist: rich>=13.0.0
31
31
  Requires-Dist: prompt_toolkit>=3.0.0
32
32
  Requires-Dist: nest-asyncio>=1.5.0
33
33
  Requires-Dist: sqlalchemy>=2.0.0
34
+ Requires-Dist: ripgrep>=0.13.0
34
35
  Provides-Extra: search
35
36
  Requires-Dist: scrapy>=2.11.0; extra == "search"
36
37
  Requires-Dist: scrapy-playwright>=0.0.34; extra == "search"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -33,6 +33,7 @@ dependencies = [
33
33
  "prompt_toolkit>=3.0.0",
34
34
  "nest-asyncio>=1.5.0",
35
35
  "sqlalchemy>=2.0.0",
36
+ "ripgrep>=0.13.0",
36
37
  ]
37
38
 
38
39
  [project.urls]
@@ -88,6 +89,8 @@ memory = [
88
89
 
89
90
  [project.scripts]
90
91
  utim = "utim_cli.utim:main_cli_entry"
92
+ utimlite = "utim_cli.utim:main_cli_entry"
93
+
91
94
 
92
95
  [tool.setuptools.packages.find]
93
96
  include = ["utim_cli*"]
@@ -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.1.0"
8
+ __version__ = "2.1.2"
9
9
 
10
10
  import builtins, os, pathlib, urllib.parse, re
11
11
 
@@ -11,4 +11,4 @@
11
11
  # from here so every surface stays in sync automatically.
12
12
  # ─────────────────────────────────────────────────────────────────────────────
13
13
 
14
- VERSION = "2.1.0"
14
+ VERSION = "2.1.2"
@@ -63,12 +63,13 @@ _VISION_MODELS: List[str] = [
63
63
  ]
64
64
 
65
65
  _CODE_MODELS: List[str] = [
66
- "poolside/laguna-m.1:free",
67
66
  DEFAULT_MODEL,
67
+ "cohere/north-mini-code:free",
68
68
  "openrouter/free"
69
69
  ]
70
70
 
71
71
 
72
+
72
73
  def _get_vision_models() -> List[str]:
73
74
  """Return vision model list, with user override prepended if configured."""
74
75
  try:
@@ -128,6 +128,7 @@ def proxy_openrouter_request(json_data: dict, stream: bool = False, timeout=None
128
128
  server_url = get_server_url()
129
129
  llm_key = os.getenv("OPENROUTER_API_KEY")
130
130
 
131
+
131
132
  if llm_key:
132
133
  # Direct path to OpenRouter — must always carry canonical attribution
133
134
  # headers so OpenRouter shows "UTIM CLI Agent" (NOT "unknown") in its
@@ -199,9 +200,25 @@ def proxy_openrouter_request(json_data: dict, stream: bool = False, timeout=None
199
200
  if "reasoning" in json_data:
200
201
  payload["reasoning"] = json_data["reasoning"]
201
202
 
202
- # UTIM server completions is a streaming endpoint
203
- resp = requests.post(f"{server_url}/completions", json=payload, headers=headers, stream=True, timeout=timeout, verify=config.verify_ssl)
204
- resp.raise_for_status()
203
+ # UTIM server completions is a streaming endpoint with retry & connection protection
204
+ import time as _timemod
205
+ max_http_retries = 2
206
+ last_http_err = None
207
+ resp = None
208
+
209
+ for attempt in range(max_http_retries + 1):
210
+ try:
211
+ resp = requests.post(f"{server_url}/completions", json=payload, headers=headers, stream=True, timeout=timeout, verify=config.verify_ssl)
212
+ resp.raise_for_status()
213
+ last_http_err = None
214
+ break
215
+ except (requests.exceptions.ConnectionError, requests.exceptions.ChunkedEncodingError, requests.exceptions.Timeout) as _conn_err:
216
+ last_http_err = _conn_err
217
+ if attempt < max_http_retries:
218
+ _timemod.sleep(1.0 * (attempt + 1))
219
+ continue
220
+ else:
221
+ raise RuntimeError(f"Connection to UTIM server failed: {str(_conn_err)}")
205
222
 
206
223
  if stream or json_data.get("stream"):
207
224
  def line_generator():
@@ -492,10 +492,12 @@ class Config:
492
492
  if models:
493
493
  return [m.strip() for m in models.split(",")]
494
494
  return [
495
- "poolside/laguna-m.1:free",
496
- "openrouter/free"
495
+ "cohere/north-mini-code:free",
496
+ "openrouter/free",
497
+ "inclusionai/ling-3.0-flash:free",
497
498
  ]
498
499
 
500
+
499
501
  @property
500
502
  def keep_full_turns(self) -> int:
501
503
  # Number of recent turns to retain in memory
@@ -10,6 +10,8 @@ SUBAGENT_DEFAULTS = {
10
10
  "generate_image": "nvidia/nemotron-3-nano-30b-a3b:free",
11
11
  }
12
12
 
13
+
14
+
13
15
  import sys
14
16
  _IS_LEGACY_WIN = (sys.platform == "win32" and "WT_SESSION" not in os.environ)
15
17
  PROMPT_SYMBOL = ">" if _IS_LEGACY_WIN else "❯"
@@ -16,11 +16,14 @@ from utim_cli.constants import DEFAULT_MODEL
16
16
  # Fallback models for compression operations - ordered by reliability and quality.
17
17
  # "openrouter/free" is the first fallback tried after the primary model.
18
18
  COMPRESSION_FALLBACK_MODELS = [
19
- "openrouter/free",
20
- "poolside/laguna-xs-2.1:free",
21
19
  "cohere/north-mini-code:free",
20
+ "openrouter/free",
21
+ "inclusionai/ling-3.0-flash:free",
22
22
  ]
23
23
 
24
+
25
+
26
+
24
27
  # Track what content has already been summarized to prevent duplicate summarization
25
28
  _summarized_content_hashes: Set[str] = set()
26
29
 
@@ -340,9 +343,10 @@ def _call_compression_model_with_fallback(messages: List[Dict], llm_key: str, ma
340
343
  if content_hash in _summarized_content_hashes:
341
344
  return "[Content previously summarized - skipped]"
342
345
  _summarized_content_hashes.add(content_hash)
343
-
346
+
344
347
  # Prioritize primary model if provided, followed by fallback models
345
348
  models_to_try = list(COMPRESSION_FALLBACK_MODELS)
349
+
346
350
  if primary_model:
347
351
  if primary_model in models_to_try:
348
352
  models_to_try.remove(primary_model)
@@ -171,22 +171,10 @@ def _get_compression_threshold(model_id: str, context_window: int) -> int:
171
171
  return int(context_window * 0.90)
172
172
 
173
173
 
174
- def _get_compression_interval(context_window: int) -> int:
175
- """Return the iteration gap for context compression based on model context window:
176
- - < 200k context: every 10th iteration
177
- - 200k – 500k context: every 20th iteration
178
- - 500k – 1M context: every 30th iteration
179
- - 1M+ context: every 45th iteration
180
- """
181
- cw = context_window or 128_000
182
- if cw < 200_000:
183
- return 10
184
- elif cw < 500_000:
185
- return 20
186
- elif cw < 1_000_000:
187
- return 30
188
- else:
189
- return 45
174
+ def _get_compression_interval(context_window: int = 0) -> int:
175
+ """Return the iteration gap for model context compression (every 35 iterations)."""
176
+ return 35
177
+
190
178
 
191
179
  from utim_cli.config import get_utim_dir
192
180
  _utim_dir_posix = get_utim_dir().as_posix()
@@ -2508,10 +2496,32 @@ class Orchestrator:
2508
2496
  header.append(f"{display_name} Failed", style="bold red")
2509
2497
  if display_arg:
2510
2498
  header.append(f" {display_arg}", style="white")
2499
+
2500
+ # Build the error body — add a hint for token/permission errors from MCP tools
2501
+ error_body = Text(result, style="red")
2502
+ result_lower = result.lower()
2503
+ is_permission_error = any(kw in result_lower for kw in [
2504
+ "permission denied", "not accessible by personal access token",
2505
+ "resource not accessible", "403", "unauthorized", "401",
2506
+ "insufficient scope", "bad credentials", "bad_credentials",
2507
+ "requires authentication", "token", "forbidden"
2508
+ ])
2509
+ # Only show hints for MCP tool errors (func_name contains __)
2510
+ if is_permission_error and "__" in func_name:
2511
+ server_name_hint = func_name.split("__")[0]
2512
+ error_body.append("\n\n💡 Hint: ", style="bold yellow")
2513
+ error_body.append(
2514
+ f"Your token for '{server_name_hint}' lacks the required permissions.\n"
2515
+ f" • Go to /mcp → select '{server_name_hint}' → 🔑 Update Token / Env Vars\n"
2516
+ f" • For GitHub: ensure your PAT has the 'repo' scope (classic token)\n"
2517
+ f" or the correct fine-grained repository permissions enabled.\n"
2518
+ f" • Generate a new token at: https://github.com/settings/tokens",
2519
+ style="yellow"
2520
+ )
2511
2521
 
2512
2522
  with self.console.capture() as capture:
2513
2523
  self.console.print(Panel(
2514
- Text(result, style="red"),
2524
+ error_body,
2515
2525
  title=header,
2516
2526
  title_align="left",
2517
2527
  border_style="red",
@@ -3759,15 +3769,9 @@ class Orchestrator:
3759
3769
  return 65_000
3760
3770
 
3761
3771
  def _get_dynamic_interval(self) -> int:
3762
- """Get dynamic iteration compression interval based on current model's context window."""
3763
- try:
3764
- from .server.models import get_model
3765
- model_entry = get_model(self.model_id)
3766
- if model_entry and model_entry.context_window:
3767
- return _get_compression_interval(model_entry.context_window)
3768
- except Exception:
3769
- pass
3770
- return 10
3772
+ """Get iteration context compression interval (every 35 iterations)."""
3773
+ return 35
3774
+
3771
3775
 
3772
3776
  def _get_iteration_budget(self) -> int:
3773
3777
  """Return the max iteration budget for the current model, scaled by context window.
@@ -5178,9 +5182,10 @@ class Orchestrator:
5178
5182
  t_tool_start = time.time()
5179
5183
 
5180
5184
  # ── Iteration-based auto-compression ─────────────────────────────
5181
- # Compress every 45 iterations to keep context lean regardless of
5182
- # model context window size.
5183
- COMPRESS_INTERVAL = 45
5185
+ # Compress every N iterations (default 35) to keep context lean.
5186
+ # The interval is dynamically determined by _get_dynamic_interval(),
5187
+ # which returns 35 by default but can scale with the model's context window.
5188
+ COMPRESS_INTERVAL = self._get_dynamic_interval()
5184
5189
  compression_instruction = "" # kept for _compress_intra_turn API compat
5185
5190
 
5186
5191
  if iteration > 0 and iteration % COMPRESS_INTERVAL == 0:
@@ -5639,7 +5644,7 @@ class Orchestrator:
5639
5644
  if hasattr(self, "_tool_name_failure_counts"):
5640
5645
  self._tool_name_failure_counts.pop(func_name, None)
5641
5646
 
5642
- # Periodic iteration context compression (every 10, 20, 30, or 45 iterations depending on context window)
5647
+ # Periodic iteration context compression (every 35 iterations)
5643
5648
  interval = getattr(self, "_compression_interval", None) or self._get_dynamic_interval()
5644
5649
  if turn_iteration > 0 and turn_iteration % interval == 0:
5645
5650
  try:
@@ -12,10 +12,11 @@ from utim_cli.constants import DEFAULT_MODEL
12
12
  # These are free-tier models optimized for structured JSON output tasks.
13
13
  # Primary is tried first; fallbacks are used on rate-limit or error.
14
14
  REFLECTION_MODELS = [
15
- "poolside/laguna-xs-2.1:free",
16
15
  "cohere/north-mini-code:free",
17
16
  "openrouter/free",
17
+ "inclusionai/ling-3.0-flash:free",
18
18
  ]
19
+
19
20
  REFLECTION_PRIMARY_MODEL = REFLECTION_MODELS[0]
20
21
  REFLECTION_MAX_TOKENS = 5000
21
22