diff --git a/concepts/Automation Business Spec.md b/concepts/Automation Business Spec.md new file mode 100644 index 0000000..2c67231 --- /dev/null +++ b/concepts/Automation Business Spec.md @@ -0,0 +1,740 @@ +# PowerOn Automation Unification — Business Specification + +**Version:** 2.0 +**Datum:** 2026-04-05 +**Status:** Draft + +--- + +## 1. Executive Summary + +PowerOn enthält heute drei teilweise überlappende Systeme für die Automatisierung von Workflows. Die Ziel-Architektur konsolidiert diese zu einer einheitlichen Platform mit klaren Grenzen zwischen Services, Features und Shared Libraries. + +**Leitprinzipien:** +- Keine Migration von Altdaten — Automation v1 ist nicht produktiv, wird nicht migriert +- Automation v1 bleibt bestehen, bis es durch das neue Feature abgelöst ist +- Klare Datenmodelle mit definierten State Machines als stabile, erweiterbare Grundlage +- Klare Trennung von Mandant/Feature/Feature-Instanz und zugehöriger RBAC-Logik +- Skalierbare AI-Tool-Architektur mit Toolboxes und Sub-Agents für 50–100+ Tools + +--- + +## 2. Ist-Analyse + +### 2.1 Drei bestehende Systeme + +| System | Modell | Stärke | Schwäche | +|--------|--------|--------|----------| +| **Automation v1** | Template + Cron + Placeholders | Stabiles, bewährtes Scheduling mit Callback-basiertem Sync, inkrementeller Job-Registrierung, Execution-Logs | Kein visuelles Editing, kein Branching, starres Template-Modell | +| **Automation2** | Graph-Editor (n8n-style) + Run/Task-Modell | Visuelles Modellieren, Branching, Loops, Human-in-the-loop, Pause/Resume, diverse Node-Typen | Kein AI-Chat im Editor, kein UDB-Zugang, Scheduler-Sync weniger robust als v1 | +| **AI Workspace** | Chat-driven AI Agent mit Tools | Natural-language Interaktion, 40+ Core Tools + Action Tools, Streaming, RAG | Kein persistiertes Workflow-Modell, kein Scheduling | + +### 2.2 Wer nutzt die Method/Action Library? + +Die `workflows/methods/` Library (methodAi, methodOutlook, methodSharepoint, etc.) ist bereits die de facto gemeinsame Basis. Alle Systeme nutzen `ActionExecutor.executeAction()`: + +| Consumer | Pfad zur Action Library | +|----------|------------------------| +| **Workspace AI Agent** | `ActionToolAdapter` registriert `dynamicMode=True` Actions als Agent-Tools → LLM wählt Tool → `ActionExecutor` | +| **Automation2 Graph Engine** | `ActionNodeExecutor` liest `_method`/`_action` aus Node-Definition → `ActionExecutor` | +| **Automation v1** | `WorkflowProcessor` → `modeAutomation` → `ActionExecutor` | +| **Workspace Dynamic Mode** | `WorkflowProcessor` → `modeDynamic` → AI plant Tasks → `ActionExecutor` | + +### 2.3 Wer nutzt die AI Tools? + +| Tool-Typ | Registrierung | Nutzung | +|----------|--------------|---------| +| **Core Tools** (40: readFile, webSearch, sendMail, etc.) | `_registerCoreTools()` | Workspace Agent, CommCoach Agent | +| **Action Tools** (dynamicMode Actions) | `ActionToolAdapter.registerAll()` | Workspace Agent (toolSet-übergreifend) | +| **Graph Node Types** (ai.prompt, email.checkEmail, etc.) | `STATIC_NODE_TYPES` in `nodeDefinitions/` | Automation2 `executeGraph` | + +**Benötigen die AI Tools die Method/Actions?** Ja, direkt. `ActionToolAdapter` transformiert jede `dynamicMode=True` Action in ein Agent-Tool. Und die Automation2-Nodes mappen über `_method`/`_action` auf dieselbe Library. + +### 2.4 Identifizierte Inkonsistenzen + +| # | Inkonsistenz | +|---|-------------| +| **I-1** | Zwei Datenmodelle: `AutomationDefinition` (v1) vs `Automation2Workflow` (v2) — kein gemeinsames Workflow-Konzept | +| **I-2** | Zwei Datenbanken: `poweron_automation` vs `poweron_automation2` | +| **I-3** | Zwei Scheduler: v1 (inkrementell, callback `automation.changed`) vs v2 (full wipe + re-register, callback `automation2.workflow.changed`) | +| **I-4** | Zwei Execution Engines: `WorkflowManager` + `WorkflowProcessor` vs `executionEngine.executeGraph` | +| **I-5** | Kein AI-Chat im Graphical Editor | +| **I-6** | `WorkspaceInput` ist nicht als wiederverwendbare Komponente extrahiert | +| **I-7** | UDB nur in Workspace und CommCoach, nicht im Graphical Editor | +| **I-8** | AI Tools skalieren nicht: 50+ Tools ohne Gruppierungslogik, alle werden dem LLM gleichzeitig exponiert | + +--- + +## 3. Ziel-Architektur + +### 3.1 Vier Säulen + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ POWERON UNIFIED PLATFORM │ +│ │ +│ ┌─────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │ +│ │ AI Service │ │ Graphical Editor │ │ Automation │ │ +│ │ (Säule 1) │ │ (Säule 2) │ │ Scheduler │ │ +│ │ │ │ │ │ (Säule 3) │ │ +│ │ • AI Gateway │ │ • Visual Flow Builder │ │ │ │ +│ │ • Agent Loop │ │ • Node Palette │ │ • Cron Engine │ │ +│ │ • Toolboxes │ │ • AI Chat Sidebar │ │ • Event Trigger │ │ +│ │ • Sub-Agents │ │ • UDB Integration │ │ • Webhook Entry │ │ +│ │ • Streaming │ │ • Tracing/Debug Log │ │ • Run History │ │ +│ │ • Failover │ │ • Test Runner │ │ • Monitoring │ │ +│ │ • Neutraliz. │ │ • Version Management │ │ • Notifications │ │ +│ └──────┬───────┘ └──────────┬─────────────┘ └────────┬─────────┘ │ +│ │ │ │ │ +│ ┌──────▼──────────────────────▼───────────────────────────▼─────────┐ │ +│ │ SÄULE 4: UNIFIED ACTION LIBRARY (Toolboxes) │ │ +│ │ │ │ +│ │ Toolbox "ai" │ Toolbox "email" │ Toolbox "files" │ │ +│ │ ───────────── │ ────────────── │ ────────────── │ │ +│ │ ai.process │ outlook.readEmails │ context.extract │ │ +│ │ ai.webResearch │ outlook.searchEmails │ context.neutralize │ │ +│ │ ai.summarize │ outlook.draftEmail │ file.create │ │ +│ │ ai.translate │ outlook.sendDraft │ context.transform │ │ +│ │ ai.generate │ │ │ │ +│ │ │ │ │ │ +│ │ Toolbox "sharepoint"│ Toolbox "clickup" │ Toolbox "jira" │ │ +│ │ ─────────────────── │ ────────────── │ ───────────── │ │ +│ │ sharepoint.findDoc │ clickup.searchTasks │ jira.connect │ │ +│ │ sharepoint.readDocs │ clickup.createTask │ jira.exportTickets │ │ +│ │ sharepoint.upload │ clickup.updateTask │ jira.importTickets │ │ +│ │ ... │ ... │ ... │ │ +│ │ │ │ +│ │ Toolbox "trustee" │ Toolbox "chatbot" │ [future toolboxes] │ │ +│ │ ────────────────── │ ────────────── │ │ │ +│ │ trustee.extract │ chatbot.queryDB │ slack.sendMessage │ │ +│ │ trustee.process │ │ teams.postChannel │ │ +│ │ trustee.syncAcctng │ │ google.readDrive │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +### 3.2 Säule 1: AI Service (besteht, zu erweitern um Toolbox-Architektur) + +**Status:** Kern bereit (`serviceAi` + `serviceAgent` + `aicore`). + +**Erweiterung: Toolbox-Architektur für 50–100+ Tools** → siehe Abschnitt 5. + +### 3.3 Säule 2: Graphical Editor (Feature "graphicalEditor") + +**Status:** Basis vorhanden (Automation2FlowEditor), zu erweitern. + +**Funktionalität:** +1. **Visual Flow Builder** (besteht): Canvas, Nodes, Connections, Branching, Loops +2. **Node Palette** (besteht): Abgeleitet aus Toolbox-Registry — jede Toolbox-Action kann als Node exponiert werden +3. **AI Chat Sidebar** (neu): Agent mit Toolbox `"workflow"` für Graph-Manipulation +4. **UDB Integration** (neu): UnifiedDataBar im Editor +5. **Tracing/Debug Log** (zu erweitern): RunStepLog mit Input-Snapshot, Duration, Error pro Node +6. **Test Runner** (zu erweitern): Visual Highlighting, Step-by-Step, AI-assisted Debugging +7. **Version Management** (neu): Draft/Published Lifecycle + +### 3.4 Säule 3: Automation Scheduler (zu konsolidieren) + +**Status:** v1-Scheduling hat bewährte Patterns, die direkt in v2 übernommen werden. + +**Von v1 übernehmen:** +- Inkrementeller Sync (nur geänderte Jobs registrieren/entfernen, nicht full wipe) +- Persistierter Job-Handle (`eventId`) auf dem Workflow für Debugging +- `automation.changed` Callback-Pattern für reaktive Synchronisation +- Handler lädt Workflow neu und prüft `active` vor Execution +- Execution-Logs als Audit Trail (capped auf 50 Einträge) +- Creator-User Tracking via `sysCreatedBy` für Execution-Kontext + +**Von v2 beibehalten:** +- `IntervalTrigger` für einfache Wiederholungen +- Main-Loop-Bridging (`call_soon_threadsafe`) für Thread-Safety +- Delayed Startup Sync (5s) für DB-Readiness +- Striktes Cron-Parsing (5/6 Felder) + +### 3.5 Säule 4: Unified Action Library mit Toolboxes + +**Status:** Infrastruktur besteht (`workflows/methods/` + `methodDiscovery` + `ActionExecutor`). Zu erweitern um Toolbox-Konzept. + +→ Detailliert in Abschnitt 5. + +--- + +## 4. Mandant/Feature/RBAC-Architektur + +### 4.1 Hierarchie + +``` +System (PowerOn Platform) +│ +├── System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null) +│ "admin", "user", "viewer" +│ → kopiert bei Mandant-Erstellung via copySystemRolesToMandate() +│ +├── Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X) +│ "workspace-admin", "workspace-user", "workspace-viewer" +│ "graphicalEditor-admin", "graphicalEditor-user", ... +│ → kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles() +│ +├── Mandant (Tenant) +│ ├── Mandate-Rollen (kopiert von System Templates) +│ │ mandateId=X, featureInstanceId=null, featureCode=null +│ │ "admin", "user", "viewer" +│ │ └── UserMandateRole ← UserMandate ← User +│ │ +│ ├── Feature-Instanz A (z.B. "AI Workspace Produktion") +│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates) +│ │ │ mandateId=X, featureInstanceId=Y, featureCode="workspace" +│ │ │ "workspace-admin", "workspace-user", "workspace-viewer" +│ │ │ └── FeatureAccessRole ← FeatureAccess ← User +│ │ └── Daten (Workflows, Files, Chats, ...) +│ │ +│ ├── Feature-Instanz B (z.B. "Graphical Editor Dev") +│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates) +│ │ │ mandateId=X, featureInstanceId=Z, featureCode="graphicalEditor" +│ │ │ "graphicalEditor-admin", "graphicalEditor-user", ... +│ │ └── Daten (Workflows, Runs, Tasks, ...) +│ │ +│ └── Feature-Instanz C (z.B. "Trustee Buchhaltung") +│ ├── Feature-Instanz-Rollen +│ └── Daten (Positionen, Belege, ...) +│ +└── Feature Catalog (Global) + ├── Feature "workspace" (code, label, icon) + ├── Feature "graphicalEditor" (code, label, icon) + ├── Feature "trustee" (code, label, icon) + └── ... +``` + +### 4.2 Zwei getrennte Template-Rollen-Systeme + +Es gibt **zwei getrennte** Template-Systeme — diese sind **nicht** dieselben: + +**System Template Rollen (für Mandanten)** + +| Feld | Wert | +|------|------| +| `isSystemRole` | `true` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | `null` | +| `roleLabel` | `"admin"`, `"user"`, `"viewer"` | +| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` | +| Ausgelöst bei | Mandant-Erstellung | +| Ziel-Rolle | `mandateId=X`, `featureInstanceId=null`, `isSystemRole=false` + kopierte AccessRules | + +**Feature Template Rollen (für Feature-Instanzen)** + +| Feld | Wert | +|------|------| +| `isSystemRole` | `false` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | z.B. `"workspace"`, `"automation2"` | +| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"`, `"workspace-viewer"` | +| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` | +| Ausgelöst bei | Feature-Instanz-Erstellung | +| Ziel-Rolle | `mandateId=X`, `featureInstanceId=Y`, `isSystemRole=false` + kopierte AccessRules | + +**Wichtig:** Templates werden **nur bei Erstellung** kopiert. Spätere Änderungen werden **nicht** automatisch propagiert. (Ausnahme: `_syncTemplateRolesToDb()` synct neue UI-Objekte auf bestehende Mandate-Rollen mit gleichem `roleLabel`.) + +### 4.3 RBAC-Resolution (Priority-System) + +| Rollen-Typ | Scope | RBAC Priority | +|------------|-------|---------------| +| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) | +| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 | +| **Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (höchste) | + +**Resolution:** +1. Lade Mandate-Rollen (via `UserMandate` → `UserMandateRole`) +2. Lade Feature-Instanz-Rollen (via `FeatureAccess` → `FeatureAccessRole`) +3. Lade `AccessRules` für alle gefundenen Rollen +4. **Höchste Priorität gewinnt** bei DATA-Permissions (Instanz schlägt Mandant schlägt Global) +5. **View wird OR-verknüpft** über alle Rollen der höchsten Prioritätsstufe +6. **Item-Spezifität** innerhalb einer Stufe: exact > prefix > generic + +### 4.3 AccessRule-Kontexte + +| Kontext | Item-Beispiel | Prüft | +|---------|---------------|-------| +| `UI` | `ui.feature.graphicalEditor.editor` | Seitenleiste, Navigation, Buttons | +| `RESOURCE` | `resource.feature.graphicalEditor.execute` | Ausführungsberechtigungen | +| `DATA` | Auto-generiert aus Tabellenname + featureCode | CRUD auf Datenbank-Ebene | + +### 4.4 Feature-Container-Pattern + +Jedes Feature folgt einem einheitlichen Container-Pattern: + +``` +features// +├── main.py ◄── Feature-Definition +│ ├── FEATURE_CODE = "" +│ ├── UI_OBJECTS = [...] ◄── RBAC UI-Objekte +│ ├── RESOURCE_OBJECTS = [...] ◄── RBAC Resource-Objekte +│ ├── TEMPLATE_ROLES = [...] ◄── Rollen-Templates mit AccessRules +│ ├── REQUIRED_SERVICES = [...] ◄── Service-Dependencies +│ ├── registerFeature(catalog) ◄── Registriert Objekte + synct Rollen +│ └── getFeatureDefinition() ◄── Katalog-Metadaten +│ +├── routeFeature.py ◄── FastAPI Router (HTTP) +│ └── router = APIRouter(prefix="/api/") +│ +├── interfaceFeature.py ◄── DB-Interface (CRUD + RBAC) +│ └── getInterface(user, mandateId, featureInstanceId) +│ +├── datamodelFeature.py ◄── Pydantic-Modelle +│ +└── [optional] service/ ◄── Feature-spezifische Services + └── mainService.py +``` + +**Automatische Discovery:** `registry.py` scannt `features/*/routeFeature*.py` und `features/*/main*.py`. Routers werden automatisch gemountet, Features automatisch im Katalog registriert. Kein manuelles Wiring nötig. + +--- + +## 5. AI-Tool-Architektur: Toolboxes und Sub-Agents + +### 5.1 Problem: Tool-Explosion + +Aktuell werden ~50 Tools dem LLM in einem einzigen Prompt exponiert. Bei 100+ Tools: +- LLM-Context wird überladen → schlechtere Tool-Auswahl +- Irrelevante Tools für den aktuellen Kontext → Confusion und Halluzinationen +- Keine Feature-isolierte Tool-Bereitstellung + +### 5.2 Konzept: Toolboxes + +Eine **Toolbox** ist eine thematisch gebündelte Gruppe von Tools mit einem klaren Scope. Toolboxes ersetzen das bisherige flache `_CORE_ONLY_TOOLS`-Set. + +``` +Toolbox-Registry +│ +├── Toolbox "core" (immer verfügbar) +│ ├── readFile, listFiles, searchInFileContent +│ ├── writeFile, deleteFile, renameFile, copyFile +│ ├── listFolders, createFolder, deleteFolder +│ ├── webSearch, readUrl +│ └── detectLanguage, translateText +│ +├── Toolbox "ai" (AI-Operationen) +│ ├── summarizeContent, describeImage +│ ├── generateImage, textToSpeech, speechToText +│ ├── renderDocument, createChart, executeCode +│ └── neutralizeData +│ +├── Toolbox "datasources" (Externe Verbindungen) +│ ├── listConnections, browseDataSource, searchDataSource +│ ├── downloadFromDataSource, uploadToExternal +│ └── browseContainer, readContentObjects, extractContainerItem +│ +├── Toolbox "email" (Outlook-Integration) +│ ├── sendMail +│ └── outlook.readEmails, outlook.searchEmails, outlook.draftEmail +│ +├── Toolbox "sharepoint" (SharePoint-Integration) +│ └── sharepoint.findDoc, sharepoint.read, sharepoint.upload, ... +│ +├── Toolbox "clickup" (ClickUp-Integration) +│ └── clickup.searchTasks, clickup.createTask, ... +│ +├── Toolbox "jira" (Jira-Integration) +│ └── jira.connect, jira.exportTickets, ... +│ +├── Toolbox "workflow" (Graph-Manipulation für Editor-Chat) +│ ├── readWorkflowGraph, addNode, removeNode +│ ├── connectNodes, setNodeParameter +│ ├── listAvailableNodeTypes, validateGraph +│ └── listWorkflowHistory, readWorkflowMessages +│ +├── Toolbox "trustee" (Feature-spezifisch) +│ └── trustee.extract, trustee.process, trustee.syncAccounting +│ +└── Toolbox "chatbot" (Feature-spezifisch) + └── chatbot.queryDatabase +``` + +### 5.3 Toolbox-Bereitstellung: Initiales Set + dynamische Eskalation + +**Kern-Idee:** Der Agent startet mit einem kompakten, eingeschränkten Tool-Set. Er kennt aber den **Katalog aller verfügbaren Toolboxes**. Wenn er für eine Aufgabe Spezial-Tools braucht, kann er eine Toolbox **anfordern** — und bekommt sie in der nächsten Runde bereitgestellt. + +**Warum nicht alle Tools sofort?** +- Bei 50–100+ Tools wird der LLM-Context überladen → schlechtere Tool-Auswahl +- Irrelevante Tools führen zu Confusion und Halluzinationen +- Die meisten Interaktionen brauchen nur 10–15 Core-Tools + +**Mechanismus: `requestToolbox` Meta-Tool** + +``` +Runde 1: + Tools: core (readFile, webSearch, ...) + requestToolbox + System-Prompt enthält: "Verfügbare Toolboxes: email, sharepoint, clickup, + jira, workflow, ai, datasources, trustee, ..." + + User: "Lies meine letzten Emails und fasse sie zusammen" + LLM: → requestToolbox("email") [braucht Email-Tools] + +Runde 2: + Tools: core + email (sendMail, outlook_readEmails, outlook_searchEmails, ...) + + requestToolbox + + LLM: → outlook_readEmails(connectionRef, folder="Inbox", limit=10) + +Runde 3: + LLM: → (Text-Antwort mit Zusammenfassung) + → COMPLETED +``` + +**`requestToolbox` Tool-Definition:** + +```python +{ + "name": "requestToolbox", + "description": "Request additional specialized tools for the current task. " + "Call this when you need capabilities beyond your current tools.", + "parameters": { + "type": "object", + "properties": { + "toolboxId": { + "type": "string", + "description": "ID of the toolbox to activate", + "enum": [...] # Dynamisch: nur verfügbare Toolboxes + }, + "reason": { + "type": "string", + "description": "Why you need this toolbox" + } + }, + "required": ["toolboxId"] + } +} +``` + +**AgentConfig:** + +```python +class AgentConfig(BaseModel): + maxRounds: int = 25 + maxCostCHF: Optional[float] = None + initialToolboxes: List[str] = ["core"] # Initiales Set + availableToolboxes: List[str] = [] # Was angefordert werden darf + temperature: Optional[float] = None +``` + +**Konfiguration pro Feature:** + +| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) | +|---------|--------------------|------------------------------------| +| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira", "workflow"]` — gefiltert nach User-Connections | +| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` | +| **CommCoach** | `["core"]` | `[]` (keine Eskalation erlaubt) | +| **Chatbot** | `["core"]` | `[]` | + +**Vorteile:** +- **Lean Context:** Die meisten Chats brauchen nur Core-Tools → schnellere, präzisere Antworten +- **Selbst-navigierend:** LLM entscheidet selbst, wann Spezial-Tools nötig sind +- **Kontrolliert:** `availableToolboxes` limitiert, was der Agent anfordern darf (RBAC) +- **Skaliert:** 100+ Tools kein Problem — der LLM sieht immer nur was er gerade braucht +- **Transparent:** `requestToolbox("email", reason="User fragt nach Emails")` ist nachvollziehbar im Tracing + +**Automatische Toolbox-Verfügbarkeit basierend auf Connections:** +Welche Toolboxes in `availableToolboxes` erscheinen, hängt von den aktiven User-Connections ab. Hat der User keine Outlook-Verbindung, erscheint `"email"` gar nicht in der Liste — der Agent kann sie auch nicht anfordern. + +### 5.4 Sub-Agents (Feature Data Agents) + +Für datenintensive Features mit eigenem Schema (z.B. Trustee Buchhaltung) existiert bereits das Sub-Agent-Pattern: + +``` +User Prompt im Workspace + │ + ▼ +Main Agent (Toolboxes: core, ai, datasources, ...) + │ + ├── Tool: queryFeatureInstance("trustee", instanceId, question) + │ │ + │ ▼ + │ Sub-Agent (Feature Data Agent) + │ Eigene ToolRegistry: browseTable, queryTable + │ Eigenes System-Prompt mit Schema-Wissen + │ Limitiert: maxRounds=5, maxCost=0.10 CHF + │ │ + │ ▼ + │ Antwort zurück an Main Agent + │ + ├── Tool: readFile(fileId) + │ (Core Tool, direkt) + │ + └── Tool: outlook_readEmails(connectionRef) + (Action Tool, direkt) +``` + +**Empfehlung: Sub-Agent-Pattern ausweiten auf:** +- **Trustee** (besteht): browseTable, queryTable auf Buchhaltungsdaten +- **Workflow** (neu): Graph-Manipulation als Sub-Agent im Editor-Chat +- **Future Features** mit eigenem Datenmodell: Gleicher Pattern + +**Vorteil:** Der Main Agent bleibt schlank. Feature-spezifisches Datenwissen ist im Sub-Agent gekapselt. Der LLM des Main Agents sieht nur ein einzelnes Tool (`queryFeatureInstance`) statt 10+ tabellenspezifische Tools. + +### 5.5 State Machine: AI Agent Run (mit Toolbox-Eskalation) + +``` + ┌──────────┐ + │ IDLE │ + └────┬─────┘ + │ runAgent(prompt, initialToolboxes, availableToolboxes) + ▼ + ┌──────────┐ + ┌────►│ THINKING │◄──────────────────────────────────┐ + │ └────┬─────┘ │ + │ │ │ + │ ├── LLM Response (Text only) ──────────────┼──► COMPLETED + │ │ │ + │ └── LLM Response (ToolCalls) │ + │ │ │ + │ ▼ │ + │ ┌────────────────────┐ │ + │ │ EXECUTING_TOOLS │ │ + │ └────┬───────────────┘ │ + │ │ │ + │ ├── requestToolbox(id) ───┐ │ + │ │ ▼ │ + │ │ ┌─────────────────────┐ │ + │ │ │ TOOLBOX_ESCALATION │ │ + │ │ │ Toolbox wird aktiviert│ │ + │ │ │ Tools der nächsten │ │ + │ │ │ Runde erweitert │ │ + │ │ └──────────┬──────────┘ │ + │ │ │ │ + │ ├── Sub-Agent call ───┐ │ │ + │ │ ▼ │ │ + │ │ ┌──────────────┐│ │ + │ │ │SUB_AGENT_CALL ││ │ + │ │ └──────┬───────┘│ │ + │ │ │ │ │ + │ ├── Regular tools ─┼────────┼────────────────┘ + │ │ (results → next round) + │ + │ maxRounds / maxCost reached + ▼ + ┌──────────┐ + │ COMPLETED │ (oder FAILED / CANCELLED) + └──────────┘ +``` + +--- + +## 6. Use Cases + +### UC-1: Datenquellen einbinden → besteht, keine Änderung + +### UC-2: AI Workspace mit UDB → besteht, keine Änderung + +### UC-3: Workflow im Workspace erstellen/bearbeiten + +**Beschreibung:** User beschreibt im Workspace-Chat einen gewünschten Workflow → Agent nutzt Toolbox `"workflow"` → generiert Graph → speichert als Draft → User öffnet im Graphical Editor. + +**Voraussetzung:** Die `"workflow"` Toolbox muss im Workspace aktivierbar sein. Die Tools validieren jeden Graph-Mutationsschritt: +- `addNode(type, parameters)` → prüft: existiert der Node-Typ? Sind die Parameter valide? +- `connectNodes(source, target)` → prüft: sind die I/O-Typen kompatibel? Keine Zyklen? +- `validateGraph()` → vollständige Validierung inkl. Trigger-Prüfung + +### UC-4: AI Chat im Graphical Editor + +**Beschreibung:** Der Graphical Editor hat eine Sidebar mit Chat. Der Agent nutzt Toolbox `"workflow"` mit speziellem System-Prompt, der den aktuellen Graph kennt. Er kann Nodes hinzufügen, entfernen, umkonfigurieren und den Graph erklären. + +**Architektur:** Gleicher `serviceAgent.runAgent()` Pfad wie Workspace, aber: +- Anderes ToolSet: `toolboxes: ["core", "workflow"]` +- System-Prompt enthält den aktuellen Graph als Kontext +- Frontend: Extrahierte `ChatBar`-Komponente + `ChatStream` + +### UC-5: Workflow testen mit Tracing Log + +**Beschreibung:** User klickt "Test Run" → Graph wird ausgeführt → jeder Node wird visuell hervorgehoben (Status-Farben) → Tracing Log zeigt pro Node: Input, Output, Dauer, Fehler. + +**Bei Fehlern:** User bespricht den Fehler im AI Chat → Agent liest den RunStepLog und schlägt Korrekturen vor. + +**Technisch:** +- `RunStepLog` pro Node mit Timestamps, Input-Snapshot, Duration, Error-Stack +- SSE/WebSocket für Live-Updates des Node-Status +- Run-Modes: "Full Run" und "Step-by-Step" (Pause nach jedem Node) + +### UC-6: Workflow automatisieren (Scheduler) + +**Beschreibung:** User konfiguriert Schedule im Editor (via trigger.schedule Node oder Invocation) → konsolidierter Scheduler registriert Cron-Job → Run-History wird persistiert → bei Fehlern Notifications. + +--- + +## 7. State Machines + +### 7.1 Workflow Lifecycle + +``` + ┌───────┐ + create ──►│ DRAFT │◄──── edit (neuer graph) + └───┬───┘ + │ publish + ▼ + ┌───────────┐ + unpublish│ PUBLISHED │◄──── edit → erzeugt neuen Draft, + │ └───────────┘ Published bleibt aktiv + │ │ + │ │ archive (manuell oder nach Deaktivierung) + │ ▼ + │ ┌───────────┐ + │ │ ARCHIVED │ + │ └───────────┘ + │ │ + └───────┘ re-publish (aus Archiv zurückholen) +``` + +**Regeln:** +- Ein Workflow hat genau **eine** published Version (oder keine) +- Scheduled Runs nutzen immer die published Version +- Edit erzeugt immer einen neuen Draft — die published Version bleibt stabil +- Archive ist reversibel + +### 7.2 WorkflowRun Lifecycle + +``` + ┌─────────┐ + executeGraph──►│ RUNNING │ + └────┬────┘ + │ + ┌────────────────┼────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────┐ ┌──────────┐ ┌────────┐ + │ PAUSED │ │COMPLETED │ │ FAILED │ + └────┬────┘ └──────────┘ └────────┘ + │ + │ resume (human task complete / email received) + │ + ▼ + ┌─────────┐ + │ RUNNING │ (Weiter ab pausiertem Node) + └────┬────┘ + │ + ├──► COMPLETED + ├──► FAILED + ├──► PAUSED (nächster Input-Node) + └──► CANCELLED (manuell abgebrochen) +``` + +**Pause-Gründe:** +- `input.*` Node → HumanTask erstellt → wartet auf User-Eingabe +- `email.checkEmail` → wartet auf neue Email (Background Poller) + +### 7.3 HumanTask Lifecycle + +``` + ┌─────────┐ + │ PENDING │ (erstellt bei Pause) + └────┬────┘ + │ + ├── User completes → COMPLETED (Run wird resumed) + ├── User cancels → CANCELLED (Run wird cancelled) + └── Timeout → EXPIRED (Run failsafe: cancel oder default) +``` + +### 7.4 Scheduler Job Lifecycle + +``` + ┌─────────────┐ + │ UNREGISTERED │ (Workflow exists, kein Schedule) + └──────┬──────┘ + │ activate + publish mit schedule invocation + ▼ + ┌─────────────┐ + │ REGISTERED │ (APScheduler Job aktiv, eventId gesetzt) + └──────┬──────┘ + │ + ├── Cron fires → executeGraph → WorkflowRun + ├── Workflow deactivated → UNREGISTERED (Job entfernt, eventId=null) + ├── Schedule geändert → Job replaced (replaceExisting=true) + └── Workflow gelöscht → UNREGISTERED (Job entfernt) +``` + +--- + +## 8. Empfehlungen und offene Punkte + +### 8.1 Starke Konzepte (umsetzen) + +| Konzept | Bewertung | +|---------|-----------| +| **Toolbox-Architektur** | Skaliert von 50 auf 100+ Tools ohne LLM-Überlastung. Klare Isolierung pro Thema. | +| **Sub-Agents pro Feature** | Bewährtes Pattern (Trustee), erweiterbar. Hält Main Agent schlank. | +| **Validierungs-Layer für Workflow-Modellierung** | Graph-Mutation nur über validierte Tools. LLM kann keinen inkonsistenten Graph erzeugen. | +| **v1-Scheduling-Patterns in v2 übernehmen** | Inkrementeller Sync ist robuster als Full-Wipe. Execution-Logs sind essentiell für Audit. | +| **State Machines als Grundlage** | Klare Zustände, klare Übergänge. Erweiterbar ohne Breaking Changes. | + +### 8.2 Was User brauchen, um das als zentrales Arbeitsinstrument zu nutzen + +| User Need | Feature | Priorität | +|-----------|---------|-----------| +| **Schneller Einstieg** | Workflow-Templates für häufige Use Cases (z.B. "Email-Attachment → AI Analyse → SharePoint Upload") | Hoch | +| **Vertrauen** | Tracing Log + Test Runner — User muss sehen, was der Workflow tut | Hoch | +| **Effizienz** | AI-gestützte Workflow-Erstellung per Chat | Hoch | +| **Fehlertoleranz** | Retry-Policies, Pause/Resume, klare Fehlermeldungen | Hoch | +| **Kontext** | UDB im Editor — Zugriff auf Files/Sources bei der Konfiguration | Hoch | +| **Zusammenarbeit** | Workflows teilen im Mandant, Rollen-basierter Zugriff | Mittel | +| **Monitoring** | Dashboard: laufende Automationen, nächste Runs, Fehlerrate, Kosten | Mittel | +| **Flexibilität** | Custom Script Nodes (Python-Sandbox), AI Decision Nodes | Mittel | +| **Mobile** | Notifications + Approval-Tasks per Mobile | Phase 2 | + +### 8.3 Offene Architektur-Fragen + +| Frage | Empfehlung | +|-------|------------| +| **Toolbox-Selektion: statisch oder dynamisch?** | Hybrid: Basis-Toolboxes pro Feature statisch konfiguriert, Connection-basierte Toolboxes dynamisch aktiviert. | +| **Sub-Agent Tiefe: verschachtelt?** | Maximal 1 Level tief. Main Agent → Sub-Agent. Kein Sub-Sub-Agent (zu komplex, unkontrollierbar). | +| **Graph-Execution und WorkflowManager: zusammenführen?** | Nein, getrennt lassen. `executeGraph` für den Graphical Editor, `WorkflowManager` bleibt für den dynamischen Workspace-Modus. Verschiedene Orchestrierungsmodelle. | +| **Feature-Code: `automation2` → `graphicalEditor`?** | Ja, Rename in Phase 1. Klarer Name für User und Entwickler. | + +--- + +## 9. Phasen-Plan + +### Phase 1: Foundation +- Unified Workflow Datenmodell (Workflow + WorkflowVersion + WorkflowRun + RunStepLog + HumanTask) +- Toolbox-Registry implementieren (Ablösung `_CORE_ONLY_TOOLS`) +- ChatBar-Komponente extrahieren +- Feature Rename: `automation2` → `graphicalEditor` +- Scheduler konsolidieren (v1 Patterns in v2 Engine) + +### Phase 2: Editor Enhancement +- AI Chat Sidebar im Editor (Toolbox `"workflow"` + Graph-Manipulation-Tools) +- UDB Integration im Editor +- WorkflowVersion Lifecycle (Draft/Published) +- Enhanced RunStepLog + Visual Tracing + +### Phase 3: Productization +- Workflow Templates / Marketplace +- Monitoring Dashboard +- Retry Policies pro Node +- Notifications bei Scheduler-Fehlern +- Automation v1 Feature entfernen (wird nicht mehr benötigt) + +### Phase 4: Advanced +- AI Decision Node (`ai.decide`) +- Custom Script Node (Python Sandbox) +- Dynamische Toolbox-Aktivierung basierend auf Connections +- Sub-Agent Pattern für weitere Features + +--- + +## 10. Glossar + +| Begriff | Definition | +|---------|-----------| +| **Toolbox** | Thematisch gebündelte Gruppe von AI-Tools, die kontextabhängig aktiviert werden | +| **Sub-Agent** | Spezialisierter Mini-Agent mit eigener Tool-Registry, aufgerufen vom Main Agent | +| **Workflow** | Persistiertes Automatisierungsmodell mit Graph-Struktur | +| **WorkflowVersion** | Immutable Snapshot eines Workflow-Graphen (draft, published, archived) | +| **WorkflowRun** | Einzelne Ausführung einer WorkflowVersion | +| **RunStepLog** | Detaillierter Log pro Node-Execution innerhalb eines Runs | +| **HumanTask** | Aufgabe für menschliche Eingabe, erstellt bei Pause eines Runs | +| **Node** | Ausführungsschritt im Graph (Trigger, Action, Flow-Control, Input/Human) | +| **Method** | Integrations-Kategorie (z.B. methodOutlook) mit mehreren Actions | +| **Action** | Spezifische Operation innerhalb einer Method (z.B. outlook.readEmails) | +| **Invocation** | Entry-Point für einen Workflow (Manual, Schedule, Webhook, Form, Event) | +| **UDB** | Unified Data Bar — Multi-Tab-Panel für Chats, Files und Sources | +| **ChatBar** | Wiederverwendbare Prompt-Input-Komponente | +| **Feature Container** | Einheitliches Verzeichnis-Pattern für ein Feature (main, route, interface, datamodel) | diff --git a/concepts/Automation Data Model.md b/concepts/Automation Data Model.md new file mode 100644 index 0000000..5dd5c32 --- /dev/null +++ b/concepts/Automation Data Model.md @@ -0,0 +1,994 @@ +# PowerOn Automation Unification — Datenmodell & Architektur + +**Version:** 2.0 +**Datum:** 2026-04-05 +**Referenz:** [Automation Business Spec.md](./Automation%20Business%20Spec.md) + +--- + +## 1. Plattform-Architektur: Schichten + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ FRONTEND (React/TS) │ +│ │ +│ Shared Components: ChatBar, ChatStream, UnifiedDataBar, │ +│ FlowEditor, FormGenerator, FolderTree │ +│ │ +│ Feature Pages: WorkspacePage, GraphicalEditorPage, │ +│ CommcoachDossierView, ChatbotView, ... │ +└──────────────────────────────┬────────────────────────────────────┘ + │ HTTP/SSE/WebSocket +┌──────────────────────────────▼────────────────────────────────────┐ +│ GATEWAY (FastAPI) │ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Layer 1: FEATURES (Feature-Instanz-gebunden) │ │ +│ │ │ │ +│ │ features/workspace/ features/graphicalEditor/ │ │ +│ │ features/commcoach/ features/chatbot/ │ │ +│ │ features/trustee/ features/realEstate/ │ │ +│ │ features/neutralization/ features/teamsbot/ │ │ +│ └──────────────────────────┬──────────────────────────────────┘ │ +│ │ nutzt │ +│ ┌──────────────────────────▼──────────────────────────────────┐ │ +│ │ Layer 2: SERVICES (Feature-unabhängig, pro Request) │ │ +│ │ │ │ +│ │ serviceAgent serviceAi serviceKnowledge serviceBilling │ │ +│ │ serviceChat serviceExtraction serviceGeneration │ │ +│ │ serviceMessaging serviceWeb serviceSubscription │ │ +│ └──────────────────────────┬──────────────────────────────────┘ │ +│ │ nutzt │ +│ ┌──────────────────────────▼──────────────────────────────────┐ │ +│ │ Layer 3: SHARED INFRASTRUCTURE │ │ +│ │ │ │ +│ │ workflows/methods/ (Unified Action Library / Toolboxes)│ │ +│ │ workflows/processing/ (ActionExecutor, methodDiscovery) │ │ +│ │ workflows/automation2/ (Graph Execution Engine) │ │ +│ │ interfaces/ (DB-Abstraktion + RBAC) │ │ +│ │ aicore/ (Provider Plugins + Model Selector) │ │ +│ │ datamodels/ (Pydantic Models) │ │ +│ │ security/ (RBAC Engine) │ │ +│ │ shared/ (Utilities, eventManagement) │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Feature-Container-Pattern (Ist-Zustand, bewährt) + +### 2.1 Verzeichnisstruktur + +``` +features// +├── main.py ◄── Feature-Definition + Registrierung +│ FEATURE_CODE: str Feature-Identifikator +│ UI_OBJECTS: List[Dict] RBAC-fähige UI-Objekte +│ RESOURCE_OBJECTS: List[Dict] RBAC-fähige Resource-Objekte +│ TEMPLATE_ROLES: List[Dict] Rollen-Templates mit AccessRules +│ REQUIRED_SERVICES: List[Dict] Service-Dependencies (optional) +│ registerFeature(catalog) → bool Registriert Objekte, synct Rollen in DB +│ getFeatureDefinition() → Dict Katalog-Metadaten (Label, Icon) +│ getServices(user, ...) → Hub Service-Hub-Factory (optional) +│ +├── routeFeature.py ◄── HTTP API +│ router = APIRouter(prefix="/api/") +│ [templateRouter] = APIRouter(...) Zusätzliche Router (optional) +│ +├── interfaceFeature.py ◄── Datenbankzugriff +│ Objects CRUD + RBAC-Filterung +│ getInterface(user, mandateId, featureInstanceId) → Singleton +│ +├── datamodelFeature.py ◄── Pydantic-Datenmodelle +│ class (PowerOnModel): ... Feature-spezifische Entitäten +│ +├── nodeDefinitions/ (optional) ◄── Graph-Node-Typen (für graphicalEditor) +│ triggers.py, flow.py, ai.py, ... +│ +└── service/ (optional) ◄── Feature-interne Services + └── mainService.py +``` + +### 2.2 Automatische Discovery + +```python +# system/registry.py — Kein manuelles Wiring nötig + +discoverFeatureContainers() + # Scannt features/*/routeFeature*.py → Liste von Feature-Dirs + +loadFeatureRouters(app) + # Importiert routeFeature*.py, mountet router + *Router + +loadFeatureMainModules() + # Importiert main*.py, cached + +registerAllFeaturesInCatalog(catalogService) + # Für jedes main-Module: + # getFeatureDefinition() → catalogService.registerFeatureDefinition() + # registerFeature() → UI/Resource-Objekte + Template-Rollen sync +``` + +### 2.3 Service-Hub-Wiring + +```python +# serviceHub/__init__.py — Dynamisches Service-Wiring + +class ServiceHub: + def __init__(self, user, workflow, mandateId, featureInstanceId): + # ServiceCenterContext erstellen + self._serviceCenterContext = ServiceCenterContext(...) + + # Core Interfaces laden + self.interfaceDbApp = getAppInterface(user, mandateId) + self.interfaceDbChat = getChatInterface(user, mandateId, featureInstanceId) + + # Feature-Interfaces dynamisch laden (glob features/*/interfaceFeature*.py) + self._loadFeatureInterfaces() + + # Feature-Services dynamisch laden (glob features/*/service*/mainService*.py) + self._loadFeatureServices() + + def __getattr__(self, name): + # Lazy: shared services via getService(name, context) bei Erstzugriff +``` + +--- + +## 3. RBAC-Architektur + +### 3.1 Zwei getrennte Template-Rollen-Systeme + +**WICHTIG:** Es gibt zwei getrennte Template-Systeme — diese sind **nicht** dieselben: + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ TEMPLATE-ROLLEN (Global) │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ System Templates (für Mandanten) Priority 1 │ │ +│ │ isSystemRole=true, mandateId=null, featureCode=null │ │ +│ │ │ │ +│ │ "admin" → Mandant-Administration │ │ +│ │ "user" → Standard-User │ │ +│ │ "viewer" → Nur-Lesen │ │ +│ │ │ │ +│ │ → kopiert bei Mandant-Erstellung (copySystemRolesToMandate)│ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Feature Templates (für Feature-Instanzen) Priority 1 │ │ +│ │ isSystemRole=false, mandateId=null, featureCode="workspace"│ │ +│ │ │ │ +│ │ "workspace-admin" → alle UI/RESOURCE/DATA Rechte │ │ +│ │ "workspace-user" → eingeschränkte DATA Rechte │ │ +│ │ "workspace-viewer" → nur View │ │ +│ │ │ │ +│ │ → kopiert bei Feature-Instanz-Erstellung (_copyTemplateRoles)│ │ +│ └──────────────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────────────────┐ +│ MANDATE-ROLLEN Priority 2 │ +│ mandateId=X, featureInstanceId=null, featureCode=null │ +│ │ +│ "admin" (kopiert von System Template) │ +│ "user" (kopiert von System Template) │ +│ "viewer" (kopiert von System Template) │ +│ → zugewiesen via UserMandate → UserMandateRole │ +└────────────────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────────────────┐ +│ INSTANZ-ROLLEN Priority 3 │ +│ mandateId=X, featureInstanceId=Y, featureCode="workspace" │ +│ │ +│ "workspace-admin" (kopiert von Feature Template) │ +│ "workspace-user" (kopiert von Feature Template) │ +│ "workspace-viewer" (kopiert von Feature Template) │ +│ → zugewiesen via FeatureAccess → FeatureAccessRole │ +└────────────────────────────────────────────────────────────────────┘ +``` + +### 3.2 Datenmodell RBAC + +```python +class Role(PowerOnModel): + # Scope bestimmt die Priorität: + mandateId: Optional[str] # null = Global/Template + featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch + featureCode: Optional[str] # z.B. "workspace", "graphicalEditor" + roleLabel: str # z.B. "workspace-admin" + isSystemRole: bool # System-Rollen nicht löschbar + +class AccessRule(PowerOnModel): + roleId: str # FK → Role + context: AccessRuleContext # DATA | UI | RESOURCE + item: Optional[str] # null=generic, oder spezifisch + view: bool + read: Optional[AccessLevel] # "n" | "o" | "m" | "a" + create: Optional[AccessLevel] + update: Optional[AccessLevel] + delete: Optional[AccessLevel] + +class AccessLevel(str, Enum): + NONE = "n" # kein Zugriff + OWN = "o" # nur eigene Records + MANDATE = "m" # alle Records im Mandant + ALL = "a" # alle Records (System-Admin) +``` + +### 3.3 User-Zuweisung + +``` +User ──► UserMandate ──► UserMandateRole ──► Role (Mandate-Scope) +User ──► FeatureAccess ──► FeatureAccessRole ──► Role (Instanz-Scope) +``` + +```python +class UserMandate(PowerOnModel): + userId: str + mandateId: str + +class UserMandateRole(PowerOnModel): + userMandateId: str # FK → UserMandate + roleId: str # FK → Role (mandateId=X, featureInstanceId=null) + +class FeatureAccess(PowerOnModel): + userId: str + featureInstanceId: str + enabled: bool + +class FeatureAccessRole(PowerOnModel): + featureAccessId: str # FK → FeatureAccess + roleId: str # FK → Role (mandateId=X, featureInstanceId=Y) +``` + +### 3.4 Resolution-Algorithmus + +``` +1. Sammle roleIds: + - Mandate-Rollen: UserMandate → UserMandateRole → roleId + - Instanz-Rollen: FeatureAccess → FeatureAccessRole → roleId + +2. Lade AccessRules für alle roleIds (context + item filter) + +3. Bestimme Priorität pro Rolle: + - Priority 3: role.featureInstanceId gesetzt (Instanz) + - Priority 2: role.mandateId gesetzt (Mandant) + - Priority 1: beides null (Global/Template) + +4. DATA-Permissions: Nur Rules der HÖCHSTEN Priorität zählen + View: OR über alle Rules der höchsten Priorität + Item-Spezifität: exact > prefix > generic (innerhalb Priorität) + +5. SQL WHERE Clause: buildRbacWhereClause(permissions, user, table) +``` + +--- + +## 4. Unified Workflow Datenmodell + +### 4.1 Entitäten + +```python +# Ziel: datamodels/datamodelWorkflowUnified.py oder +# features/graphicalEditor/datamodelFeatureGraphicalEditor.py + +class WorkflowStatus(str, Enum): + DRAFT = "draft" + PUBLISHED = "published" + ARCHIVED = "archived" + +class Workflow(PowerOnModel): + """Workflow-Metadaten und Ownership.""" + id: str + mandateId: str + featureInstanceId: str + label: str + description: Optional[str] + tags: List[str] = [] + isTemplate: bool = False + templateSourceId: Optional[str] # geklont von diesem Template + currentVersionId: Optional[str] # aktive/published Version + active: bool = True # Scheduler-enabled + eventId: Optional[str] # APScheduler Job-ID (v1-Pattern) + +class WorkflowVersion(PowerOnModel): + """Immutable Snapshot eines Workflow-Graphen.""" + id: str + workflowId: str # FK → Workflow + versionNumber: int # auto-increment + status: WorkflowStatus # draft | published | archived + graph: Dict[str, Any] # { nodes: [...], connections: [...] } + invocations: List[Dict[str, Any]] # Entry-Points (manual, schedule, webhook, form) + publishedAt: Optional[float] + publishedBy: Optional[str] +``` + +### 4.2 State Machine: WorkflowVersion.status + +``` + ┌───────┐ + create ───►│ DRAFT │◄─── edit (graph mutation via API oder AI Tools) + └───┬───┘ + │ publish + ▼ + ┌───────────┐ + unpublish─►│ PUBLISHED │ + (→ DRAFT) └─────┬─────┘ + │ archive + ▼ + ┌───────────┐ + │ ARCHIVED │──► re-publish (→ PUBLISHED) + └───────────┘ + +Invariante: Pro Workflow maximal 1 Version mit status=PUBLISHED. + Scheduler nutzt immer die PUBLISHED Version. +``` + +### 4.3 Run-Modell + +```python +class RunStatus(str, Enum): + PENDING = "pending" + RUNNING = "running" + PAUSED = "paused" + COMPLETED = "completed" + FAILED = "failed" + CANCELLED = "cancelled" + +class WorkflowRun(PowerOnModel): + """Einzelne Ausführung einer WorkflowVersion.""" + id: str + workflowId: str # FK → Workflow + versionId: str # FK → WorkflowVersion + status: RunStatus + trigger: Dict[str, Any] # { type: "manual"|"schedule"|"webhook"|..., metadata } + startedAt: float + completedAt: Optional[float] + nodeOutputs: Dict[str, Any] # Outputs pro Node-ID + currentNodeId: Optional[str] # Paused bei diesem Node + resumeContext: Dict[str, Any] # Kontext für Resume + error: Optional[str] # Top-Level Fehler + costTokens: Optional[int] # Aggregierte Token-Kosten + costCredits: Optional[float] # Aggregierte Credit-Kosten +``` + +### 4.4 State Machine: WorkflowRun.status + +``` + ┌─────────┐ + executeGraph──►│ RUNNING │ + └────┬────┘ + │ + ┌────────────────┼────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌────────┐ ┌──────────┐ ┌────────┐ +│ PAUSED │ │COMPLETED │ │ FAILED │ +└───┬────┘ └──────────┘ └────────┘ + │ + │ resume(taskResult | emailReceived) + ▼ +┌─────────┐ +│ RUNNING │───► COMPLETED | FAILED | PAUSED | CANCELLED +└─────────┘ + +Pause-Gründe: + - input.* Node → HumanTask erstellt + - email.checkEmail → EmailWait (Background Poller) + +Cancel: + - Manuell durch User + - Timeout bei HumanTask (expiresAt) +``` + +### 4.5 RunStepLog und HumanTask + +```python +class StepStatus(str, Enum): + RUNNING = "running" + COMPLETED = "completed" + FAILED = "failed" + SKIPPED = "skipped" + +class RunStepLog(PowerOnModel): + """Detaillierter Log pro Node-Execution.""" + id: str + runId: str # FK → WorkflowRun + nodeId: str # Node-ID im Graph + nodeType: str # z.B. "ai.prompt", "email.checkEmail" + status: StepStatus + inputSnapshot: Dict[str, Any] # Parameters + Upstream-Daten bei Execution + output: Optional[Dict[str, Any]] # Node-Output + error: Optional[str] + startedAt: float + completedAt: Optional[float] + durationMs: Optional[int] + tokensUsed: Optional[int] # AI-Calls in diesem Step + retryCount: int = 0 + +class TaskStatus(str, Enum): + PENDING = "pending" + COMPLETED = "completed" + CANCELLED = "cancelled" + EXPIRED = "expired" + +class HumanTask(PowerOnModel): + """Aufgabe für menschliche Eingabe bei Pause.""" + id: str + runId: str # FK → WorkflowRun + workflowId: str # FK → Workflow (Convenience-FK) + nodeId: str + nodeType: str # input.approval, input.form, etc. + config: Dict[str, Any] # Node-Parameter (Formular-Felder, Approval-Text) + assigneeId: str + status: TaskStatus + result: Optional[Dict[str, Any]] + expiresAt: Optional[float] # Timeout +``` + +### 4.6 State Machine: HumanTask.status + +``` +┌─────────┐ +│ PENDING │ (erstellt bei Run-Pause) +└────┬────┘ + │ + ├── complete(result) → COMPLETED (Run wird resumed mit result) + ├── cancel() → CANCELLED (Run wird cancelled) + └── expiresAt passed → EXPIRED (Run wird cancelled oder Default) +``` + +### 4.7 ER-Diagramm + +``` +┌──────────────────┐ ┌──────────────────────┐ +│ Workflow │ 1───N │ WorkflowVersion │ +├──────────────────┤ ├──────────────────────┤ +│ id │ │ id │ +│ mandateId │ │ workflowId (FK) │ +│ featureInstanceId│ │ versionNumber │ +│ label │ │ status │ +│ description │ │ graph {} │ +│ tags [] │ │ invocations [] │ +│ isTemplate │ │ publishedAt │ +│ currentVersionId ├───1──►│ publishedBy │ +│ active │ └──────────┬───────────┘ +│ eventId │ │ 1:N +└──────────────────┘ ┌──────────▼───────────┐ + │ WorkflowRun │ + ├──────────────────────┤ + │ id │ + │ workflowId (FK) │ + │ versionId (FK) │ + │ status │ + │ trigger {} │ + │ startedAt │ + │ completedAt │ + │ nodeOutputs {} │ + │ currentNodeId │ + │ resumeContext {} │ + │ error │ + │ costTokens │ + │ costCredits │ + └──────────┬───────────┘ + 1:N │ │ 1:N + ┌─────────────┘ └──────────┐ + ▼ ▼ + ┌──────────────────┐ ┌──────────────────┐ + │ RunStepLog │ │ HumanTask │ + ├──────────────────┤ ├──────────────────┤ + │ id │ │ id │ + │ runId (FK) │ │ runId (FK) │ + │ nodeId │ │ workflowId (FK) │ + │ nodeType │ │ nodeId │ + │ status │ │ nodeType │ + │ inputSnapshot {} │ │ config {} │ + │ output {} │ │ assigneeId │ + │ error │ │ status │ + │ startedAt │ │ result {} │ + │ completedAt │ │ expiresAt │ + │ durationMs │ └──────────────────┘ + │ tokensUsed │ + │ retryCount │ + └──────────────────┘ +``` + +--- + +## 5. AI-Tool-Architektur: Toolbox-Datenmodell + +### 5.1 Toolbox-Definition + +```python +# Ziel: serviceCenter/services/serviceAgent/toolboxRegistry.py + +class ToolboxDefinition(BaseModel): + """Definition einer thematischen Tool-Gruppe.""" + id: str # z.B. "core", "email", "sharepoint", "workflow" + label: Dict[str, str] # Multilingual: {"en": "Email", "de": "E-Mail"} + description: str + featureCode: Optional[str] # null = Feature-unabhängig, "trustee" = Feature-spezifisch + tools: List[str] # Tool-Namen in dieser Toolbox + isDefault: bool = False # Immer aktiv (z.B. "core") + requiresConnection: Optional[str] # "outlook", "sharepoint" → auto-aktiviert bei Connection + +class ToolboxRegistry: + """Verwaltet Toolboxes und deren Zuordnung zu Tools.""" + _toolboxes: Dict[str, ToolboxDefinition] + _toolRegistry: ToolRegistry # Referenz auf die bestehende ToolRegistry + + def registerToolbox(self, toolbox: ToolboxDefinition): ... + + def getActiveToolboxes( + self, + featureCode: str, + userConnections: List[str], # Aktive Connection-Typen des Users + explicitToolboxes: List[str], # Explizit aktivierte Toolboxes + ) -> List[str]: ... + + def getToolsForToolboxes( + self, + activeToolboxIds: List[str], + ) -> List[ToolDefinition]: ... +``` + +### 5.2 AgentConfig (erweitert mit Eskalation) + +```python +class AgentConfig(BaseModel): + maxRounds: int = 25 + maxCostCHF: Optional[float] = None + initialToolboxes: List[str] = ["core"] # Tools bei Runde 1 + availableToolboxes: List[str] = [] # Via requestToolbox anforderbar + temperature: Optional[float] = None +``` + +### 5.3 Dynamische Toolbox-Eskalation + +**Kern-Idee:** Der Agent startet mit einem kompakten Initial-Set (`initialToolboxes`). Er kennt den Katalog aller `availableToolboxes`. Wenn er Spezial-Tools braucht, ruft er `requestToolbox(id)` auf — die Tools werden in der nächsten Runde bereitgestellt. + +``` +Runde 1: + Aktive Tools: core (readFile, webSearch, ...) + requestToolbox + System-Prompt: "Verfügbare Toolboxes: email, sharepoint, ai, ..." + + User: "Lies meine letzten Emails und fasse sie zusammen" + LLM: → requestToolbox("email") + +Runde 2: + Aktive Tools: core + email (outlook_readEmails, ...) + requestToolbox + LLM: → outlook_readEmails(connectionRef, folder="Inbox") + +Runde 3: + LLM: → (Text-Antwort mit Zusammenfassung) → COMPLETED +``` + +**requestToolbox Meta-Tool:** + +```python +{ + "name": "requestToolbox", + "description": "Request specialized tools for the current task.", + "parameters": { + "toolboxId": { + "type": "string", + "enum": [...] # Dynamisch: nur availableToolboxes + }, + "reason": {"type": "string"} + } +} +``` + +### 5.4 Toolbox-Registrierung (Architektur) + +``` +App Startup + │ + ├── _registerCoreTools(registry) → 40 Core-Tools in ToolRegistry + ├── ActionToolAdapter.registerAll(registry) → dynamicMode Actions als Tools + │ + └── ToolboxRegistry.registerAll() + ├── Toolbox "core" → readFile, listFiles, webSearch, ... (isDefault=true) + ├── Toolbox "ai" → summarizeContent, generateImage, ... + ├── Toolbox "datasources" → browseDataSource, downloadFromDataSource, ... + ├── Toolbox "email" → sendMail, outlook_readEmails, ... (requiresConnection="outlook") + ├── Toolbox "sharepoint" → sharepoint_findDocumentPath, ... (requiresConnection="sharepoint") + ├── Toolbox "clickup" → clickup_searchTasks, ... (requiresConnection="clickup") + ├── Toolbox "jira" → jira_connectJira, ... (requiresConnection="jira") + ├── Toolbox "workflow" → readWorkflowGraph, addNode, connectNodes, ... + ├── Toolbox "trustee" → trustee_extractFromFiles, ... (featureCode="trustee") + └── Toolbox "chatbot" → chatbot_queryDatabase, ... (featureCode="chatbot") + +Per Agent-Run: + │ + ├── Feature-Instanz Config → initialToolboxes + availableToolboxes + ├── User Connections → filtert availableToolboxes (nur was User nutzen kann) + │ + ├── Runde 1: getToolsForToolboxes(initialToolboxes) + requestToolbox + │ + └── Nach requestToolbox("email"): + Runde 2+: getToolsForToolboxes(initialToolboxes + ["email"]) + requestToolbox +``` + +### 5.5 Sub-Agent-Architektur + +```python +# Bestehendes Pattern: featureDataAgent.py + +# Aufruf-Kette: +# Main Agent → Tool "queryFeatureInstance" → _queryFeatureInstance() +# → runFeatureDataAgent(question, featureInstanceId, ...) +# → Eigene ToolRegistry (browseTable, queryTable) +# → runAgentLoop(prompt, registry, config=AgentConfig(maxRounds=5)) +# → Antwort zurück → ToolResult → Main Agent +``` + +**Sub-Agents sind komplementär zu Toolboxes:** +- **Toolbox-Eskalation:** Agent bekommt Tools direkt und nutzt sie selbst (z.B. Email-Tools) +- **Sub-Agent:** Agent delegiert an spezialisierten Mini-Agent mit eigenem Wissen (z.B. Trustee DB-Schema) + +Faustregel: Toolbox wenn die Tools generisch sind. Sub-Agent wenn Feature-spezifisches Kontext-/Schema-Wissen nötig ist. + +### 5.6 Toolbox-Zuordnung pro Feature + +| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) | +|---------|--------------------|------------------------------------| +| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira"]` — gefiltert nach User-Connections | +| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` | +| **CommCoach** | `["core"]` | `[]` (keine Eskalation) | +| **Chatbot** | (Eigenes Tool-System) | — | + +--- + +## 6. Graph-Struktur und Node-Definitionen + +### 6.1 Graph-Schema (innerhalb WorkflowVersion.graph) + +```json +{ + "nodes": [ + { + "id": "node-uuid", + "type": "trigger.schedule", + "position": { "x": 100, "y": 200 }, + "parameters": { + "cronExpression": "0 8 * * 1-5" + } + }, + { + "id": "node-uuid-2", + "type": "ai.prompt", + "position": { "x": 300, "y": 200 }, + "parameters": { + "prompt": "Analysiere die Emails und erstelle eine Zusammenfassung" + } + } + ], + "connections": [ + { + "source": "node-uuid", + "target": "node-uuid-2", + "sourceOutput": 0, + "targetInput": 0 + } + ] +} +``` + +### 6.2 Node-Definition-Schema + +```python +# Jede Node-Definition ist ein Dict mit folgender Struktur: +{ + "id": "ai.prompt", # Unique Node-Type-ID + "category": "ai", # Kategorie (für Palette-Gruppierung) + "label": {"en": "Prompt", "de": "Prompt"}, # Multilingual + "description": {"en": "...", "de": "..."}, + "parameters": [ + { + "name": "prompt", + "type": "string", + "required": True, + "description": {"en": "AI prompt", "de": "KI-Prompt"} + } + ], + "inputs": 1, # Anzahl Eingänge + "outputs": 1, # Anzahl Ausgänge + "meta": {"icon": "mdi-robot", "color": "#9C27B0"}, + + # Interne Felder (nicht an Frontend exponiert): + "_method": "ai", # Method für ActionExecutor + "_action": "process", # Action für ActionExecutor + "_paramMap": {"prompt": "aiPrompt"}, # Parameter-Mapping Node → Action +} +``` + +### 6.3 Node-Type zu Method/Action Mapping + +| Node Type | `_method` | `_action` | `_paramMap` | +|-----------|-----------|-----------|-------------| +| `trigger.manual` | — | — | — (TriggerExecutor) | +| `trigger.schedule` | — | — | — (TriggerExecutor) | +| `trigger.form` | — | — | — (TriggerExecutor) | +| `flow.ifElse` | — | — | — (FlowExecutor) | +| `flow.switch` | — | — | — (FlowExecutor) | +| `flow.loop` | — | — | — (FlowExecutor) | +| `input.*` | — | — | — (InputExecutor → HumanTask) | +| `ai.prompt` | `ai` | `process` | `prompt→aiPrompt` | +| `ai.webResearch` | `ai` | `webResearch` | `query→prompt` | +| `ai.summarizeDocument` | `ai` | `summarizeDocument` | — | +| `ai.translateDocument` | `ai` | `translateDocument` | `targetLanguage→targetLanguage` | +| `ai.generateDocument` | `ai` | `generateDocument` | `prompt→prompt` | +| `email.checkEmail` | `outlook` | `readEmails` | `connectionId→connectionReference` | +| `email.searchEmail` | `outlook` | `searchEmails` | `connectionId→connectionReference` | +| `email.draftEmail` | `outlook` | `composeAndDraft...` | `connectionId→connectionReference` | +| `sharepoint.*` | `sharepoint` | entsprechend | `connectionId→connectionReference` | +| `clickup.*` | `clickup` | entsprechend | `connectionId→connectionReference` | +| `file.create` | `file` | `create` | `template→template` | + +### 6.4 Execution-Routing + +``` +executeGraph(graph, services, ...) + │ + ├── parseGraph() → nodes, connections, nodeIds + ├── validateGraph() → Konsistenzprüfung + ├── topoSort(nodes, connectionMap) → geordnete Node-Liste + │ + └── Pro Node: + │ + ├── nodeType.startsWith("trigger.") → TriggerExecutor + │ → runEnvelope passthrough + │ + ├── nodeType.startsWith("flow.") → FlowExecutor + │ → ifElse: evaluiert Bedingung, setzt active path + │ → switch: evaluiert Match, setzt active path + │ → loop: iteriert mit _loopState + │ + ├── nodeType.startsWith("input.") → InputExecutor + │ → erstellt HumanTask + │ → setzt Run auf PAUSED + │ → raise PauseForHumanTaskError + │ + └── nodeType.startsWith("ai." | "email." | "sharepoint." | ...) → ActionNodeExecutor + │ + ├── _getNodeDefinition(nodeType) → _method, _action, _paramMap + ├── Parameter-Mapping (Node-Params → Action-Params) + ├── Upstream-Daten mergen (documents von vorherigen Nodes) + └── ActionExecutor.executeAction(method, action, params) +``` + +--- + +## 7. Scheduler-Architektur (konsolidiert) + +### 7.1 Konsolidierter Scheduler (Ziel) + +```python +# Ziel: workflows/scheduler/mainScheduler.py +# Übernimmt die besten Patterns aus v1 und v2 + +class WorkflowScheduler: + """Konsolidierter Scheduler für Workflow-Automationen.""" + + def start(self, eventUser): + """Startup: Initial Sync + Delayed Sync + Callback Registration.""" + eventManager.start() + self._syncScheduledWorkflows(eventUser) + self._registerDelayedSync(eventUser, delaySeconds=5) + callbackRegistry.register("workflow.changed", + lambda _: self._syncScheduledWorkflows(eventUser)) + + def stop(self, eventUser): + """Shutdown: Cleanup.""" + pass # APScheduler handles job cleanup + + def _syncScheduledWorkflows(self, eventUser): + """Inkrementeller Sync (v1-Pattern).""" + workflows = self._getAllSchedulableWorkflows() + + for wf in workflows: + jobId = f"workflow.{wf.id}" + + if wf.active and wf.currentVersionId: + # Register/Replace Job + version = self._getPublishedVersion(wf.currentVersionId) + cronKwargs = self._extractSchedule(version) + + if cronKwargs: + handler = self._createHandler(wf, version, eventUser) + eventManager.registerCron( + jobId=jobId, + func=handler, + cronKwargs=cronKwargs, + replaceExisting=True # v1-Pattern: atomic replace + ) + # Persist eventId on Workflow (v1-Pattern: debugging) + if wf.eventId != jobId: + self._updateEventId(wf.id, jobId) + else: + # Deactivated: Remove Job + Clear eventId + self._removeJob(jobId) + if wf.eventId: + self._updateEventId(wf.id, None) + + def _createHandler(self, wf, version, eventUser): + """Handler: Reload + Active-Check + Execute (v1-Pattern).""" + async def handler(): + # Reload Workflow (v1-Pattern: Zustand könnte sich geändert haben) + current = self._getWorkflow(wf.id) + if not current or not current.active: + return + + # Execute published version + services = self._buildServices(current, eventUser) + result = await executeGraph( + graph=version.graph, + services=services, + workflowId=current.id, + ... + ) + + # Execution Log (v1-Pattern: capped audit trail) + self._appendExecutionLog(current.id, result) + + # Thread-Bridge (v2-Pattern) + return self._wrapAsync(handler) +``` + +### 7.2 Scheduler State Machine + +``` +Workflow.active=false ──► UNREGISTERED (kein APScheduler Job) + │ + │ activate + published version mit schedule invocation + ▼ +Workflow.active=true ──► REGISTERED (eventId gesetzt, Job aktiv) + │ + ├── Cron fires → handler() → executeGraph → WorkflowRun + │ + ├── Workflow deactivated → remove job → UNREGISTERED (eventId=null) + │ + ├── Schedule geändert → replaceExisting=true → REGISTERED (neuer Trigger) + │ + └── Workflow gelöscht → remove job → (Workflow gelöscht) +``` + +--- + +## 8. Backend Code-Struktur (Ziel) + +``` +gateway/modules/ +│ +├── features/ +│ ├── graphicalEditor/ ◄── NEUES FEATURE (ersetzt automation2) +│ │ ├── mainGraphicalEditor.py +│ │ │ FEATURE_CODE = "graphicalEditor" +│ │ │ UI_OBJECTS = [ +│ │ │ "ui.feature.graphicalEditor.editor", +│ │ │ "ui.feature.graphicalEditor.workflows", +│ │ │ "ui.feature.graphicalEditor.tasks", +│ │ │ ] +│ │ │ RESOURCE_OBJECTS = [ +│ │ │ "resource.feature.graphicalEditor.execute", +│ │ │ "resource.feature.graphicalEditor.schedule", +│ │ │ ] +│ │ │ TEMPLATE_ROLES = [ +│ │ │ "graphicalEditor-admin", +│ │ │ "graphicalEditor-user", +│ │ │ "graphicalEditor-viewer", +│ │ │ ] +│ │ │ +│ │ ├── routeFeatureGraphicalEditor.py +│ │ │ router prefix: /api/workflows +│ │ │ Endpoints: CRUD Workflows, Versions, Execute, Runs, Tasks, NodeTypes, Chat +│ │ │ +│ │ ├── interfaceFeatureGraphicalEditor.py +│ │ │ DB: poweron_workflows (oder poweron_automation2 renamed) +│ │ │ Tables: Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask +│ │ │ +│ │ ├── datamodelFeatureGraphicalEditor.py +│ │ │ Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask +│ │ │ WorkflowStatus, RunStatus, StepStatus, TaskStatus (State Machines) +│ │ │ +│ │ └── nodeDefinitions/ +│ │ triggers.py, flow.py, input.py, ai.py, email.py, sharepoint.py, clickup.py, file.py +│ │ +│ ├── workspace/ ◄── BLEIBT (AI Chat mit UDB) +│ ├── automation/ ◄── BLEIBT bis Ablösung durch graphicalEditor +│ ├── commcoach/ ◄── BLEIBT +│ ├── chatbot/ ◄── BLEIBT +│ ├── trustee/ ◄── BLEIBT +│ └── ... +│ +├── serviceCenter/services/ +│ ├── serviceAgent/ +│ │ ├── mainServiceAgent.py ◄── Core Tools + Toolbox-Tagging +│ │ ├── agentLoop.py ◄── Toolbox-Filtering statt toolSet +│ │ ├── toolRegistry.py ◄── BLEIBT +│ │ ├── toolboxRegistry.py ◄── NEU: Toolbox-Verwaltung +│ │ ├── actionToolAdapter.py ◄── BLEIBT (+ Toolbox-Zuordnung) +│ │ ├── featureDataAgent.py ◄── BLEIBT (Sub-Agent Pattern) +│ │ └── datamodelAgent.py ◄── toolboxes: List[str] statt toolSet +│ └── ... +│ +├── workflows/ +│ ├── methods/ ◄── BLEIBT (Unified Action Library) +│ ├── processing/ ◄── BLEIBT (ActionExecutor, methodDiscovery) +│ ├── automation2/ ◄── Graph Engine BLEIBT (execution, executors, graphUtils) +│ ├── scheduler/ ◄── NEU (konsolidierter Scheduler) +│ │ └── mainScheduler.py +│ ├── automation/ ◄── BLEIBT bis Ablösung +│ └── workflowManager.py ◄── BLEIBT (für Workspace Dynamic Mode) +│ +└── ... +``` + +--- + +## 9. Frontend Code-Struktur (Ziel) + +``` +frontend_nyla/src/ +│ +├── components/ ◄── SHARED COMPONENTS +│ ├── ChatBar/ ◄── NEU (extrahiert aus WorkspaceInput) +│ │ ├── ChatBar.tsx Props: onSend, onStop, onVoice, +│ │ │ fileAttachments, showProviderSelector, +│ │ │ showVoice, placeholder +│ │ ├── ChatBar.module.css +│ │ └── index.ts +│ │ +│ ├── ChatStream/ ◄── NEU (extrahiert aus workspace/ChatStream) +│ │ ├── ChatStream.tsx SSE-driven message stream, wiederverwendbar +│ │ └── index.ts +│ │ +│ ├── UnifiedDataBar/ ◄── BLEIBT +│ │ +│ ├── FlowEditor/ ◄── RENAMED von Automation2FlowEditor +│ │ ├── editor/ +│ │ │ ├── FlowEditor.tsx + AI Chat Panel (ChatBar + ChatStream) +│ │ │ ├── FlowCanvas.tsx + Visual Run Tracing +│ │ │ ├── EditorChatPanel.tsx ◄── NEU +│ │ │ └── ... +│ │ └── ... +│ │ +│ └── ... +│ +├── pages/views/ +│ ├── workspace/ +│ │ ├── WorkspacePage.tsx Nutzt ChatBar, ChatStream, UDB +│ │ └── ... +│ │ +│ ├── graphicalEditor/ ◄── RENAMED von automation2 +│ │ ├── GraphicalEditorPage.tsx FlowEditor + UDB + ChatPanel +│ │ ├── WorkflowsListPage.tsx +│ │ └── TasksPage.tsx +│ │ +│ └── ... +│ +├── api/ +│ ├── workflowApi.ts ◄── Konsolidiert (ersetzt automationApi + automation2Api) +│ └── ... +│ +└── ... +``` + +--- + +## 10. Zusammenfassung: Architektur-Prinzipien + +| Prinzip | Umsetzung | +|---------|-----------| +| **Feature-Container-Pattern** | Jedes Feature folgt dem gleichen Verzeichnis-Pattern mit automatischer Discovery. Kein manuelles Wiring. | +| **RBAC auf zwei Ebenen** | Mandate-Rollen (generell) + Instanz-Rollen (feature-spezifisch). Höchste Priorität gewinnt. | +| **State Machines als Grundlage** | WorkflowVersion, WorkflowRun, HumanTask und Scheduler haben klar definierte Zustände und Übergänge. | +| **Unified Action Library** | Eine Method/Action-Library für alle Consumers (Agent Tools, Graph Nodes, Workflow Processor). | +| **Toolboxes statt flache Tool-Liste** | Thematisch gruppierte Tools, kontextabhängig aktiviert. Skaliert auf 100+ Tools. | +| **Sub-Agents für Feature-Daten** | Main Agent bleibt schlank. Feature-spezifisches Wissen in dedizierten Sub-Agents. | +| **Keine Migration** | Direkte Implementierung auf Automation2-Basis. v1 bleibt bis Ablösung. | +| **Scheduler konsolidiert** | Beste Patterns aus v1 (inkrementell, eventId, reload+check) und v2 (interval, thread-bridge). |