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.
- {utim_cli-2.1.0 → utim_cli-2.1.2}/CHANGELOG.md +14 -0
- utim_cli-2.1.2/LICENSE +26 -0
- {utim_cli-2.1.0/utim_cli.egg-info → utim_cli-2.1.2}/PKG-INFO +2 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/pyproject.toml +4 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/__init__.py +1 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_version.py +1 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/blender_agent.py +2 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/client_utils.py +20 -3
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/config.py +4 -2
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/constants.py +2 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/context_pruner.py +7 -3
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/orchestrator.py +35 -30
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/reflection.py +2 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/auth.py +485 -483
- utim_cli-2.1.2/utim_cli/server/batch_processor.py +119 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/db.py +25 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/email_utils.py +32 -84
- utim_cli-2.1.2/utim_cli/server/fix_duplicate_users.py +91 -0
- utim_cli-2.1.2/utim_cli/server/micro_batcher.py +95 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/models.py +6 -10
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/router.py +32 -35
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/__init__.py +1 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/auth_routes.py +6 -3
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/completion_routes.py +67 -3
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/quota_routes.py +1604 -1595
- utim_cli-2.1.2/utim_cli/server/routes/share_routes.py +263 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/server.py +4 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/storage_nodes.py +4 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/share.py +245 -92
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/share_tui.py +1115 -792
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/situational_scoring.py +5 -2
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tools.py +383 -131
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/feedback_dialog.py +433 -419
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/mcp_dialog.py +44 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/model_dialog.py +11 -4
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/utim.py +68 -6
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/vector_memory.py +10 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2/utim_cli.egg-info}/PKG-INFO +2 -1
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/SOURCES.txt +3 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/entry_points.txt +1 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/requires.txt +1 -0
- utim_cli-2.1.0/LICENSE +0 -21
- utim_cli-2.1.0/utim_cli/server/routes/share_routes.py +0 -216
- {utim_cli-2.1.0 → utim_cli-2.1.2}/MANIFEST.in +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/README.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/setup.cfg +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/setup.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_check_obj.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_mcp.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_plan.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_find_tool.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_patch_multimodal.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/_test_regex.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/agent.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/auth.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/backup.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/billing.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/bootstrap.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/doctor.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/harbor.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/local_db.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/logger.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_clean_wrapper.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/report.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/captcha.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/cli_auth.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/history.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/pricing_updater.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/credit_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/marketplace_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/security_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/state.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_app_state.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/marketplace_layout_engine.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/miniagents_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/publish_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_redeem_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/quota_share_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/resume_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/skills_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/subagents_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/tools_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/tui/update_dialog.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/utilities.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli/workspace.py +0 -0
- {utim_cli-2.1.0 → utim_cli-2.1.2}/utim_cli.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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.
|
|
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*"]
|
|
@@ -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
|
-
|
|
204
|
-
|
|
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
|
-
"
|
|
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
|
|
@@ -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
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
|
3763
|
-
|
|
3764
|
-
|
|
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
|
|
5182
|
-
#
|
|
5183
|
-
|
|
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
|
|
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
|
|