From 60ed59cbe41035f1174afcdf58a7d6c99bf71bfe Mon Sep 17 00:00:00 2001 From: chenyida7-prog Date: Wed, 26 Aug 2026 11:18:38 +0800 Subject: [PATCH] fix(enchante): add top-level name to Agent deeplink JSON bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real-device testing on a Mac with Enchanté installed showed the Agent deeplink (enchante://agent/install) never surfaced an install prompt. Root cause: Enchanté requires the display name duplicated *inside* the base64 config JSON (top-level "name"), not only in the outer ?name= query param — without it, deserialization/validation fails silently on Enchanté's side. Adds "name" to the bundle in enchante_agent_deeplink() and asserts it in the existing test_agent_deeplink test. The MCP-only deeplink (enchante://mcp/install) and the base64 URL percent-encoding are unaffected — both already matched Enchanté's requirements. --- backend/client_config.py | 12 ++++++++++-- tests/test_client_config.py | 13 +++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/backend/client_config.py b/backend/client_config.py index 76a6b12..e2e155a 100644 --- a/backend/client_config.py +++ b/backend/client_config.py @@ -266,7 +266,13 @@ def enchante_agent_deeplink() -> str: ``enchante://agent/install?name=MyKnowledge 知识管理专家&config=`` Creates a one-click dedicated "MyKnowledge 知识管理专家" role in Enchanté's top - Agent dropdown. Payload schema confirmed with Enchante (2026-08-19): + Agent dropdown. Payload schema confirmed with Enchante (2026-08-19), amended + 2026-08-26 after a real-device install failed silently — Enchanté requires + the display name **inside** the base64 JSON too (top-level ``name``), not + only as the outer ``?name=`` query param; without it, deserialization/ + validation fails silently and the deeplink never surfaces an install prompt: + - ``name``: same display string as the outer query param, duplicated at + the JSON top level (Enchanté's requirement, not redundant on our side). - ``role``: the agent persona / system instructions, reusing ``_agent_template("Enchante")`` (``MyKnowledge-agent-Enchante.md``, 精简版) **as plain text — no YAML frontmatter** (Enchanté injects it verbatim as @@ -289,7 +295,9 @@ def enchante_agent_deeplink() -> str: Conflict Resolution float — Replace / Rename / Skip). """ import urllib.parse + display_name = "MyKnowledge 知识管理专家" bundle = { + "name": display_name, "role": _agent_template("Enchante"), "skillNames": [], "mcpServers": { @@ -301,7 +309,7 @@ def enchante_agent_deeplink() -> str: } }, } - name = urllib.parse.quote("MyKnowledge 知识管理专家") + name = urllib.parse.quote(display_name) return (f"enchante://agent/install?name={name}&config=" f"{_base64_quote(bundle)}") diff --git a/tests/test_client_config.py b/tests/test_client_config.py index 7aa33d4..6336092 100644 --- a/tests/test_client_config.py +++ b/tests/test_client_config.py @@ -723,12 +723,14 @@ def test_deeplink_base64_decodes_after_unquote(self, fake_home: Path) -> None: assert bundle["config"]["env"]["MYKNOWLEDGE_CLIENT"] == "Enchante" def test_agent_deeplink(self, fake_home: Path) -> None: - """Agent deeplink (schema confirmed with Enchante 2026-08-19). + """Agent deeplink (schema confirmed with Enchante 2026-08-19, amended + 2026-08-26: top-level ``name`` added after a real-device install failed + silently without it — see ``enchante_agent_deeplink`` docstring). Pins the URL scheme, display name ``MyKnowledge 知识管理专家``, the shared - '+'→'%2B' quoting + base64 round-trip, and the confirmed payload schema - ``{role, skillNames, mcpServers}`` (role reuses the agent template, the - mcpServers bundle reuses mcp_entry("Enchante")). + '+'→'%2B' quoting + base64 round-trip, and the payload schema + ``{name, role, skillNames, mcpServers}`` (role reuses the agent template, + the mcpServers bundle reuses mcp_entry("Enchante")). """ import base64 import urllib.parse @@ -741,6 +743,9 @@ def test_agent_deeplink(self, fake_home: Path) -> None: assert "+" not in enc # '+'→'%2B' bundle = json.loads( base64.b64decode(urllib.parse.unquote(enc)).decode("utf-8")) + # top-level name must match the outer ?name= query param exactly — + # Enchante requires it duplicated inside the JSON, not only outside. + assert bundle["name"] == "MyKnowledge 知识管理专家" assert bundle["role"].startswith("# MyKnowledge Agent") # no standalone skill is shipped anymore → skillNames stays empty assert bundle["skillNames"] == []