v3.13.0: Desktop Updater, profile system fix, Antigravity E2E, conservative compaction

- Codex Desktop Updater: check/install/rollback/service management + manual rebuild
- Fix Codex CLI 0.134.0 profiles: separate <slug>.config.toml files
- Fix Antigravity: prod endpoint first, model resolution, OAUTH_PROVIDER
- Fix compaction: max_input_items 60->200 for 1M-token models
- Antigravity E2E test suite: test-antigravity.sh
- Windows GUI: UpdateDesktopWindow + profile slug fix
- Updated CHANGELOG.md and README.md
This commit is contained in:
Roman | RyzenAdvanced
2026-05-27 13:52:02 +04:00
Unverified
parent f9ba3b6e1c
commit 6a50714da6
11 changed files with 10347 additions and 71 deletions

View File

@@ -83,6 +83,109 @@ model_catalog_json = ""
"""
CHANGELOG = [
("3.13.0", "2026-05-27", [
"Codex Desktop Updater: auto-update from ilysenko/codex-desktop-linux",
"Fix Antigravity: prod endpoint first, model resolution, OAUTH_PROVIDER derivation",
"Fix Codex CLI 0.134.0 profile system: separate .config.toml files",
"Fix compaction: max_input_items 60->200 for 1M-token Antigravity models",
"Antigravity E2E test suite: bash test-antigravity.sh",
]),
("3.12.1", "2026-05-27", [
"Fix Antigravity adapter (PR #15): simplify model resolution",
"Removed broken schema sanitization, restored correct headers",
"Expanded model alias map for all Antigravity variants",
"Re-enabled gRPC fallback by default",
]),
("3.12.0", "2026-05-27", [
"gRPC auto-fallback for Antigravity provider (PR #13)",
"New antigravity_grpc module with protobuf client",
"REST 404 triggers gRPC fallback using display names",
"gRPC supports streaming and unary generate",
"Dynamic version fetch with probe validation",
"Antigravity v2 handler rewrite (anti-api approach)",
"Safety settings, stopSequences, sessionId, requestType: agent",
]),
("3.11.11", "2026-05-26", [
"Final trimming only removes plain messages, never function_call_output",
]),
("3.11.10", "2026-05-26", [
"Fix Antigravity: interleave function_call/output pairs in correct sequence (PR #11)",
"Fix Gemini sanitizer: trim leading/trailing non-user turns for Google API compliance",
"Stricter function call/response isolation — no merging across role boundaries",
]),
("3.11.9", "2026-05-26", [
"Fix Antigravity: preserve functionCall/functionResponse in Gemini sanitizer (PR #10)",
"Prevents tool responses from being merged/dropped in multi-turn Antigravity sessions",
]),
("3.11.8", "2026-05-26", [
"Vision description cache persisted across requests (no redundant API calls for same image)",
"Merge PR #8: fix vision cache persistence across requests",
]),
("3.11.7", "2026-05-26", [
"Vision auto-detect: uses provider's own vision model (e.g. 0G-Qwen-VL) as fallback for image description",
"Vision preprocessing replaces image stripping: images described via API instead of just removed",
"Fix AttributeError in image_url handling when value is string not dict",
"Merge PR #6: vision/OCR preprocessing for text-only models",
"Merge PR #7: 177 unit tests for translate-proxy.py",
"Auth os error 2 fix: GUI shows config-missing message instead of raw error",
]),
("3.11.6", "2026-05-26", [
"Antigravity loop breakers: per-session tracking, edit-intent nudge (first turn only)",
"Loop breaker: same tool+args repeated 5+ times triggers force finalization",
"Latest user instruction appended exactly once per request",
"Detailed [antigravity-loop] logging for all tracking fields",
"has_content fix: function_call now counts as valid output (no more infinite loops)",
"Antigravity-only changes, no touch to other providers",
]),
("3.11.5", "2026-05-26", [
"Token-aware compaction: fixes context_length_exceeded on small-context models (25 items x 1600 tokens)",
"Proactive compaction triggers on token count (>80% model limit), not just item count",
"Universal adaptive compaction: removed crof.ai-only gates, all providers get compaction",
"Vision model detection: strips images for non-vision models, keeps for vision-capable ones",
"Per-model token limit learning from context_length_exceeded error messages",
"Compaction aggression levels: normal vs extreme when tokens > 1.5x model limit",
"Smart-continue text-tool detection: triggers on tool-call text patterns, not just function_call_output",
"Active endpoint sync: GUI auto-removes stale endpoint references on startup",
]),
("3.11.0", "2026-05-26", [
"Merge cobra PR: concurrency semaphore (max 3), auto-continue for truncated text",
"SO_REUSEADDR on sticky port, proxy-stderr.log, stream diagnostics logging",
"Timeout/OSError handler sends response.failed SSE instead of silent drop",
"Restart Proxy button: only restarts proxy without killing Codex Desktop",
"Tool call argument normalizer: fixes Arguments->arguments, strips markdown wrapping",
"Smart-continue loop (2x retries): escalating nudges when model stops text-only mid-task",
"XML tool call extraction: parses patterns from text, injects as real calls",
"Auto-continue + smart-continue ordered with skip guard to avoid double-firing",
"API key hot-reload with mtime tracking + /admin/reload + /admin/verify-key endpoints",
"GUI hot-reload: auto-refreshes proxy key on endpoint edit, verifies with upstream",
"Synthetic tool-results disabled: was causing deepseek-v4-pro truncation on opencode.ai",
]),
("3.10.12", "2026-05-26", [
"Sticky endpoint: caches last working endpoint, sequential fallback on failure",
"Endpoint order: cloudcode-pa first (matches agy CLI), daily-cloudcode-pa fallback",
"Anti-stall engine: kills stale proxy processes + clears pycache on startup",
"Smart error classification: quota vs capacity vs banned vs validation vs auth",
"Rate limit reset parsing: extracts cooldown from error body for accuracy",
"Missing headers: X-Client-Name, X-Client-Version, x-goog-api-client, sessionId",
"Guardrail skip: simple messages (hi) skip agent guardrail, no more tool-call loops",
"Claude fixes: preserve all tools, skip compaction/normalizer/sanitization for Claude",
"Normalizer model param: distinguishes Claude vs Gemini for correct behavior",
]),
("3.10.11", "2026-05-26", [
"Hybrid endpoint fallback: cloudcode-pa then daily-cloudcode-pa on 429",
"daily-cloudcode-pa.googleapis.com (same endpoint agy-core uses)",
"429 errors log full response body for debugging",
"Rate-limit marking only after ALL endpoints fail",
"Restored SERVICE_DISABLED (403) fallthrough",
]),
("3.10.10", "2026-05-25", [
"Fix normalizer stripping ALL context after compaction on resumed sessions",
"No auto-reset when compaction summary present (preserves 1925+ turn history)",
"Always preserve compaction summaries in normalizer output",
"Deduplicate consecutive identical goal_context messages",
"Emergency reset preserves compaction summaries",
"Fix hashlib NameError in _antigravity_normalize_context (string comparison instead)",
]),
("3.10.9", "2026-05-25", [
"Antigravity: production-only endpoints (cloudcode-pa.googleapis.com), sandbox blocked unless ALLOW_ANTIGRAVITY_STAGING=1",
"Antigravity: 403 SERVICE_DISABLED falls through, 429 returns to client (no sandbox fallback)",
@@ -618,6 +721,9 @@ def safe_name(name):
digest = hashlib.sha1(name.encode("utf-8")).hexdigest()[:8]
return f"{base}-{digest}"
def _profile_slug(name):
return "".join(ch if ch.isalnum() else "-" for ch in name).strip("-") or "default"
def label_for_backend(backend_type):
return {
@@ -1008,10 +1114,9 @@ def write_config_for_native(endpoint, selected_model):
mc_path = PROXY_CONFIG_DIR / f"models-{safe_name(endpoint['name'])}.json"
mc_path.parent.mkdir(parents=True, exist_ok=True)
mc_path.write_text(json.dumps(model_catalog, indent=2))
mc_str = str(mc_path).replace("\\", "/")
new_config = [
f'profile = "{_toml_safe(endpoint["name"])}"\n',
main_config = [
f'model = "{_toml_safe(selected_model)}"\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
f'model_catalog_json = "{mc_str}"\n',
@@ -1019,16 +1124,21 @@ def write_config_for_native(endpoint, selected_model):
f'name = "{_toml_safe(endpoint["name"])}"\n',
f'base_url = "{_toml_safe(endpoint["base_url"])}"\n',
f'experimental_bearer_token = "{_toml_safe(_resolve_secret(endpoint["api_key"]))}"\n',
f'\n[profiles."{endpoint["name"]}"]\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
]
existing = CONFIG.read_text(encoding="utf-8") if CONFIG.exists() else ""
merged = _merge_toml(existing, "".join(main_config))
write_secure_text(CONFIG, merged)
profile_slug = _profile_slug(endpoint["name"])
profile_path = CONFIG.parent / f"{profile_slug}.config.toml"
profile_lines = [
f'model = "{_toml_safe(selected_model)}"\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
f'model_catalog_json = "{mc_str}"\n',
f'service_tier = "default"\n',
f'approvals_reviewer = "user"\n',
]
existing = CONFIG.read_text(encoding="utf-8") if CONFIG.exists() else ""
merged = _merge_toml(existing, "".join(new_config))
write_secure_text(CONFIG, merged)
write_secure_text(profile_path, "".join(profile_lines))
def write_config_for_translated(endpoint, selected_model, proxy_port=8080):
@@ -1037,10 +1147,9 @@ def write_config_for_translated(endpoint, selected_model, proxy_port=8080):
mc_path = PROXY_CONFIG_DIR / f"models-{safe_name(endpoint['name'])}.json"
mc_path.parent.mkdir(parents=True, exist_ok=True)
mc_path.write_text(json.dumps(model_catalog, indent=2))
mc_str = str(mc_path).replace("\\", "/")
new_config = [
f'profile = "{_toml_safe(endpoint["name"])}"\n',
main_config = [
f'model = "{_toml_safe(selected_model)}"\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
f'model_catalog_json = "{mc_str}"\n',
@@ -1048,16 +1157,21 @@ def write_config_for_translated(endpoint, selected_model, proxy_port=8080):
f'name = "{_toml_safe(endpoint["name"])}"\n',
f'base_url = "http://127.0.0.1:{proxy_port}"\n',
f'experimental_bearer_token = "codex-launcher-local"\n',
f'\n[profiles."{endpoint["name"]}"]\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
]
existing = CONFIG.read_text(encoding="utf-8") if CONFIG.exists() else ""
merged = _merge_toml(existing, "".join(main_config))
write_secure_text(CONFIG, merged)
profile_slug = _profile_slug(endpoint["name"])
profile_path = CONFIG.parent / f"{profile_slug}.config.toml"
profile_lines = [
f'model = "{_toml_safe(selected_model)}"\n',
f'model_provider = "{_toml_safe(endpoint["name"])}"\n',
f'model_catalog_json = "{mc_str}"\n',
f'service_tier = "fast"\n',
f'approvals_reviewer = "user"\n',
]
existing = CONFIG.read_text(encoding="utf-8") if CONFIG.exists() else ""
merged = _merge_toml(existing, "".join(new_config))
write_secure_text(CONFIG, merged)
write_secure_text(profile_path, "".join(profile_lines))
# ═══════════════════════════════════════════════════════════════════════
# Model fetching
@@ -1442,6 +1556,7 @@ def _pick_free_port():
try:
saved = int(_PROXY_PORT_FILE.read_text().strip())
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", saved))
return saved
except (ValueError, OSError, FileNotFoundError):
@@ -1533,11 +1648,19 @@ def _start_proxy_with_config(pcfg_path, port, logfn):
)
_register_pgid_entry("proxy", _proxy_proc.pid)
_proxy_log_path = PROXY_CONFIG_DIR / "proxy-stderr.log"
_proxy_log_file = open(_proxy_log_path, "a", encoding="utf-8")
def _pipe_stderr():
if not _proxy_proc.stderr:
return
for line in _proxy_proc.stderr:
logfn(f"[proxy] {line.rstrip()}")
try:
_proxy_log_file.write(line)
_proxy_log_file.flush()
except Exception:
pass
threading.Thread(target=_pipe_stderr, daemon=True).start()
@@ -1655,6 +1778,10 @@ def check_codex_auth():
return ("unknown", "No output from codex login status")
except FileNotFoundError:
return ("not_installed", "codex not found")
except OSError as e:
if e.errno == 2:
return ("not_configured", "Config not found — launch Codex once to create it")
return ("error", str(e))
except Exception as e:
return ("error", str(e))