v3.10.7 — Prompt Enhancer: offline + AI-powered modes, per-provider toggle

This commit is contained in:
Roman | RyzenAdvanced
2026-05-25 21:04:30 +04:00
Unverified
parent 869a2625fc
commit f3f536e428
8 changed files with 239 additions and 10 deletions

View File

@@ -1,5 +1,21 @@
# Changelog
## v3.10.7 (2026-05-25)
**Prompt Enhancer — Fix Lost Context After Compaction**
### Prompt Enhancer (Per-Provider Toggle)
- **Offline mode**: Injects structured XML instructions before every user prompt to keep the model focused, decisive, and context-aware after compaction strips conversation history
- **AI-powered mode**: Optionally calls an external LLM (configurable model/URL/key) to rewrite vague prompts into clear, actionable instructions
- Prevents the "had to resend and reword" problem in long sessions where compaction summarizes hundreds of turns
- **Per-endpoint setting** — enable/disable for each provider independently
- Configurable in both Linux and Windows GUI: toggle switch, mode selector, enhancer model, URL, API key fields
### How It Works
- **Offline**: Prepends a `<prompt-enhancer>` block with rules like "never ask for clarification, infer from compacted context, execute decisively"
- **AI-powered**: Sends the user's prompt + compaction summary to a separate model (e.g. DeepSeek V4 Flash via Freebuff) which rewrites it for clarity, then prepends the offline instructions too
- Both modes run after compaction but before the request is sent upstream
## v3.10.6 (2026-05-25)
**Freebuff Integration + Codebuff OAuth Fix + Windows Consolidation**

Binary file not shown.

Binary file not shown.

View File

@@ -3,11 +3,11 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -f "$SCRIPT_DIR/codex-launcher_3.10.6_all.deb" ]; then
echo "Installing codex-launcher_3.10.6_all.deb ..."
sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.10.6_all.deb"
if [ -f "$SCRIPT_DIR/codex-launcher_3.10.7_all.deb" ]; then
echo "Installing codex-launcher_3.10.7_all.deb ..."
sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.10.7_all.deb"
echo ""
echo "Installed v3.10.6 via .deb package."
echo "Installed v3.10.7 via .deb package."
echo " translate-proxy.py -> /usr/bin/translate-proxy.py"
echo " codex-launcher-gui -> /usr/bin/codex-launcher-gui"
echo " cleanup-codex-stale -> /usr/bin/cleanup-codex-stale.sh"

View File

@@ -1798,7 +1798,7 @@ class LauncherWin(Gtk.Window):
# header row
hdr = Gtk.Box(spacing=8)
vbox.pack_start(hdr, False, False, 0)
lbl = Gtk.Label(label="<b>Codex Launcher v3.10.6</b>")
lbl = Gtk.Label(label="<b>Codex Launcher v3.10.7</b>")
lbl.set_use_markup(True)
hdr.pack_start(lbl, False, False, 0)
changelog_btn = Gtk.Button(label="Changelog")
@@ -2925,7 +2925,7 @@ class LauncherWin(Gtk.Window):
fp_id = str(uuid.uuid4())
body = json.dumps({"fingerprintId": fp_id}).encode()
req = urllib.request.Request("https://www.codebuff.com/api/auth/cli/code",
data=body, headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.6"})
data=body, headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.7"})
resp = urllib.request.urlopen(req, timeout=30)
rdata = json.loads(resp.read())
login_url = rdata.get("loginUrl", "") or rdata.get("login_url", "")
@@ -2944,7 +2944,7 @@ class LauncherWin(Gtk.Window):
while time.time() < deadline:
time.sleep(2)
try:
pr = urllib.request.Request(poll, headers={"User-Agent": "codex-launcher/3.10.6"})
pr = urllib.request.Request(poll, headers={"User-Agent": "codex-launcher/3.10.7"})
pd = json.loads(urllib.request.urlopen(pr, timeout=10).read())
if pd.get("user", {}).get("authToken"):
result["success"] = True
@@ -3503,6 +3503,35 @@ class EditEndpointDialog(Gtk.Dialog):
add_row(7, "Effort:", self._combo_effort)
self._on_reasoning_toggled()
enhancer_box = Gtk.Box(spacing=6)
self._switch_enhancer = Gtk.Switch()
self._switch_enhancer.set_active(self._data.get("prompt_enhancer", False))
enhancer_box.pack_start(self._switch_enhancer, False, False, 0)
enhancer_box.pack_start(Gtk.Label(label="Prompt Enhancer"), False, False, 0)
self._combo_enhancer_mode = Gtk.ComboBoxText()
for mode in ["offline", "ai-powered"]:
self._combo_enhancer_mode.append(mode, mode.capitalize())
self._combo_enhancer_mode.set_active_id(self._data.get("prompt_enhancer_mode", "offline"))
enhancer_box.pack_start(self._combo_enhancer_mode, False, False, 6)
add_row(8, "Enhance:", enhancer_box)
self._entry_enhancer_model = Gtk.Entry()
self._entry_enhancer_model.set_placeholder_text("e.g. deepseek/deepseek-v4-flash (ai-powered mode only)")
self._entry_enhancer_model.set_text(self._data.get("prompt_enhancer_model", ""))
add_row(9, "Enhancer Model:", self._entry_enhancer_model)
self._entry_enhancer_url = Gtk.Entry()
self._entry_enhancer_url.set_placeholder_text("e.g. https://www.codebuff.com/api/v1 (ai-powered mode only)")
self._entry_enhancer_url.set_text(self._data.get("prompt_enhancer_url", ""))
add_row(10, "Enhancer URL:", self._entry_enhancer_url)
self._entry_enhancer_key = Gtk.Entry()
self._entry_enhancer_key.set_placeholder_text("API key for enhancer model (ai-powered mode only)")
self._entry_enhancer_key.set_text(self._data.get("prompt_enhancer_key", ""))
self._entry_enhancer_key.set_visibility(False)
self._entry_enhancer_key.set_invisible_char("*")
add_row(11, "Enhancer Key:", self._entry_enhancer_key)
# Models
mlbl = Gtk.Label(label="Models:", xalign=0)
area.pack_start(mlbl, False, False, 4)
@@ -3971,7 +4000,7 @@ class EditEndpointDialog(Gtk.Dialog):
auth_url = "https://www.codebuff.com/api/auth/cli/code"
body = json.dumps({"fingerprintId": fingerprint_id}).encode()
req = urllib.request.Request(auth_url, data=body,
headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.6"})
headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.7"})
resp = urllib.request.urlopen(req, timeout=30)
data = json.loads(resp.read())
login_url = data.get("loginUrl", "") or data.get("login_url", "")
@@ -3996,7 +4025,7 @@ class EditEndpointDialog(Gtk.Dialog):
time.sleep(2)
try:
poll_req = urllib.request.Request(poll_url,
headers={"User-Agent": "codex-launcher/3.10.6"})
headers={"User-Agent": "codex-launcher/3.10.7"})
poll_resp = urllib.request.urlopen(poll_req, timeout=10)
poll_data = json.loads(poll_resp.read())
user = poll_data.get("user")
@@ -4195,6 +4224,17 @@ class EditEndpointDialog(Gtk.Dialog):
new_ep["cc_version"] = cc_ver
new_ep["reasoning_enabled"] = self._switch_reasoning.get_active()
new_ep["reasoning_effort"] = self._combo_effort.get_active_id() or "medium"
new_ep["prompt_enhancer"] = self._switch_enhancer.get_active()
new_ep["prompt_enhancer_mode"] = self._combo_enhancer_mode.get_active_id() or "offline"
enh_model = self._entry_enhancer_model.get_text().strip()
enh_url = self._entry_enhancer_url.get_text().strip()
enh_key = self._entry_enhancer_key.get_text().strip()
if enh_model:
new_ep["prompt_enhancer_model"] = enh_model
if enh_url:
new_ep["prompt_enhancer_url"] = enh_url
if enh_key:
new_ep["prompt_enhancer_key"] = enh_key
preset_name = self._combo_preset.get_active_text() or "Custom"
preset = PROVIDER_PRESETS.get(preset_name, {})
if preset.get("oauth_provider"):

View File

@@ -225,6 +225,26 @@ class EditEndpointDialog:
add_field("Reasoning:", lambda: reason_frame)
self._on_reasoning_toggled()
enhancer_frame = ttk.Frame(grid)
self._enhancer_var = tk.BooleanVar(value=self._data.get("prompt_enhancer", False))
ttk.Checkbutton(enhancer_frame, text="Prompt Enhancer", variable=self._enhancer_var).pack(side="left")
self._enhancer_mode = ttk.Combobox(enhancer_frame, values=["offline", "ai-powered"], state="readonly", width=10)
self._enhancer_mode.set(self._data.get("prompt_enhancer_mode", "offline"))
self._enhancer_mode.pack(side="left", padx=(8, 0))
add_field("Enhancer:", lambda: enhancer_frame)
self._entry_enhancer_model = ttk.Entry(grid)
self._entry_enhancer_model.insert(0, self._data.get("prompt_enhancer_model", ""))
add_field("Enhancer Model:", lambda: self._entry_enhancer_model)
self._entry_enhancer_url = ttk.Entry(grid)
self._entry_enhancer_url.insert(0, self._data.get("prompt_enhancer_url", ""))
add_field("Enhancer URL:", lambda: self._entry_enhancer_url)
self._entry_enhancer_key = ttk.Entry(grid, show="*")
self._entry_enhancer_key.insert(0, self._data.get("prompt_enhancer_key", ""))
add_field("Enhancer Key:", lambda: self._entry_enhancer_key)
grid.columnconfigure(1, weight=1)
ttk.Label(main, text="Models:").pack(anchor="w", pady=(8, 2))
@@ -713,10 +733,21 @@ class EditEndpointDialog:
"provider_preset": self._combo_preset.get() or "Custom",
"reasoning_enabled": self._reason_var.get(),
"reasoning_effort": self._combo_effort.get() or "medium",
"prompt_enhancer": self._enhancer_var.get(),
"prompt_enhancer_mode": self._enhancer_mode.get() or "offline",
}
cc_ver = self._entry_cc_ver.get().strip()
if cc_ver:
new_ep["cc_version"] = cc_ver
enh_model = self._entry_enhancer_model.get().strip()
enh_url = self._entry_enhancer_url.get().strip()
enh_key = self._entry_enhancer_key.get().strip()
if enh_model:
new_ep["prompt_enhancer_model"] = enh_model
if enh_url:
new_ep["prompt_enhancer_url"] = enh_url
if enh_key:
new_ep["prompt_enhancer_key"] = enh_key
preset_name = self._combo_preset.get() or "Custom"
preset = PROVIDER_PRESETS.get(preset_name, {})
if preset.get("oauth_provider"):

View File

@@ -83,6 +83,14 @@ model_catalog_json = ""
"""
CHANGELOG = [
("3.10.7", "2026-05-25", [
"Prompt Enhancer: per-provider toggle to improve prompt clarity after compaction",
"Two modes: offline (template injection) and ai-powered (external LLM rewrites)",
"Offline mode: injects structured instructions to keep model focused post-compaction",
"AI-powered mode: uses configurable model/URL/key to rewrite prompts for clarity",
"Linux/Windows GUI: Prompt Enhancer switch + mode selector + model/URL/key fields",
"Prevents lost context issues in long sessions with aggressive compaction",
]),
("3.10.6", "2026-05-25", [
"Freebuff integration: free DeepSeek/Kimi via codebuff.com API",
"Fixed Freebuff User-Agent to match official SDK (ai-sdk/openai-compatible/1.0.25/codebuff)",

View File

@@ -247,6 +247,11 @@ REASONING_ENABLED = True
REASONING_EFFORT = "medium"
FORCE_MODEL = ""
BGP_ROUTES = []
PROMPT_ENHANCER = False
PROMPT_ENHANCER_MODE = "offline"
PROMPT_ENHANCER_MODEL = ""
PROMPT_ENHANCER_URL = ""
PROMPT_ENHANCER_KEY = ""
SERVER = None
if _IS_WINDOWS:
@@ -769,7 +774,7 @@ def _ensure_antigravity_version():
def _init_runtime():
global CONFIG, PORT, BACKEND, TARGET_URL, API_KEY, OAUTH_PROVIDER, _antigravity_version
global MODELS, CC_VERSION, REASONING_ENABLED, REASONING_EFFORT, BGP_ROUTES
global _api_key_pool
global _api_key_pool, PROMPT_ENHANCER
CONFIG = load_config()
PORT = CONFIG["port"]
@@ -782,6 +787,11 @@ def _init_runtime():
REASONING_ENABLED = CONFIG.get("reasoning_enabled", True)
REASONING_EFFORT = CONFIG.get("reasoning_effort", "medium")
FORCE_MODEL = (CONFIG.get("force_model") or "").strip()
PROMPT_ENHANCER = CONFIG.get("prompt_enhancer", False)
PROMPT_ENHANCER_MODE = CONFIG.get("prompt_enhancer_mode", "offline")
PROMPT_ENHANCER_MODEL = CONFIG.get("prompt_enhancer_model", "")
PROMPT_ENHANCER_URL = CONFIG.get("prompt_enhancer_url", "")
PROMPT_ENHANCER_KEY = CONFIG.get("prompt_enhancer_key", "")
BGP_ROUTES = CONFIG.get("bgp_routes", [])
_api_key_pool = None
if API_KEY and "," in API_KEY and not OAUTH_PROVIDER.startswith("google") and BACKEND not in ("codebuff", "freebuff"):
@@ -1694,6 +1704,120 @@ def _adaptive_compact(input_data, model, policy=None):
f"items {len(input_data)}->{len(head)+1+len(tail)}", file=sys.stderr)
return head + [summary_msg] + tail, True
# ═══════════════════════════════════════════════════════════════════
# Prompt Enhancer
# ═══════════════════════════════════════════════════════════════════
_PROMPT_ENHANCER_SYSTEM = """You are a prompt enhancement assistant for a coding agent (Codex CLI).
Your job: rewrite the user's latest message to be clearer, more specific, and more actionable.
Rules:
- Preserve the user's EXACT intent — never change what they want done
- Add explicit action verbs and step-by-step clarity
- If the message is vague ("fix it", "make it better"), infer context from prior conversation summary and make it specific
- Keep the enhanced prompt concise — no longer than 2x the original
- If the original prompt is already clear and specific, return it unchanged
- Output ONLY the enhanced prompt text, nothing else
- Never add tasks the user didn't ask for"""
_PROMPT_ENHANCER_OFFLINE = """<prompt-enhancer>
<instructions>
You are a coding agent operating inside a context-compacted session. Follow these rules strictly:
1. ACTION CLARITY: Re-read the user's latest message. Identify every explicit and implicit action request. Execute ALL of them — do not skip any.
2. COMPACTED CONTEXT: Previous conversation was summarized. The summary preserves your task history but may lose details. If the user references earlier work ("fix that", "continue", "update it"), infer from the compacted summary what was done and what remains.
3. NO CLARIFICATION ASKING: Never ask "which file?" or "what exactly?" — infer from context. If truly ambiguous, make a reasonable assumption and proceed. The user can correct you.
4. DECISIVE EXECUTION: When the user says "fix", "update", "change", "add", "remove" — do it immediately in the relevant file(s). Do not describe what you would do — actually do it.
5. COMPLETE EDITS: When editing files, make the FULL change requested. Do not partially apply edits or leave placeholders.
6. PRESERVE WORKING STATE: Never break existing functionality. If changing code, keep all surrounding logic intact.
7. MULTI-STEP REQUESTS: If the user asks for multiple things, do ALL of them in sequence. Do not stop after the first one.
</instructions>
</prompt-enhancer>
"""
def _enhance_prompt_llm(text, compaction_summary=""):
global PROMPT_ENHANCER_MODEL, PROMPT_ENHANCER_URL, PROMPT_ENHANCER_KEY
if not PROMPT_ENHANCER_MODEL or not PROMPT_ENHANCER_URL:
return text
try:
messages = [
{"role": "system", "content": _PROMPT_ENHANCER_SYSTEM},
]
if compaction_summary:
messages.append({"role": "user", "content": f"Context from earlier conversation (compacted):\n{compaction_summary[:2000]}"})
messages.append({"role": "user", "content": f"Enhance this prompt:\n{text}"})
body = json.dumps({"model": PROMPT_ENHANCER_MODEL, "messages": messages, "max_tokens": 2000, "temperature": 0.3}).encode()
headers = {"Content-Type": "application/json"}
if PROMPT_ENHANCER_KEY:
headers["Authorization"] = f"Bearer {PROMPT_ENHANCER_KEY}"
req = urllib.request.Request(f"{PROMPT_ENHANCER_URL.rstrip('/')}/chat/completions", data=body, headers=headers)
resp = urllib.request.urlopen(req, timeout=15)
data = json.loads(resp.read())
enhanced = data.get("choices", [{}])[0].get("message", {}).get("content", "").strip()
if enhanced and len(enhanced) >= len(text) * 0.5:
print(f"[prompt-enhancer] AI enhanced: {text[:80]}... -> {enhanced[:80]}...", file=sys.stderr)
return enhanced
except Exception as e:
print(f"[prompt-enhancer] AI enhancement failed: {e}", file=sys.stderr)
return text
def _apply_prompt_enhancer(input_data):
global PROMPT_ENHANCER_MODE
if not isinstance(input_data, list) or len(input_data) == 0:
return input_data
last_user_idx = None
for i in range(len(input_data) - 1, -1, -1):
item = input_data[i]
if isinstance(item, dict) and item.get("type") == "message" and item.get("role") == "user":
last_user_idx = i
break
if last_user_idx is None:
return input_data
item = input_data[last_user_idx]
content = item.get("content", "")
if isinstance(content, list):
text = content[0].get("text", "") if content else ""
elif isinstance(content, str):
text = content
else:
return input_data
if not text or len(text) < 5:
return input_data
if text.startswith("<prompt-enhancer>"):
return input_data
compaction_summary = ""
for it in input_data:
if isinstance(it, dict) and it.get("type") == "message" and it.get("role") == "user":
c = it.get("content", "")
t = ""
if isinstance(c, list):
t = c[0].get("text", "") if c else ""
elif isinstance(c, str):
t = c
if "[Auto-compacted:" in t:
compaction_summary = t[:3000]
break
if PROMPT_ENHANCER_MODE == "ai-powered" and PROMPT_ENHANCER_MODEL and PROMPT_ENHANCER_URL:
enhanced = _enhance_prompt_llm(text, compaction_summary)
else:
enhanced = text
enhanced = _PROMPT_ENHANCER_OFFLINE + enhanced
new_item = dict(item)
if isinstance(item.get("content"), list):
new_item["content"] = [{"type": "input_text", "text": enhanced}]
else:
new_item["content"] = enhanced
result = list(input_data)
result[last_user_idx] = new_item
print(f"[prompt-enhancer] mode={PROMPT_ENHANCER_MODE} enhanced last user message ({len(text)}->{len(enhanced)} chars)", file=sys.stderr)
return result
# ═══════════════════════════════════════════════════════════════════
# Tool-call pairing validator
# ═══════════════════════════════════════════════════════════════════
@@ -4294,6 +4418,11 @@ class Handler(http.server.BaseHTTPRequestHandler):
body = dict(body)
body["input"] = input_data
if PROMPT_ENHANCER and isinstance(input_data, list):
input_data = _apply_prompt_enhancer(input_data)
body = dict(body)
body["input"] = input_data
crof_limit = _crof_item_limit(model)
_crof_eligible = TARGET_URL and "crof.ai" in TARGET_URL
if _crof_eligible and not compacted and isinstance(input_data, list) and len(input_data) > crof_limit:
@@ -4467,6 +4596,11 @@ class Handler(http.server.BaseHTTPRequestHandler):
body = dict(body)
body["input"] = input_data
if PROMPT_ENHANCER and isinstance(input_data, list):
input_data = _apply_prompt_enhancer(input_data)
body = dict(body)
body["input"] = input_data
access_token = _refresh_oauth_token()
token_name = "google-antigravity-oauth-token.json" if OAUTH_PROVIDER == "google-antigravity" else "google-cli-oauth-token.json"
token_path = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy", token_name)