diff --git a/aria-brain/agent.py b/aria-brain/agent.py index 0038e90..2474131 100644 --- a/aria-brain/agent.py +++ b/aria-brain/agent.py @@ -787,10 +787,18 @@ def _skill_to_tool(s: dict) -> dict: } if a.get("required"): required.append(name) + # Tool-Namen duerfen in der Anthropic/Claude tool_use-API nur + # [a-zA-Z0-9_-]{1,64} sein, aber der claude-max-api-proxy (OpenAI- + # Format-Adapter) ist restriktiver und faellt bei Bindestrichen auf + # die Nase — die GANZE Tool-Liste wird dann verworfen und ARIA + # bekommt "No such tool available". Skill-Namen wie 'yt-dlp-download' + # oder 'pdf-umfrage-generator' muessen daher zu run_yt_dlp_download + # bzw. run_pdf_umfrage_generator gemappt werden. + safe_name = "run_" + re.sub(r"[^a-zA-Z0-9_]", "_", s["name"]) return { "type": "function", "function": { - "name": f"run_{s['name']}", + "name": safe_name, "description": s.get("description", "(ohne Beschreibung)"), "parameters": { "type": "object", @@ -844,7 +852,7 @@ class Agent: count = ev["count"] migration_key = f"auto/skill-bypass/{skill_name}" title = f"Skill '{skill_name}' nutzen, nicht curl" - run_tool = f"run_{skill_name.replace('-', '_')}" + run_tool = "run_" + re.sub(r"[^a-zA-Z0-9_]", "_", skill_name) content = ( f"WICHTIG fuer Performance + Stefans Wartezeit: " f"Skill '{skill_name}' existiert und deckt {host} ab. " @@ -1262,7 +1270,18 @@ class Agent: f"Sicherheits-Snapshot des vorherigen Stands: {res.get('safety_snapshot')}" ) if name.startswith("run_"): - skill_name = name[len("run_"):] + # Tool-Namen sind 'safe' (nur _), Skill-Namen koennen aber + # Bindestriche enthalten (z.B. yt-dlp-download). Wir suchen + # zuerst exakt, dann ueber Underscore-zu-Bindestrich-Mapping. + tool_suffix = name[len("run_"):] + skill_name = tool_suffix + if skills_mod.read_manifest(skill_name) is None: + # ggf. Bindestriche zurueckmappen + for cand in skills_mod.list_skills(active_only=False): + cand_name = cand.get("name") or "" + if re.sub(r"[^a-zA-Z0-9_]", "_", cand_name) == tool_suffix: + skill_name = cand_name + break res = skills_mod.run_skill(skill_name, args=arguments) snippet = (res.get("stdout") or "")[:2000] or "(kein stdout)" err = (res.get("stderr") or "")[:500] diff --git a/aria-brain/api_heuristic.py b/aria-brain/api_heuristic.py index 3fa6492..dd23a95 100644 --- a/aria-brain/api_heuristic.py +++ b/aria-brain/api_heuristic.py @@ -164,8 +164,9 @@ def build_bypass_section(bypass_events: list[dict]) -> str: sname = ev["skill_name"] host = ev["host"] count = ev["count"] + safe = re.sub(r"[^a-zA-Z0-9_]", "_", sname) lines.append(f"- gegen **{host}** ({count}x kuerzlich) → nutze " - f"`run_{sname.replace('-', '_')}(...)` statt curl. " + f"`run_{safe}(...)` statt curl. " f"Der Skill ist da. Nutze ihn.") lines.append("") return "\n".join(lines)