fix: Projekt-Fokus pro Geraet unabhaengig (Diagnostic/App/Multi-Instanz)

Zwei Kopplungs-Bugs behoben:
- set_project_kind nutzte den globalen active_project statt des Request-Projekts
  → markierte das falsche Projekt (belegt: global aktiv war mac_os_update_fehler,
  das kind=code bekam, obwohl die App in basic_os war). _dispatch_tool bekommt
  jetzt die project_id des Requests durchgereicht; set_project_kind wirkt darauf.
- Die App erzwang bei project_changed-Broadcasts (ARIA-Tool/Diagnostic/andere
  App-Instanz) einen Fokuswechsel → alle Geraete wurden mitgezogen. Fokus ist
  jetzt rein geraetelokal; Broadcasts aktualisieren nur Namen/Typ. Wechseln nur
  noch lokal ueber den Drawer.

py-compile + tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 22:37:51 +02:00
co-authored by Claude Opus 4.8
parent f45d42cd30
commit c2122c43c2
3 changed files with 36 additions and 13 deletions
+15 -8
View File
@@ -1515,7 +1515,7 @@ class Agent:
targs = {}
logger.info("[router] lokal Tool-Call: %s(%s)", tname,
", ".join(targs.keys()))
tresult = self._dispatch_tool(tname, targs)
tresult = self._dispatch_tool(tname, targs, active_project_id)
# Echter Skill (run_*) erfolgreich? Dann uebernimmt dieser
# Turn das speak-Flag des Skills (Steuerbefehl=stumm,
# Antwort-Skill=vorlesen). Letzter Skill gewinnt.
@@ -1807,7 +1807,7 @@ class Agent:
))
# Tools ausfuehren + Ergebnis als role=tool zurueck
for tc in result.tool_calls:
tool_result = self._dispatch_tool(tc["name"], tc["arguments"])
tool_result = self._dispatch_tool(tc["name"], tc["arguments"], active_project_id)
# Steuerbefehl-Skill (run_*, speak=false) erfolgreich
# ausgefuehrt → Antwort nicht vorlesen (letzter Skill gewinnt).
_tn = tc.get("name") or ""
@@ -1895,10 +1895,12 @@ class Agent:
# ── Tool-Dispatcher ───────────────────────────────────────
def _dispatch_tool(self, name: str, arguments: dict) -> str:
def _dispatch_tool(self, name: str, arguments: dict, project_id: str = "") -> str:
"""Fuehrt einen Tool-Call aus und gibt ein kurzes Text-Resultat zurueck.
Niemals werfen — Fehler werden als Text-Resultat reportet damit Claude
weitermachen kann."""
weitermachen kann. project_id = Projekt DIESES Requests (fuer Tools die
aufs aktuelle Projekt wirken, z.B. set_project_kind) — NICHT der globale
active_project (geraeteuebergreifend)."""
try:
if name == "skill_create":
# ARIA-Skills sind immer Python — execution ist nicht mehr im Schema
@@ -2602,13 +2604,18 @@ class Agent:
kind = (arguments.get("kind") or "").strip().lower()
if kind not in ("code", "chat"):
return "FEHLER: kind muss 'code' oder 'chat' sein."
active_id = projects_mod.get_active()
# WICHTIG: das Projekt des AKTUELLEN Requests markieren, NICHT den
# globalen active_project — der ist geraeteuebergreifend und wuerde
# (bei mehreren App-/Diagnostic-Instanzen) das falsche Projekt
# treffen. project_id kommt pro Request vom Client.
active_id = (project_id or "").strip()
if not active_id:
return ("Kein aktives Projekt — Du bist im Hauptthread. Erst ein Projekt "
"anlegen/betreten (project_create/project_enter), dann set_project_kind.")
return ("Kein aktives Projekt — dieser Chat laeuft im Hauptthread. "
"set_project_kind wirkt nur, wenn der Request in einem Projekt "
"laeuft (der Client sendet die project_id mit).")
updated = projects_mod.update_project(active_id, {"kind": kind})
if not updated:
return f"FEHLER: aktives Projekt '{active_id}' nicht gefunden."
return f"FEHLER: Projekt '{active_id}' nicht gefunden."
self._pending_events.append({
"type": "project_changed",
"project": updated,