Expose connector skill activation
Add a protected skills_activate endpoint and context-aware skills_list support so connector clients can activate skills in live chats. Advertise the capability through the connector API.
Alessandro committed
May 22, 2026 at 17:03 UTC
770b53e292de5cf57d6c700f2e8dedd79cc7bd75
3 files changed
+58
-1
plugins/_a0_connector/api/v1/capabilities.py
+1
@@ -35,6 +35,7 @@ _OPTIONAL_FEATURES: dict[str, tuple[str, ...]] = {
35
"agent_profile_set": ("api.agent_profile_set",),
36
"agents_list": ("helpers.subagents",),
37
"skills_list": ("helpers.skills", "helpers.files", "helpers.projects", "helpers.runtime"),
38
+ "skills_activate": ("helpers.skills", "helpers.persist_chat"),
39
"skills_delete": ("helpers.skills", "helpers.files", "helpers.projects", "helpers.runtime"),
40
"model_presets": ("plugins._model_config.helpers.model_config",),
41
"model_switcher": ("plugins._model_config.helpers.model_config",),
plugins/_a0_connector/api/v1/skills_activate.py
new
+49
@@ -0,0 +1,49 @@
1
+"""POST /api/plugins/_a0_connector/v1/skills_activate."""
2
+from __future__ import annotations
3
+
4
+import json
5
+from typing import Any
6
+
7
+from helpers.api import Request, Response
8
+import plugins._a0_connector.api.v1.base as connector_base
9
+
10
+
11
+def _json_error(message: str, status: int) -> Response:
12
+ return Response(
13
+ response=json.dumps({"ok": False, "error": message}),
14
+ status=status,
15
+ mimetype="application/json",
16
+ )
17
+
18
+
19
+class SkillsActivate(connector_base.ProtectedConnectorApiHandler):
20
+ async def process(self, input: dict, request: Request) -> dict | Response:
21
+ from agent import AgentContext
22
+ from helpers import skills
23
+ from helpers.persist_chat import save_tmp_chat
24
+
25
+ context_id = str(input.get("context_id", "") or "").strip()
26
+ if not context_id:
27
+ return _json_error("context_id is required", 400)
28
+
29
+ context = AgentContext.get(context_id)
30
+ if context is None:
31
+ return _json_error("Context not found", 404)
32
+
33
+ entries = skills.normalize_active_skills([input.get("skill")])
34
+ if not entries:
35
+ return _json_error("skill is required", 400)
36
+
37
+ skill_entry: dict[str, Any] = dict(entries[0])
38
+ try:
39
+ active_skills = skills.activate_chat_skill(context.get_agent(), skill_entry)
40
+ save_tmp_chat(context)
41
+ except ValueError as exc:
42
+ return _json_error(str(exc), 400)
43
+
44
+ return {
45
+ "ok": True,
46
+ "context_id": context.id,
47
+ "skill": skill_entry,
48
+ "active_skills": active_skills,
49
+ }
plugins/_a0_connector/api/v1/skills_list.py
+8
-1
@@ -7,10 +7,17 @@ import plugins._a0_connector.api.v1.base as connector_base
7
8
class SkillsList(connector_base.ProtectedConnectorApiHandler):
9
async def process(self, input: dict, request: Request) -> dict | Response:
10
+ from agent import AgentContext
11
from helpers import files, projects, skills
12
13
+ context_id = str(input.get("context_id", "") or "").strip()
14
project_name = str(input.get("project_name", "")).strip() or None
13
- skill_list = skills.list_skill_catalog(project_name=project_name or "")
15
+ context = AgentContext.get(context_id) if context_id else None
16
+ agent = context.get_agent() if context else None
17
+ if context is not None and not project_name:
18
+ project_name = projects.get_context_project_name(context) or None
19
+
20
+ skill_list = skills.list_skill_catalog(project_name=project_name or "", agent=agent)
21
22
agent_profile = str(input.get("agent_profile", "")).strip() or None
23
if agent_profile: