Compare commits
6 Commits
a1d0fc3707
...
v3.10.11
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,5 +1,36 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.10.11 (2026-05-26)
|
||||||
|
|
||||||
|
**Hybrid Endpoint Fallback — Redundant Antigravity Endpoints**
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
- Hybrid endpoint fallback: tries `cloudcode-pa.googleapis.com` then `daily-cloudcode-pa.googleapis.com` on 429
|
||||||
|
- `daily-cloudcode-pa.googleapis.com` is the same production endpoint agy-core uses (separate rate limit bucket)
|
||||||
|
- 429 errors now log full response body for debugging
|
||||||
|
- SERVICE_DISABLED (403) still falls through to next endpoint
|
||||||
|
- Rate-limit marking only happens after ALL endpoints fail
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Fixed 429 on one endpoint immediately failing — now tries fallback before giving up
|
||||||
|
- Restored SERVICE_DISABLED fallthrough (was accidentally removed)
|
||||||
|
|
||||||
|
## v3.10.10 (2026-05-25)
|
||||||
|
|
||||||
|
**Context Normalizer Fix — Compaction Summary Preservation**
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Fixed normalizer stripping ALL context on resumed sessions after compaction
|
||||||
|
- Normalizer no longer auto-resets when compaction summary is present
|
||||||
|
- Compaction summaries ("Auto-compacted: N earlier turns") are always preserved
|
||||||
|
- Deduplicates consecutive identical `<goal_context>` messages (10→1)
|
||||||
|
- Emergency reset now preserves compaction summaries
|
||||||
|
- Previous behavior: after compaction reduced 1925→185 items, normalizer saw `n_tool_outputs == 0` and stripped to just `system + latest_user`, losing all context — model responded with "I don't have context"
|
||||||
|
|
||||||
|
### hashlib Fix (v3.10.9 hotfix)
|
||||||
|
- `_antigravity_normalize_context` crashed with `NameError: hashlib` on resumed sessions
|
||||||
|
- Replaced SHA256 duplicate detection with string comparison
|
||||||
|
|
||||||
## v3.10.9 (2026-05-25)
|
## v3.10.9 (2026-05-25)
|
||||||
|
|
||||||
**Antigravity Overhaul — Context Normalizer, Claude Thinking Fix, Endpoint Lockdown**
|
**Antigravity Overhaul — Context Normalizer, Claude Thinking Fix, Endpoint Lockdown**
|
||||||
|
|||||||
@@ -1856,7 +1856,7 @@ class LauncherWin(Gtk.Window):
|
|||||||
# header row
|
# header row
|
||||||
hdr = Gtk.Box(spacing=8)
|
hdr = Gtk.Box(spacing=8)
|
||||||
vbox.pack_start(hdr, False, False, 0)
|
vbox.pack_start(hdr, False, False, 0)
|
||||||
lbl = Gtk.Label(label="<b>Codex Launcher v3.10.7</b>")
|
lbl = Gtk.Label(label="<b>Codex Launcher v3.10.9</b>")
|
||||||
lbl.set_use_markup(True)
|
lbl.set_use_markup(True)
|
||||||
hdr.pack_start(lbl, False, False, 0)
|
hdr.pack_start(lbl, False, False, 0)
|
||||||
changelog_btn = Gtk.Button(label="Changelog")
|
changelog_btn = Gtk.Button(label="Changelog")
|
||||||
|
|||||||
BIN
codex-launcher_3.10.10_all.deb
Normal file
BIN
codex-launcher_3.10.10_all.deb
Normal file
Binary file not shown.
BIN
codex-launcher_3.10.11_all.deb
Normal file
BIN
codex-launcher_3.10.11_all.deb
Normal file
Binary file not shown.
Binary file not shown.
@@ -3,11 +3,11 @@ set -e
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/codex-launcher_3.10.9_all.deb" ]; then
|
if [ -f "$SCRIPT_DIR/codex-launcher_3.10.11_all.deb" ]; then
|
||||||
echo "Installing codex-launcher_3.10.9_all.deb ..."
|
echo "Installing codex-launcher_3.10.11_all.deb ..."
|
||||||
sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.10.9_all.deb"
|
sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.10.11_all.deb"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Installed v3.10.9 via .deb package."
|
echo "Installed v3.10.11 via .deb package."
|
||||||
echo " translate-proxy.py -> /usr/bin/translate-proxy.py"
|
echo " translate-proxy.py -> /usr/bin/translate-proxy.py"
|
||||||
echo " codex-launcher-gui -> /usr/bin/codex-launcher-gui"
|
echo " codex-launcher-gui -> /usr/bin/codex-launcher-gui"
|
||||||
echo " cleanup-codex-stale -> /usr/bin/cleanup-codex-stale.sh"
|
echo " cleanup-codex-stale -> /usr/bin/cleanup-codex-stale.sh"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -83,6 +83,21 @@ model_catalog_json = ""
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
("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", [
|
("3.10.9", "2026-05-25", [
|
||||||
"Antigravity: production-only endpoints (cloudcode-pa.googleapis.com), sandbox blocked unless ALLOW_ANTIGRAVITY_STAGING=1",
|
"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)",
|
"Antigravity: 403 SERVICE_DISABLED falls through, 429 returns to client (no sandbox fallback)",
|
||||||
|
|||||||
@@ -4310,13 +4310,19 @@ def _antigravity_normalize_context(input_data):
|
|||||||
if os.environ.get("ANTIGRAVITY_AUTO_RESET_POLLUTED_CONTEXT", "1") != "1":
|
if os.environ.get("ANTIGRAVITY_AUTO_RESET_POLLUTED_CONTEXT", "1") != "1":
|
||||||
auto_reset = False
|
auto_reset = False
|
||||||
|
|
||||||
if is_simple and (auto_reset or n_tool_outputs == 0):
|
has_compaction_summary = any(
|
||||||
|
isinstance(it, dict) and it.get("type") == "message" and it.get("role") == "user"
|
||||||
|
and ("Auto-compacted" in str(it.get("content", "")) or "auto-compacted" in str(it.get("content", "")).lower())
|
||||||
|
for it in input_data
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_simple and auto_reset and not has_compaction_summary:
|
||||||
system_items = [it for it in input_data if isinstance(it, dict) and it.get("type") == "message" and it.get("role") in ("developer", "system")]
|
system_items = [it for it in input_data if isinstance(it, dict) and it.get("type") == "message" and it.get("role") in ("developer", "system")]
|
||||||
user_item = input_data[latest_user_idx]
|
user_item = input_data[latest_user_idx]
|
||||||
result = system_items + [user_item] if system_items else [user_item]
|
result = system_items + [user_item] if system_items else [user_item]
|
||||||
print(f"[antigravity-context] raw_items={n_raw} compacted_items={n_raw} final_items={len(result)}", file=sys.stderr)
|
print(f"[antigravity-context] raw_items={n_raw} compacted_items={n_raw} final_items={len(result)}", file=sys.stderr)
|
||||||
print(f"[antigravity-context] raw_tool_outputs={n_tool_outputs} kept_tool_outputs=0", file=sys.stderr)
|
print(f"[antigravity-context] raw_tool_outputs={n_tool_outputs} kept_tool_outputs=0", file=sys.stderr)
|
||||||
print(f"[antigravity-context] simple_latest_user=true auto_reset={auto_reset}", file=sys.stderr)
|
print(f"[antigravity-context] simple_latest_user=true auto_reset={auto_reset} has_compaction={has_compaction_summary}", file=sys.stderr)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
dev_messages = []
|
dev_messages = []
|
||||||
@@ -4357,6 +4363,22 @@ def _antigravity_normalize_context(input_data):
|
|||||||
tail_start = max(0, len(recent_items) - 6)
|
tail_start = max(0, len(recent_items) - 6)
|
||||||
recent_tail = recent_items[tail_start:]
|
recent_tail = recent_items[tail_start:]
|
||||||
|
|
||||||
|
deduped_tail = []
|
||||||
|
seen_goal_context = False
|
||||||
|
for idx, msg_item in recent_tail:
|
||||||
|
content_str = ""
|
||||||
|
c = msg_item.get("content", "")
|
||||||
|
if isinstance(c, str):
|
||||||
|
content_str = c
|
||||||
|
elif isinstance(c, list):
|
||||||
|
content_str = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict))
|
||||||
|
if "<goal_context>" in content_str:
|
||||||
|
if seen_goal_context:
|
||||||
|
continue
|
||||||
|
seen_goal_context = True
|
||||||
|
deduped_tail.append((idx, msg_item))
|
||||||
|
recent_tail = deduped_tail if deduped_tail else recent_tail
|
||||||
|
|
||||||
tool_call_ids = set()
|
tool_call_ids = set()
|
||||||
for _, t_item in kept_tools:
|
for _, t_item in kept_tools:
|
||||||
cid = t_item.get("call_id", t_item.get("id", ""))
|
cid = t_item.get("call_id", t_item.get("id", ""))
|
||||||
@@ -4371,6 +4393,15 @@ def _antigravity_normalize_context(input_data):
|
|||||||
|
|
||||||
result = list(dev_messages)
|
result = list(dev_messages)
|
||||||
|
|
||||||
|
compaction_summaries = []
|
||||||
|
for idx, msg_item in recent_items:
|
||||||
|
if msg_item is input_data[latest_user_idx]:
|
||||||
|
continue
|
||||||
|
c = msg_item.get("content", "")
|
||||||
|
content_str = c if isinstance(c, str) else " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict)) if isinstance(c, list) else ""
|
||||||
|
if "Auto-compacted" in content_str or "auto-compacted" in content_str.lower():
|
||||||
|
compaction_summaries.append(msg_item)
|
||||||
|
|
||||||
if n_summarized > 0:
|
if n_summarized > 0:
|
||||||
summary_text = f"[Tool history summary: {n_summarized} older tool outputs omitted. {n_tool_calls} prior function calls were made for file inspection/editing.]"
|
summary_text = f"[Tool history summary: {n_summarized} older tool outputs omitted. {n_tool_calls} prior function calls were made for file inspection/editing.]"
|
||||||
result.append({"type": "message", "role": "user", "content": [{"type": "input_text", "text": summary_text}]})
|
result.append({"type": "message", "role": "user", "content": [{"type": "input_text", "text": summary_text}]})
|
||||||
@@ -4381,23 +4412,26 @@ def _antigravity_normalize_context(input_data):
|
|||||||
for _, tool_item in kept_tools:
|
for _, tool_item in kept_tools:
|
||||||
result.append(tool_item)
|
result.append(tool_item)
|
||||||
|
|
||||||
|
for cs_item in compaction_summaries:
|
||||||
|
result.append(cs_item)
|
||||||
|
|
||||||
for _, msg_item in recent_tail:
|
for _, msg_item in recent_tail:
|
||||||
if msg_item is not input_data[latest_user_idx]:
|
if msg_item is not input_data[latest_user_idx]:
|
||||||
result.append(msg_item)
|
result.append(msg_item)
|
||||||
|
|
||||||
latest_hash = hashlib.sha256(" ".join(latest_user.strip().split()).encode()).hexdigest()
|
latest_norm = " ".join(latest_user.strip().split())[:200].lower()
|
||||||
already_present = False
|
already_present = False
|
||||||
for r in result:
|
for r in result:
|
||||||
if isinstance(r, dict) and r.get("type") == "message" and r.get("role") == "user":
|
if isinstance(r, dict) and r.get("type") == "message" and r.get("role") == "user":
|
||||||
c = r.get("content", "")
|
c = r.get("content", "")
|
||||||
if isinstance(c, str):
|
if isinstance(c, str):
|
||||||
rh = hashlib.sha256(" ".join(c.strip().split()).encode()).hexdigest()
|
rn = " ".join(c.strip().split())[:200].lower()
|
||||||
elif isinstance(c, list):
|
elif isinstance(c, list):
|
||||||
combined = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict))
|
combined = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict))
|
||||||
rh = hashlib.sha256(" ".join(combined.strip().split()).encode()).hexdigest()
|
rn = " ".join(combined.strip().split())[:200].lower()
|
||||||
else:
|
else:
|
||||||
rh = ""
|
rn = ""
|
||||||
if rh == latest_hash:
|
if rn == latest_norm:
|
||||||
already_present = True
|
already_present = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -4408,7 +4442,10 @@ def _antigravity_normalize_context(input_data):
|
|||||||
|
|
||||||
if total_chars > _ANTIGRAVITY_EMERGENCY_CHARS:
|
if total_chars > _ANTIGRAVITY_EMERGENCY_CHARS:
|
||||||
print(f"[antigravity-context] EMERGENCY: {total_chars} chars exceeds limit, resetting to minimal", file=sys.stderr)
|
print(f"[antigravity-context] EMERGENCY: {total_chars} chars exceeds limit, resetting to minimal", file=sys.stderr)
|
||||||
result = list(dev_messages) + [input_data[latest_user_idx]]
|
result = list(dev_messages)
|
||||||
|
if compaction_summaries:
|
||||||
|
result.extend(compaction_summaries)
|
||||||
|
result.append(input_data[latest_user_idx])
|
||||||
total_chars = sum(len(json.dumps(it, ensure_ascii=False)) for it in result)
|
total_chars = sum(len(json.dumps(it, ensure_ascii=False)) for it in result)
|
||||||
|
|
||||||
while len(result) > _ANTIGRAVITY_MAX_CONTENTS and total_chars > _ANTIGRAVITY_SOFT_CHARS:
|
while len(result) > _ANTIGRAVITY_MAX_CONTENTS and total_chars > _ANTIGRAVITY_SOFT_CHARS:
|
||||||
@@ -5034,7 +5071,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
_allow_staging = os.environ.get("ALLOW_ANTIGRAVITY_STAGING", "0") == "1"
|
_allow_staging = os.environ.get("ALLOW_ANTIGRAVITY_STAGING", "0") == "1"
|
||||||
if OAUTH_PROVIDER == "google-antigravity":
|
if OAUTH_PROVIDER == "google-antigravity":
|
||||||
_antigravity_endpoints = ["https://cloudcode-pa.googleapis.com"]
|
_antigravity_endpoints = [
|
||||||
|
"https://cloudcode-pa.googleapis.com",
|
||||||
|
"https://daily-cloudcode-pa.googleapis.com",
|
||||||
|
]
|
||||||
if _allow_staging:
|
if _allow_staging:
|
||||||
_antigravity_endpoints.extend([
|
_antigravity_endpoints.extend([
|
||||||
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
||||||
@@ -5091,14 +5131,16 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
if e.code == 403 and "SERVICE_DISABLED" in err_body[:500] and ep != endpoints[-1]:
|
if e.code == 403 and "SERVICE_DISABLED" in err_body[:500] and ep != endpoints[-1]:
|
||||||
print(f"[{self._session_id}] {ep} SERVICE_DISABLED, trying next endpoint", file=sys.stderr)
|
print(f"[{self._session_id}] {ep} SERVICE_DISABLED, trying next endpoint", file=sys.stderr)
|
||||||
continue
|
continue
|
||||||
if e.code == 429 and ep != endpoints[-1] and _allow_staging:
|
if e.code == 429 and OAUTH_PROVIDER.startswith("google"):
|
||||||
print(f"[{self._session_id}] {ep} HTTP 429, trying next endpoint", file=sys.stderr)
|
print(f"[{self._session_id}] 429 from {ep}, body: {err_body[:300]}", file=sys.stderr)
|
||||||
continue
|
if ep != endpoints[-1]:
|
||||||
if e.code == 429:
|
print(f"[{self._session_id}] {ep} HTTP 429, trying fallback endpoint", file=sys.stderr)
|
||||||
|
continue
|
||||||
pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool
|
pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool
|
||||||
_, acct = _get_google_account(OAUTH_PROVIDER)
|
_, acct = _get_google_account(OAUTH_PROVIDER)
|
||||||
if acct:
|
if acct:
|
||||||
pool.mark_rate_limited(acct, 60)
|
pool.mark_rate_limited(acct, 60)
|
||||||
|
print(f"[{self._session_id}] all endpoints returned 429", file=sys.stderr)
|
||||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if ep == endpoints[-1]:
|
if ep == endpoints[-1]:
|
||||||
|
|||||||
@@ -4385,19 +4385,19 @@ def _antigravity_normalize_context(input_data):
|
|||||||
if msg_item is not input_data[latest_user_idx]:
|
if msg_item is not input_data[latest_user_idx]:
|
||||||
result.append(msg_item)
|
result.append(msg_item)
|
||||||
|
|
||||||
latest_hash = hashlib.sha256(" ".join(latest_user.strip().split()).encode()).hexdigest()
|
latest_norm = " ".join(latest_user.strip().split())[:200].lower()
|
||||||
already_present = False
|
already_present = False
|
||||||
for r in result:
|
for r in result:
|
||||||
if isinstance(r, dict) and r.get("type") == "message" and r.get("role") == "user":
|
if isinstance(r, dict) and r.get("type") == "message" and r.get("role") == "user":
|
||||||
c = r.get("content", "")
|
c = r.get("content", "")
|
||||||
if isinstance(c, str):
|
if isinstance(c, str):
|
||||||
rh = hashlib.sha256(" ".join(c.strip().split()).encode()).hexdigest()
|
rn = " ".join(c.strip().split())[:200].lower()
|
||||||
elif isinstance(c, list):
|
elif isinstance(c, list):
|
||||||
combined = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict))
|
combined = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict))
|
||||||
rh = hashlib.sha256(" ".join(combined.strip().split()).encode()).hexdigest()
|
rn = " ".join(combined.strip().split())[:200].lower()
|
||||||
else:
|
else:
|
||||||
rh = ""
|
rn = ""
|
||||||
if rh == latest_hash:
|
if rn == latest_norm:
|
||||||
already_present = True
|
already_present = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user