Status : IMPLEMENTEE
{{nodes.search.output.messages[0].id}} est complexe┌─────────────────────────────────────────────────────────────────────────────┐
│ ROUTINE: Gmail non lus [Sauvegarder] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ TRIGGER ─────────────────────────────────────────────────────────────┐ │
│ │ manual params: query │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─ ACTION: Recherche Gmail ─────────────────────────────────────────────┐ │
│ │ gmail.message.list q: {{trigger.params.query}} │ │
│ │ Output: messages[] [Config] │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─ LOOP: Pour chaque email ─────────────────────────────────────────────┐ │
│ │ items: {{nodes.search.output.messages}} │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ BODY (exécuté pour chaque item) [+ Node] │ │ │
│ │ │ │ │ │
│ │ │ ┌─ ACTION: Détails ──────────────────────────────────────────┐│ │ │
│ │ │ │ gmail.message.get id: {{loop.item.id}} ││ │ │
│ │ │ └────────────────────────────────────────────────────────────┘│ │ │
│ │ │ │ │ │ │
│ │ │ ┌─ TRANSFORM ─────────────────────────────────────────────────┐│ │ │
│ │ │ │ pick: [id, snippet, from] ││ │ │
│ │ │ └────────────────────────────────────────────────────────────┘│ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─ OUTPUT ──────────────────────────────────────────────────────────────┐ │
│ │ {{nodes.loop.output}} │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Loop :
Condition :
┌─ CONDITION: Has results ───────────────────────────┐
│ {{nodes.search.output.messages.length}} > 0 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ TRUE branch │ │ FALSE branch │ │
│ │ [+ Node] │ │ [+ Node] │ │
│ └─────────────────┘ └─────────────────┘ │
└────────────────────────────────────────────────────┘
Bouton "Ask AI" à côté de chaque champ expression :
┌─ Input mapping ──────────────────────────────────────────────────────┐
│ q: [{{trigger.params.query}} ] [?] [AI] │
│ │
│ ┌─ AI Assistant ───────────────────────────────────────────────────┐ │
│ │ "Je veux chercher les emails non lus de la dernière semaine" │ │
│ │ [Générer] │ │
│ │ │ │
│ │ Suggestion: │ │
│ │ is:unread after:{{env.DATE | dateSubtract(7)}} │ │
│ │ [Appliquer] │ │
│ └───────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────┘
L'IA a accès à :
Panneau latéral montrant tout ce qui est disponible :
┌─ Contexte disponible ────────────────────┐
│ │
│ ▼ trigger │
│ └─ params │
│ └─ query: string │
│ │
│ ▼ nodes │
│ ├─ search (action) │
│ │ └─ output │
│ │ ├─ messages: array [10] │
│ │ │ └─ [0] │
│ │ │ ├─ id: "abc123" │
│ │ │ └─ threadId: "xyz" │
│ │ └─ nextPageToken: string │
│ └─ loop (loop) │
│ └─ output: array │
│ │
│ ▼ variables │
│ └─ count: 0 │
│ │
│ ▼ loop (si dans un loop) │
│ ├─ item: object │
│ │ ├─ id: "abc123" │
│ │ └─ threadId: "xyz" │
│ └─ index: 0 │
│ │
│ ▼ env │
│ ├─ DATE: "2026-02-03" │
│ └─ TIMESTAMP: 1738600000 │
│ │
│ [Clic pour insérer dans le champ actif] │
└──────────────────────────────────────────┘
Click-to-insert : Cliquer sur un élément l'insère dans le champ actuellement focus.
Au lieu d'écrire du JSON, interface visuelle :
┌─ Transform: Extraire infos ─────────────────────────────────────────┐
│ │
│ Input: [{{nodes.fetch.output}} ] [Browse...] │
│ │
│ Operations: [+ Operation] │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 1. [pick ▼] keys: [id] [snippet] [from] [+] │ │
│ │ [×] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 2. [filter ▼] key: [from] [contains ▼] value: [@gmail] │ │
│ │ [×] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ Preview output: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ [ │ │
│ │ { "id": "abc", "snippet": "Hello...", "from": "x@gmail" } │ │
│ │ ] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ [Ask AI: "Je veux garder seulement les emails de Google"] │
└──────────────────────────────────────────────────────────────────────┘
Pour les utilisateurs avancés, toggle vers mode JSON brut :
┌─ Transform ──────────────────────── [Visual] [Code] ─┐
│ { │
│ "input": "{{nodes.fetch.output}}", │
│ "operations": [ │
│ { "type": "pick", "config": { "keys": [...] }}, │
│ { "type": "filter", "config": {...} } │
│ ] │
│ } │
└──────────────────────────────────────────────────────┘
Écrire une expression : "Je veux l'email du premier message"
→ {{nodes.search.output.messages[0].payload.headers | find('From').value}}
Créer un transform : "Extraire sujet et date de chaque email" → Génère les operations pick + template
Débugger : "Pourquoi mon loop ne fonctionne pas ?" → Analyse la config et suggère des corrections
Générer une routine complète : "Routine pour archiver les vieux emails" → Génère tous les nodes avec la bonne structure
POST /api/ai/routine-assist
{
"routine": { ... }, // Routine actuelle
"current_node": "transform1", // Node en cours d'édition
"field": "input", // Champ focus
"prompt": "Je veux filtrer par date",
"context": { // Contexte live si dispo
"nodes.search.output": { ... }
}
}
/api/ai/routine-assistsrc/components/routines/LoopNodeEditor.tsxsrc/components/routines/ConditionNodeEditor.tsxsrc/components/routines/TransformBuilder.tsxsrc/components/routines/ContextExplorer.tsxsrc/components/routines/AIAssistant.tsxsrc/components/routines/ExpressionInput.tsx (avec AI + context browse)src/routes/routines/[id]/edit/index.tsx - Refonte complète du layoutsrc/components/routines/NodeConfigForm.tsx - Intégrer les nouveaux composantssrc/routes/ai-routine-assist.ts - Endpoint IAEstimation : Gros chantier, 3-4 sessions de travail