Merge origin/main; keep PM as plugin dependency owner
Reconcile plugin declarations and validation through PM's atomic generation publication; preserve external runtimes, target markers, and conflict refusal. Keep one source-update completion owner and port upstream lifecycle changes to the PM desktop/runtime paths.
This commit is contained in:
@@ -221,7 +221,7 @@ class _ModelCatalog:
|
||||
f"Provider: {provider_name}" + (" • current" if is_current else ""),
|
||||
)
|
||||
|
||||
def add_named_catalogs(self, catalogs: list, normalized_provider: str) -> None:
|
||||
def add_named_catalogs(self, catalogs: list, current_choice_provider: str) -> None:
|
||||
"""Named user-defined endpoints (providers: / custom_providers:) are invisible
|
||||
to canonical enumeration — append them like the TUI /model picker. An empty
|
||||
catalog marks that slug authoritative-empty."""
|
||||
@@ -230,7 +230,7 @@ class _ModelCatalog:
|
||||
self.empty_authoritative.add(str(named_slug).strip().lower())
|
||||
continue
|
||||
for named_model, named_desc in named_catalog:
|
||||
is_current = named_slug == normalized_provider and named_model == self.current_model
|
||||
is_current = named_slug.lower() == current_choice_provider and named_model == self.current_model
|
||||
parts = [f"Provider: {named_label}", str(named_desc or "").strip(), "current" if is_current else ""]
|
||||
self.add(named_slug, named_model, named_model, " • ".join(part for part in parts if part))
|
||||
|
||||
@@ -251,13 +251,38 @@ def build_model_state(model: str, provider: str, base_url: str) -> SessionModelS
|
||||
probe_custom_providers=False, probe_current_custom_provider=False, max_models=ACP_MAX_MODELS_PER_PROVIDER,
|
||||
)
|
||||
|
||||
named_catalogs = _named_custom_provider_catalogs()
|
||||
named_slugs = {str(slug).strip().lower() for slug, _label, _models in named_catalogs}
|
||||
current_choice_provider = str(provider or "").strip().lower()
|
||||
current_base = base_url.strip().rstrip("/").lower()
|
||||
# ``build_models_payload`` represents configured ``providers:`` entries by their raw
|
||||
# config key. ACP ids must instead use the durable ``custom:<key>`` identity so the
|
||||
# picker value round-trips through ``parse_model_input``. Only user-defined rows are
|
||||
# replaced by the named catalogs: a ``providers:`` key that shadows a canonical name
|
||||
# (``providers.openrouter:`` → proxy) must leave the canonical row — and a session that
|
||||
# runs on the canonical endpoint — alone, or picking "current" re-routes to the proxy.
|
||||
all_rows = payload.get("providers") or []
|
||||
canonical_current = any(
|
||||
str(r.get("slug") or "").strip().lower() == current_choice_provider and not r.get("is_user_defined")
|
||||
for r in all_rows
|
||||
)
|
||||
inventory_rows: list = []
|
||||
for row in all_rows:
|
||||
slug = str(row.get("slug") or "").strip().lower()
|
||||
if not row.get("is_user_defined") or not {slug, f"custom:{slug}"} & named_slugs:
|
||||
inventory_rows.append(row)
|
||||
continue
|
||||
row_base = str(row.get("api_url") or "").strip().rstrip("/").lower()
|
||||
if slug.removeprefix("custom:") == current_choice_provider and (current_base == row_base or not canonical_current):
|
||||
current_choice_provider = f"custom:{current_choice_provider}"
|
||||
|
||||
cat = _ModelCatalog(
|
||||
normalize_provider=normalize_provider, current_model=model,
|
||||
current_choice_provider=str(provider or "").strip().lower(),
|
||||
current_base_url=base_url.strip().rstrip("/").lower(),
|
||||
current_choice_provider=current_choice_provider,
|
||||
current_base_url=current_base,
|
||||
)
|
||||
cat.add_inventory_rows(payload.get("providers") or [], provider_label)
|
||||
cat.add_named_catalogs(_named_custom_provider_catalogs(), normalized_provider)
|
||||
cat.add_inventory_rows(inventory_rows, provider_label)
|
||||
cat.add_named_catalogs(named_catalogs, current_choice_provider)
|
||||
available_models = cat.models
|
||||
|
||||
def empty_applies(provider_id: str) -> bool:
|
||||
|
||||
@@ -397,6 +397,7 @@ class SessionManager:
|
||||
"platform": "acp", "quiet_mode": True, "session_id": session_id, "session_db": self._get_db(),
|
||||
"enabled_toolsets": _expand_acp_enabled_toolsets(["hermes-acp"], mcp_server_names=configured_mcp_servers),
|
||||
"model": model or default_model,
|
||||
"cwd": cwd,
|
||||
}
|
||||
try:
|
||||
runtime = resolve_runtime_provider(requested=requested_provider or config_provider)
|
||||
@@ -424,9 +425,6 @@ class SessionManager:
|
||||
logger.debug("ACP: bounded MCP discovery wait failed", exc_info=True)
|
||||
|
||||
agent = AIAgent(**kwargs)
|
||||
# Codex app-server sessions spawn lazily on the first turn; stamp the ACP
|
||||
# workspace so the Codex runtime starts from the editor cwd, not ours.
|
||||
agent.session_cwd = cwd
|
||||
# ACP stdio: stdout is protocol-only JSON-RPC; agent chatter goes to stderr.
|
||||
agent._print_fn = _acp_stderr_print
|
||||
return agent
|
||||
|
||||
Reference in New Issue
Block a user