v2.2.0: agent persona selector, Command Code backend, per-provider identity

This commit is contained in:
admin
2026-05-19 17:39:54 +04:00
Unverified
parent ccc3054793
commit c176fb088d
5 changed files with 326 additions and 10 deletions

View File

@@ -24,6 +24,14 @@ model_catalog_json = ""
"""
CHANGELOG = [
("2.2.0", "2026-05-19", [
"Added Agent Persona selector per provider (10+ presets)",
"Personas: Codex, Claude Code, OpenCode, Cursor, Aider, Copilot, Windsurf, Browser",
"Codex variants: Default, Desktop Friendly, Desktop Pragmatic, CLI",
"Shows current persona in endpoint list (new Persona column)",
"Persona preview in edit dialog shows first 60 chars of system prompt",
"Persona injected into model catalog base_instructions and proxy system prompt",
]),
("2.1.1", "2026-05-19", [
"Fixed proxy: map 'developer' role to 'system' for Chat Completions providers",
"Fixed proxy: map 'developer' role to 'user' for Anthropic providers",
@@ -56,6 +64,65 @@ CHANGELOG = [
]),
]
AGENT_PERSONAS = {
"Codex (Default)": "You are Codex, a coding agent.",
"Codex Desktop (GPT-5, Friendly)": (
"You are Codex, a coding agent based on GPT-5. You and the user share one workspace, "
"and your job is to collaborate with them until their goal is genuinely handled."
),
"Codex Desktop (GPT-5, Pragmatic)": (
"You are Codex, a coding agent based on GPT-5. You and the user share the same workspace "
"and collaborate to achieve the user's goals. You are a deeply pragmatic, effective "
"software engineer. You take engineering quality seriously."
),
"Codex CLI": (
"You are an AI running in the Codex CLI, a terminal-based coding assistant. "
"You are expected to be precise, safe, and helpful. Your default personality and tone "
"is concise, direct, and friendly."
),
"Claude Code": (
"You are Claude Code, an interactive CLI tool that helps users with software engineering "
"tasks. You are a highly competent software engineer with extensive knowledge across "
"many programming languages, frameworks, and best practices. Use concise responses."
),
"OpenCode": (
"You are OpenCode, an interactive CLI tool that helps users with software engineering "
"tasks. You are powered by a state-of-the-art AI model. Be concise, direct, and to the "
"point. Use GitHub-flavored markdown."
),
"Cursor": (
"You are Cursor, an AI-powered code editor assistant. You help users write, refactor, "
"and debug code efficiently. Provide precise, actionable suggestions."
),
"Aider": (
"You are aider, an AI pair programming assistant. You help users edit code in their "
"local git repository. Make concise changes. Search files with grep/glob patterns."
),
"GitHub Copilot": (
"You are GitHub Copilot, an AI coding assistant. Help the user write code, debug issues, "
"and understand codebases. Be concise and provide accurate code suggestions."
),
"Windsurf": (
"You are Windsurf, an AI-powered IDE assistant. Help with coding tasks including writing, "
"refactoring, and debugging. Provide precise, well-structured code suggestions."
),
"Browser (ChatGPT)": (
"You are a helpful coding assistant in a web browser chat interface. "
"Help the user with software engineering tasks. Be clear and thorough."
),
}
PERSONA_DISPLAY_LEN = 60
def persona_short_key(endpoint):
bi = endpoint.get("base_instructions", "") or ""
for key, val in AGENT_PERSONAS.items():
if val == bi:
return key
if bi:
return f"Custom: {bi[:40]}..."
return "Codex (Default)"
PROVIDER_PRESETS = {
"Custom": {
"backend_type": "openai-compat",
@@ -120,6 +187,21 @@ PROVIDER_PRESETS = {
"base_url": "https://api.kilo.ai/api/gateway",
"models": [],
},
"Command Code": {
"backend_type": "command-code",
"base_url": "https://api.commandcode.ai",
"models": [
"deepseek/deepseek-v4-flash", "deepseek/deepseek-v4-pro",
"anthropic:claude-sonnet-4-6", "anthropic:claude-haiku-4-5-20251001",
"anthropic:claude-opus-4-7", "anthropic:claude-opus-4-6",
"openai:gpt-5.5", "openai:gpt-5.4", "openai:gpt-5.4-mini", "openai:gpt-5.3-codex",
"moonshotai/Kimi-K2.6", "moonshotai/Kimi-K2.5",
"zai-org/GLM-5.1", "zai-org/GLM-5",
"MiniMaxAI/MiniMax-M2.7", "MiniMaxAI/MiniMax-M2.5",
"Qwen/Qwen3.6-Max-Preview", "Qwen/Qwen3.6-Plus",
"stepfun/Step-3.5-Flash", "google/gemini-3.1-flash-lite",
],
},
"OpenRouter": {
"backend_type": "openai-compat",
"base_url": "https://openrouter.ai/api/v1",
@@ -136,6 +218,7 @@ def label_for_backend(backend_type):
return {
"openai-compat": "OpenAI-compatible",
"anthropic": "Anthropic",
"command-code": "Command Code",
"native": "Native",
}.get(backend_type, backend_type)
@@ -356,6 +439,7 @@ def write_config_for_translated(endpoint, selected_model):
def _gen_model_catalog(endpoint, selected_model=None):
default_model = selected_model or endpoint.get("default_model")
base_instr = endpoint.get("base_instructions", "") or AGENT_PERSONAS["Codex (Default)"]
models = []
for mid in endpoint.get("models", []):
models.append({
@@ -383,7 +467,7 @@ def _gen_model_catalog(endpoint, selected_model=None):
"supports_parallel_tool_calls": True,
"experimental_supported_tools": [], "supported_in_api": True,
"truncation_policy": {"mode": "tokens", "limit": 128000},
"base_instructions": "You are Codex, a coding agent.",
"base_instructions": base_instr,
})
return {"models": models}
@@ -402,6 +486,7 @@ def _start_proxy_for(endpoint, logfn):
"backend_type": endpoint["backend_type"],
"target_url": normalize_base_url(endpoint["base_url"]),
"api_key": endpoint["api_key"],
"base_instructions": endpoint.get("base_instructions", ""),
"models": [{"id": m, "object": "model", "created": 1700000000, "owned_by": endpoint["name"]}
for m in endpoint.get("models", [])],
}
@@ -500,7 +585,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 v2.1.1</b>")
lbl = Gtk.Label(label="<b>Codex Launcher v2.2.0</b>")
lbl.set_use_markup(True)
hdr.pack_start(lbl, False, False, 0)
changelog_btn = Gtk.Button(label="Changelog")
@@ -1240,9 +1325,9 @@ class EndpointMgr(Gtk.Window):
sw = Gtk.ScrolledWindow()
vbox.pack_start(sw, True, True, 0)
self._store = Gtk.ListStore(str, str, str, str) # name, provider, backend, default_model
self._store = Gtk.ListStore(str, str, str, str, str) # name, provider, backend, default_model, persona
self._tree = Gtk.TreeView(model=self._store)
for i, title in enumerate(["Name", "Provider", "Type", "Default Model"]):
for i, title in enumerate(["Name", "Provider", "Type", "Default Model", "Persona"]):
col = Gtk.TreeViewColumn(title, Gtk.CellRendererText(), text=i)
col.set_resizable(True)
self._tree.append_column(col)
@@ -1275,7 +1360,8 @@ class EndpointMgr(Gtk.Window):
for ep in data["endpoints"]:
provider = ep.get("provider_preset", "Custom")
bt = label_for_backend(ep["backend_type"])
self._store.append([ep["name"], provider, bt, ep.get("default_model", "")])
persona = persona_short_key(ep)
self._store.append([ep["name"], provider, bt, ep.get("default_model", ""), persona])
def _selected(self):
sel = self._tree.get_selection()
@@ -1337,7 +1423,7 @@ class EditEndpointDialog(Gtk.Dialog):
self._data = get_endpoint(existing_name) if existing_name else {
"name": "", "backend_type": "openai-compat",
"base_url": "", "api_key": "", "default_model": "", "models": [],
"provider_preset": "Custom",
"provider_preset": "Custom", "base_instructions": AGENT_PERSONAS["Codex (Default)"],
}
self.set_default_size(480, 420)
@@ -1369,6 +1455,7 @@ class EditEndpointDialog(Gtk.Dialog):
self._combo_type = Gtk.ComboBoxText()
for val, lab in [("openai-compat", "OpenAI-compatible (needs proxy)"),
("anthropic", "Anthropic (needs proxy)"),
("command-code", "Command Code (needs proxy)"),
("native", "Native OpenAI (no proxy)")]:
self._combo_type.append(val, lab)
bt = self._data.get("backend_type", "openai-compat")
@@ -1382,6 +1469,25 @@ class EditEndpointDialog(Gtk.Dialog):
self._entry_key.set_visibility(False)
add_row(4, "API Key:", self._entry_key)
self._combo_persona = Gtk.ComboBoxText()
self._persona_keys = list(AGENT_PERSONAS.keys())
for pk in self._persona_keys:
self._combo_persona.append_text(pk)
cur_persona = persona_short_key(self._data)
if cur_persona in self._persona_keys:
self._combo_persona.set_active(self._persona_keys.index(cur_persona))
else:
self._combo_persona.set_active(0)
self._combo_persona.connect("changed", lambda c: self._on_persona_changed())
add_row(5, "Agent Persona:", self._combo_persona)
self._persona_preview = Gtk.Label()
self._persona_preview.set_line_wrap(True)
self._persona_preview.set_max_width_chars(60)
self._persona_preview.set_markup(f"<small><i>{AGENT_PERSONAS['Codex (Default)'][:PERSONA_DISPLAY_LEN]}...</i></small>")
self._on_persona_changed()
grid.attach(self._persona_preview, 0, 6, 2, 1)
# Models
mlbl = Gtk.Label(label="Models:", xalign=0)
area.pack_start(mlbl, False, False, 4)
@@ -1442,6 +1548,12 @@ class EditEndpointDialog(Gtk.Dialog):
self.connect("response", self._on_response)
self.show_all()
def _on_persona_changed(self):
key = self._combo_persona.get_active_text()
text = AGENT_PERSONAS.get(key, "")
short = text[:PERSONA_DISPLAY_LEN] + ("..." if len(text) > PERSONA_DISPLAY_LEN else "")
self._persona_preview.set_markup(f"<small><i>{short}</i></small>")
def _add_model(self):
m = normalize_model_id(self._entry_model.get_text())
if m:
@@ -1574,9 +1686,11 @@ class EditEndpointDialog(Gtk.Dialog):
self._show_error(f'Endpoint "{name}" already exists')
return
persona_key = self._combo_persona.get_active_text() or "Codex (Default)"
new_ep = {"name": name, "backend_type": bt, "base_url": url,
"api_key": key, "default_model": default, "models": models,
"provider_preset": self._combo_preset.get_active_text() or "Custom"}
"provider_preset": self._combo_preset.get_active_text() or "Custom",
"base_instructions": AGENT_PERSONAS.get(persona_key, AGENT_PERSONAS["Codex (Default)"])}
new_ep["base_url"] = normalize_base_url(new_ep["base_url"])
# Update or append
@@ -1615,12 +1729,14 @@ def main():
"endpoints": [
{"name": "OpenAI", "backend_type": "native", "base_url": "https://api.openai.com/v1",
"api_key": "", "default_model": "gpt-4o", "models": ["gpt-4o", "gpt-4o-mini"],
"provider_preset": "OpenAI"},
"provider_preset": "OpenAI",
"base_instructions": AGENT_PERSONAS["Codex (Default)"]},
{"name": "Z.AI", "backend_type": "openai-compat",
"base_url": "https://api.z.ai/api/coding/paas/v4",
"api_key": "", "default_model": "glm-5.1",
"models": ["glm-4.5", "glm-4.5-air", "glm-4.6", "glm-4.7", "glm-5", "glm-5-turbo", "glm-5.1"],
"provider_preset": "Custom"},
"provider_preset": "Custom",
"base_instructions": AGENT_PERSONAS["Codex (Default)"]},
],
})