v3.11.0: merge cobra PR, smart-continue, hot-reload, XML extraction

- Merge PR #5 from cobra91: concurrency semaphore, auto-continue, SO_REUSEADDR,
  proxy-stderr.log, stream diagnostics, timeout handler, restart proxy fix
- Tool call argument normalizer, smart-continue loop, XML extraction
- API key hot-reload with mtime tracking + /admin/ endpoints
- GUI hot-reload on endpoint edit with upstream verification
- Synthetic tool-results disabled (caused deepseek-v4-pro truncation)
- Version bump 3.10.12 -> 3.11.0, rebuild .deb
This commit is contained in:
Roman | RyzenAdvanced
2026-05-26 15:02:02 +04:00
Unverified
parent cae161023f
commit c3ba3286ff
7 changed files with 1100 additions and 191 deletions

View File

@@ -83,6 +83,19 @@ model_catalog_json = ""
"""
CHANGELOG = [
("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 (2× retries): escalating nudges when model stops text-only mid-task",
"XML tool call extraction: parses <tool_call> 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",
@@ -1468,6 +1481,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):
@@ -1559,11 +1573,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()