{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://automation.zokastech.com/docs/schema/zokas.package-1.schema.json",
  "title": "Package ZOKAS Automate (zokas.package/1)",
  "description": "Processus ZOKAS Automate. Documentation : https://automation.zokastech.com/docs/reference/package",
  "type": "object",
  "required": [
    "format",
    "name",
    "steps"
  ],
  "properties": {
    "format": {
      "const": "zokas.package/1"
    },
    "name": {
      "type": "string",
      "minLength": 1
    },
    "version": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "project": {
      "type": "string"
    },
    "screenshots": {
      "enum": [
        "robot",
        "nav",
        "all",
        "off"
      ]
    },
    "roi": {
      "type": "object",
      "properties": {
        "minutes_per_run": {
          "type": [
            "string",
            "number"
          ]
        },
        "hourly_cost": {
          "type": [
            "string",
            "number"
          ]
        }
      }
    },
    "variables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    },
    "steps": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/step"
      }
    }
  },
  "$defs": {
    "props:web.open": {
      "title": "web.open — Ouvrir une page web",
      "description": "Ouvre une adresse dans le navigateur du robot et attend que la page soit chargée.",
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "description": "Adresse complète (https://…). Peut contenir des variables.",
          "examples": [
            "{{url_portail}}/factures"
          ]
        }
      },
      "required": [
        "url"
      ],
      "additionalProperties": false
    },
    "props:ui.click": {
      "title": "ui.click — Cliquer sur un élément",
      "description": "Clique sur un élément de la page web décrit en langage naturel ou par un sélecteur.",
      "type": "object",
      "properties": {
        "target": {
          "type": "string",
          "description": "Élément visé, décrit comme on le dirait à quelqu'un : « bouton Valider », « lien Factures », ou sélecteur CSS (#valider).",
          "examples": [
            "bouton « Comptabiliser le lot »"
          ]
        }
      },
      "required": [
        "target"
      ],
      "additionalProperties": false
    },
    "props:ui.type": {
      "title": "ui.type — Saisir du texte",
      "description": "Remplit un champ de la page web avec un texte (variables comprises).",
      "type": "object",
      "properties": {
        "target": {
          "type": "string",
          "description": "Champ visé : « champ Montant », « zone de recherche », #montant.",
          "examples": [
            "champ Montant"
          ]
        },
        "text": {
          "type": "string",
          "description": "Texte saisi. Le contenu précédent du champ est remplacé.",
          "examples": [
            "{{facture.montant}}"
          ]
        }
      },
      "required": [
        "target",
        "text"
      ],
      "additionalProperties": false
    },
    "props:ui.key": {
      "title": "ui.key — Touche dans la page",
      "description": "Envoie une touche ou un raccourci à la page web (enter, tab, esc, ctrl+a…).",
      "type": "object",
      "properties": {
        "combo": {
          "type": "string",
          "description": "Touche ou combinaison, en minuscules, séparée par « + ».",
          "examples": [
            "enter"
          ]
        }
      },
      "required": [
        "combo"
      ],
      "additionalProperties": false
    },
    "props:ui.read": {
      "title": "ui.read — Lire un texte à l'écran",
      "description": "Lit le texte d'un élément de la page et l'enregistre dans une variable.",
      "type": "object",
      "properties": {
        "target": {
          "type": "string",
          "description": "Élément dont on lit le texte.",
          "examples": [
            "statut du lot"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable qui reçoit le texte.",
          "default": "resultat",
          "examples": [
            "statut"
          ]
        }
      },
      "required": [
        "target"
      ],
      "additionalProperties": false
    },
    "props:ui.table": {
      "title": "ui.table — Extraire un tableau",
      "description": "Transforme un tableau HTML en liste de lignes ; les clés sont les en-têtes de colonnes.",
      "type": "object",
      "properties": {
        "target": {
          "type": "string",
          "description": "Tableau visé : « table », « #factures », « tableau des commandes ».",
          "default": "table",
          "examples": [
            "#factures"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable liste qui reçoit les lignes.",
          "default": "resultat",
          "examples": [
            "factures"
          ]
        }
      },
      "required": [
        "target"
      ],
      "additionalProperties": false
    },
    "props:ui.image": {
      "title": "ui.image — Agir sur un objet (image)",
      "description": "Retrouve un objet à l'écran à partir de son image (même déplacé ou restylé), puis clique, saisit ou vérifie sa présence.",
      "type": "object",
      "properties": {
        "template": {
          "type": "string",
          "description": "Image PNG (base64) de l'objet, choisie dans Studio › Choisir sur une capture. Ne pas écrire à la main."
        },
        "surface": {
          "type": "string",
          "description": "Où chercher.",
          "enum": [
            "screen",
            "web"
          ],
          "default": "screen"
        },
        "action": {
          "type": "string",
          "description": "Action à effectuer.",
          "enum": [
            "click",
            "double",
            "right",
            "hover",
            "type",
            "wait",
            "exists"
          ],
          "default": "click"
        },
        "text": {
          "type": "string",
          "description": "Texte saisi (action « type »)."
        },
        "submit": {
          "type": "string",
          "description": "Appuie sur Entrée après la saisie.",
          "default": "false"
        },
        "threshold": {
          "type": "string",
          "description": "Ressemblance minimale, de 0 à 1.",
          "default": "0.8"
        },
        "timeout": {
          "type": "string",
          "description": "Secondes d'attente de l'objet (120 maximum).",
          "default": "10"
        },
        "ref_x": {
          "type": "string",
          "description": "Position d'origine horizontale (0 à 1), renseignée par Studio."
        },
        "ref_y": {
          "type": "string",
          "description": "Position d'origine verticale (0 à 1), renseignée par Studio."
        },
        "name": {
          "type": "string",
          "description": "Libellé de l'objet : le retrouve même si son aspect change.",
          "examples": [
            "Nouveau document"
          ]
        },
        "role": {
          "type": "string",
          "description": "Rôle accessible (bouton, champ…) pour départager les candidats."
        },
        "offset_x": {
          "type": "string",
          "description": "Décalage horizontal du clic, en points.",
          "default": "0"
        },
        "offset_y": {
          "type": "string",
          "description": "Décalage vertical du clic, en points.",
          "default": "0"
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat : position trouvée, ou vrai/faux pour « exists ».",
          "default": "trouve"
        }
      },
      "required": [
        "template"
      ],
      "additionalProperties": false
    },
    "props:ui.type_secret": {
      "title": "ui.type_secret — Saisir un identifiant du coffre",
      "description": "Saisit un identifiant ou un mot de passe du coffre dans un champ ; la valeur n'apparaît jamais dans le journal, les captures ni les messages à l'IA.",
      "type": "object",
      "properties": {
        "target": {
          "type": "string",
          "description": "Champ visé.",
          "examples": [
            "champ Mot de passe"
          ]
        },
        "credential": {
          "type": "string",
          "description": "Nom de l'identifiant dans le Coffre.",
          "examples": [
            "ERP Comptabilité"
          ]
        },
        "field": {
          "type": "string",
          "description": "Valeur saisie : le secret ou l'identifiant.",
          "enum": [
            "password",
            "username"
          ],
          "default": "password"
        }
      },
      "required": [
        "target",
        "credential"
      ],
      "additionalProperties": false
    },
    "props:file.list": {
      "title": "file.list — Lister un dossier",
      "description": "Liste les fichiers d'un dossier du poste du robot, avec un filtre facultatif.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Dossier (~ = dossier personnel).",
          "examples": [
            "~/Documents/Factures"
          ]
        },
        "pattern": {
          "type": "string",
          "description": "Filtre de noms (jokers * et ?).",
          "default": "*",
          "examples": [
            "*.pdf"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable liste.",
          "default": "resultat",
          "examples": [
            "fichiers"
          ]
        }
      },
      "required": [
        "path"
      ],
      "additionalProperties": false
    },
    "props:file.read": {
      "title": "file.read — Lire un document",
      "description": "Lit un fichier : texte (txt, md, pdf, docx), lignes (csv, xlsx) ou données (json).",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Fichier à lire.",
          "examples": [
            "{{fichier.path}}"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "default": "resultat",
          "examples": [
            "texte"
          ]
        }
      },
      "required": [
        "path"
      ],
      "additionalProperties": false
    },
    "props:file.search": {
      "title": "file.search — Chercher dans les documents",
      "description": "Cherche un texte dans les documents d'un dossier et renvoie les fichiers trouvés avec un extrait.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Dossier de recherche.",
          "examples": [
            "~/Documents"
          ]
        },
        "query": {
          "type": "string",
          "description": "Texte cherché (insensible à la casse).",
          "examples": [
            "Nordinfo"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable liste.",
          "default": "resultat",
          "examples": [
            "resultats"
          ]
        }
      },
      "required": [
        "path",
        "query"
      ],
      "additionalProperties": false
    },
    "props:file.write": {
      "title": "file.write — Produire un fichier",
      "description": "Écrit un fichier dont le format dépend de l'extension : .txt .md .csv .xlsx .json .docx.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Fichier à produire (dossiers créés si besoin).",
          "examples": [
            "~/Documents/rapport-{{date}}.docx"
          ]
        },
        "content": {
          "type": "string",
          "description": "Contenu. Pour .csv et .xlsx : une liste JSON de lignes (ex. {{lignes}}).",
          "examples": [
            "# Rapport\n{{resume}}"
          ]
        }
      },
      "required": [
        "path",
        "content"
      ],
      "additionalProperties": false
    },
    "props:file.move": {
      "title": "file.move — Déplacer / ranger",
      "description": "Déplace un fichier ; le dossier de destination est créé si besoin.",
      "type": "object",
      "properties": {
        "source": {
          "type": "string",
          "description": "Fichier à déplacer.",
          "examples": [
            "{{fichier.path}}"
          ]
        },
        "destination": {
          "type": "string",
          "description": "Dossier (terminé par /) ou chemin complet.",
          "examples": [
            "~/Documents/Classement/{{categorie}}/"
          ]
        }
      },
      "required": [
        "source",
        "destination"
      ],
      "additionalProperties": false
    },
    "props:file.copy": {
      "title": "file.copy — Copier",
      "description": "Copie un fichier vers un dossier ou un chemin.",
      "type": "object",
      "properties": {
        "source": {
          "type": "string",
          "description": "Fichier à copier."
        },
        "destination": {
          "type": "string",
          "description": "Dossier ou chemin.",
          "examples": [
            "~/Documents/Archive/"
          ]
        }
      },
      "required": [
        "source",
        "destination"
      ],
      "additionalProperties": false
    },
    "props:file.trash": {
      "title": "file.trash — Mettre à la Corbeille",
      "description": "Place un fichier dans la Corbeille (récupérable), jamais de suppression définitive.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Fichier."
        }
      },
      "required": [
        "path"
      ],
      "additionalProperties": false
    },
    "props:file.mkdir": {
      "title": "file.mkdir — Créer un dossier",
      "description": "Crée un dossier et ses parents.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Dossier à créer.",
          "examples": [
            "~/Documents/Classement/{{annee}}"
          ]
        }
      },
      "required": [
        "path"
      ],
      "additionalProperties": false
    },
    "props:app.open": {
      "title": "app.open — Ouvrir une application",
      "description": "Ouvre une application du poste, éventuellement avec un fichier.",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "Nom de l'application.",
          "examples": [
            "Microsoft Excel"
          ]
        },
        "path": {
          "type": "string",
          "description": "Fichier à ouvrir avec l'application (facultatif)."
        }
      },
      "required": [
        "name"
      ],
      "additionalProperties": false
    },
    "props:desktop.type": {
      "title": "desktop.type — Saisir au clavier",
      "description": "Tape un texte dans l'application au premier plan (ou celle indiquée).",
      "type": "object",
      "properties": {
        "text": {
          "type": "string",
          "description": "Texte saisi.",
          "examples": [
            "{{resume}}"
          ]
        },
        "app": {
          "type": "string",
          "description": "Application à activer avant la saisie (facultatif).",
          "examples": [
            "TextEdit"
          ]
        }
      },
      "required": [
        "text"
      ],
      "additionalProperties": false
    },
    "props:desktop.key": {
      "title": "desktop.key — Raccourci clavier",
      "description": "Envoie un raccourci à l'application (cmd+s, enter, tab…).",
      "type": "object",
      "properties": {
        "combo": {
          "type": "string",
          "description": "Touches séparées par « + ».",
          "examples": [
            "cmd+s"
          ]
        },
        "app": {
          "type": "string",
          "description": "Application (facultatif)."
        }
      },
      "required": [
        "combo"
      ],
      "additionalProperties": false
    },
    "props:desktop.click": {
      "title": "desktop.click — Cliquer à l'écran",
      "description": "Clique à des coordonnées de l'écran, en points.",
      "type": "object",
      "properties": {
        "x": {
          "type": "string",
          "description": "Abscisse en points.",
          "examples": [
            "640"
          ]
        },
        "y": {
          "type": "string",
          "description": "Ordonnée en points.",
          "examples": [
            "410"
          ]
        }
      },
      "required": [
        "x",
        "y"
      ],
      "additionalProperties": false
    },
    "props:mail.read": {
      "title": "mail.read — Lire des e-mails",
      "description": "Lit les messages d'un compte e-mail (IMAP) configuré dans l'Orchestrator.",
      "type": "object",
      "properties": {
        "account": {
          "type": "string",
          "description": "Nom du compte e-mail (ZOKAS Agent › E-mail).",
          "examples": [
            "Compta"
          ]
        },
        "folder": {
          "type": "string",
          "description": "Dossier IMAP.",
          "default": "INBOX"
        },
        "unread_only": {
          "type": "string",
          "description": "Messages non lus seulement.",
          "default": "true"
        },
        "from": {
          "type": "string",
          "description": "Filtre sur l'expéditeur (contient)."
        },
        "subject": {
          "type": "string",
          "description": "Filtre sur le sujet (contient).",
          "examples": [
            "Facture"
          ]
        },
        "limit": {
          "type": "string",
          "description": "Nombre maximum de messages.",
          "default": "20"
        },
        "mark_seen": {
          "type": "string",
          "description": "Marquer les messages comme lus.",
          "default": "false"
        },
        "into": {
          "type": "string",
          "description": "Variable liste.",
          "default": "emails"
        }
      },
      "required": [
        "account"
      ],
      "additionalProperties": false
    },
    "props:mail.attachments": {
      "title": "mail.attachments — Enregistrer les pièces jointes",
      "description": "Enregistre dans un dossier les pièces jointes d'un message lu par mail.read.",
      "type": "object",
      "properties": {
        "message": {
          "type": "string",
          "description": "Variable qui contient le message (élément de boucle).",
          "default": "email",
          "examples": [
            "email"
          ]
        },
        "folder": {
          "type": "string",
          "description": "Dossier de destination.",
          "examples": [
            "~/Documents/Factures"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable liste des fichiers enregistrés.",
          "default": "pieces_jointes"
        }
      },
      "required": [
        "folder"
      ],
      "additionalProperties": false
    },
    "props:mail.send": {
      "title": "mail.send — Envoyer un e-mail",
      "description": "Envoie un message (SMTP) depuis un compte configuré, avec pièces jointes.",
      "type": "object",
      "properties": {
        "account": {
          "type": "string",
          "description": "Compte e-mail d'envoi.",
          "examples": [
            "Compta"
          ]
        },
        "to": {
          "type": "string",
          "description": "Destinataires, séparés par des virgules.",
          "examples": [
            "compta@exemple.fr"
          ]
        },
        "cc": {
          "type": "string",
          "description": "Copie."
        },
        "subject": {
          "type": "string",
          "description": "Sujet.",
          "examples": [
            "Rapport du {{date}}"
          ]
        },
        "body": {
          "type": "string",
          "description": "Corps du message.",
          "examples": [
            "Bonjour,\n\n{{resume}}"
          ]
        },
        "html": {
          "type": "string",
          "description": "Corps en HTML.",
          "default": "false"
        },
        "attachments": {
          "type": "string",
          "description": "Chemins de fichiers séparés par des virgules."
        }
      },
      "required": [
        "account",
        "to",
        "subject"
      ],
      "additionalProperties": false
    },
    "props:data.assign": {
      "title": "data.assign — Affecter une variable",
      "description": "Donne une valeur (texte interpolé) à une variable.",
      "type": "object",
      "properties": {
        "variable": {
          "type": "string",
          "description": "Nom de la variable.",
          "examples": [
            "libelle"
          ]
        },
        "value": {
          "type": "string",
          "description": "Valeur (interpolée).",
          "examples": [
            "{{facture.fournisseur}} — {{facture.montant}} €"
          ]
        }
      },
      "required": [
        "variable"
      ],
      "additionalProperties": false
    },
    "props:data.table": {
      "title": "data.table — Lire un tableau CSV",
      "description": "Transforme un contenu CSV (séparateur ; ou ,) en liste de lignes.",
      "type": "object",
      "properties": {
        "csv": {
          "type": "string",
          "description": "Contenu CSV avec ligne d'en-tête. Séparateur détecté sur la première ligne.",
          "examples": [
            "fournisseur;montant\nNordinfo;2450"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable liste.",
          "examples": [
            "lignes"
          ]
        }
      },
      "required": [
        "csv",
        "into"
      ],
      "additionalProperties": false
    },
    "props:http.call": {
      "title": "http.call — Appeler une API",
      "description": "Envoie une requête HTTP depuis le robot et enregistre la réponse (statut, corps, JSON).",
      "type": "object",
      "properties": {
        "method": {
          "type": "string",
          "description": "Méthode HTTP.",
          "enum": [
            "GET",
            "POST",
            "PUT",
            "PATCH",
            "DELETE"
          ],
          "default": "GET"
        },
        "url": {
          "type": "string",
          "description": "Adresse appelée.",
          "examples": [
            "https://api.exemple.fr/v1/dossiers"
          ]
        },
        "headers": {
          "type": "string",
          "description": "En-têtes « Nom: valeur » (un par ligne) ou objet JSON."
        },
        "body": {
          "type": "string",
          "description": "Corps : JSON (envoyé en application/json) ou texte brut.",
          "examples": [
            "{\"numero\": \"{{numero}}\"}"
          ]
        },
        "credential": {
          "type": "string",
          "description": "Identifiant du Coffre utilisé pour l'authentification."
        },
        "auth": {
          "type": "string",
          "description": "Mode d'authentification avec « credential » : bearer, basic, ou header:Nom-En-Tête.",
          "default": "bearer",
          "examples": [
            "header:X-API-Key"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de réponse.",
          "default": "reponse"
        }
      },
      "required": [
        "url"
      ],
      "additionalProperties": false
    },
    "props:cmd.run": {
      "title": "cmd.run — Exécuter une commande",
      "description": "Lance une commande sur le poste du robot (npm, podman, git, curl…), suit sa sortie et récupère son code de sortie.",
      "type": "object",
      "properties": {
        "command": {
          "type": "string",
          "description": "Une seule commande, sans interpréteur : pas de « ; », « && », « | » ni redirection. « NOM=valeur programme » est accepté.",
          "examples": [
            "npm run release:mac:arm64"
          ]
        },
        "cwd": {
          "type": "string",
          "description": "Dossier de travail (dans les dossiers autorisés au robot).",
          "default": "première racine autorisée",
          "examples": [
            "{{depot}}/desktop"
          ]
        },
        "env": {
          "type": "string",
          "description": "Variables d'environnement, une par ligne NOM=valeur."
        },
        "credential": {
          "type": "string",
          "description": "Identifiant du Coffre injecté en variable d'environnement."
        },
        "credential_env": {
          "type": "string",
          "description": "Variable qui reçoit le secret (l'identifiant va dans NOM_USER).",
          "default": "ZOKAS_SECRET",
          "examples": [
            "ZOKAS_HUB_TOKEN"
          ]
        },
        "timeout": {
          "type": "string",
          "description": "Délai maximal en secondes (6 h maximum).",
          "default": "3600"
        },
        "detach": {
          "type": "string",
          "description": "Lancer en arrière-plan sans attendre (redémarrage d'un service).",
          "default": "false"
        },
        "delay": {
          "type": "string",
          "description": "Secondes d'attente avant le lancement (avec detach).",
          "default": "0"
        },
        "allow_fail": {
          "type": "string",
          "description": "Ne pas arrêter le processus si le code de sortie n'est pas 0.",
          "default": "false"
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "default": "commande"
        }
      },
      "required": [
        "command"
      ],
      "additionalProperties": false
    },
    "props:ai.agent": {
      "title": "ai.agent — Agent IA",
      "description": "Applique une consigne libre à une donnée (résumer, reformuler, extraire, rédiger) avec le moteur IA configuré.",
      "type": "object",
      "properties": {
        "instruction": {
          "type": "string",
          "description": "Consigne, en français.",
          "examples": [
            "Résume ce texte en une phrase."
          ]
        },
        "input": {
          "type": "string",
          "description": "Donnée à traiter.",
          "examples": [
            "{{texte}}"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "examples": [
            "resume"
          ]
        }
      },
      "required": [
        "instruction",
        "input",
        "into"
      ],
      "additionalProperties": false
    },
    "props:ai.classify": {
      "title": "ai.classify — Classer avec l'IA",
      "description": "Range un texte dans exactement une catégorie d'une liste fermée ; la décision est tracée.",
      "type": "object",
      "properties": {
        "input": {
          "type": "string",
          "description": "Texte à classer.",
          "examples": [
            "{{facture.objet}}"
          ]
        },
        "labels": {
          "type": "string",
          "description": "Catégories séparées par des virgules.",
          "examples": [
            "Informatique, Fournitures, Prestation de service"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "examples": [
            "categorie"
          ]
        }
      },
      "required": [
        "input",
        "labels",
        "into"
      ],
      "additionalProperties": false
    },
    "props:doc.extract": {
      "title": "doc.extract — Lire un document (extraction)",
      "description": "Extrait des champs typés d'un document (facture, bon de commande…) ; validation humaine si la confiance est faible.",
      "type": "object",
      "properties": {
        "path": {
          "type": "string",
          "description": "Fichier (pdf, image, docx…).",
          "examples": [
            "{{fichier.path}}"
          ]
        },
        "fields": {
          "type": "string",
          "description": "Champs séparés par des virgules, au format nom[:type[:libellé]] ; types : texte, nombre, date.",
          "examples": [
            "numero, date:date, fournisseur, montant_ttc:nombre:Montant TTC"
          ]
        },
        "instructions": {
          "type": "string",
          "description": "Consignes complémentaires."
        },
        "min_confidence": {
          "type": "string",
          "description": "Confiance minimale (0 à 1) sous laquelle un humain valide.",
          "default": "0.8"
        },
        "validate": {
          "type": "string",
          "description": "Validation humaine.",
          "enum": [
            "auto",
            "always",
            "never"
          ],
          "default": "auto"
        },
        "assignee": {
          "type": "string",
          "description": "Validateur.",
          "examples": [
            "Comptabilité"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "default": "document"
        }
      },
      "required": [
        "path",
        "fields"
      ],
      "additionalProperties": false
    },
    "props:human.approve": {
      "title": "human.approve — Validation humaine",
      "description": "Suspend le job jusqu'à la décision d'une personne (approuver / rejeter).",
      "type": "object",
      "properties": {
        "question": {
          "type": "string",
          "description": "Question posée, avec le contexte utile.",
          "examples": [
            "Valider la facture {{facture.fournisseur}} de {{facture.montant}} € ?"
          ]
        },
        "assignee": {
          "type": "string",
          "description": "Rôle ou personne.",
          "examples": [
            "Contrôle de gestion"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de décision.",
          "default": "decision"
        }
      },
      "required": [
        "question"
      ],
      "additionalProperties": false
    },
    "props:human.form": {
      "title": "human.form — Formulaire humain",
      "description": "Demande à une personne de compléter des champs ; le job reprend avec les valeurs saisies.",
      "type": "object",
      "properties": {
        "question": {
          "type": "string",
          "description": "Consigne affichée.",
          "default": "Compléter le formulaire",
          "examples": [
            "Compléter l'imputation"
          ]
        },
        "fields": {
          "type": "string",
          "description": "Champs séparés par des virgules (nom[:type[:libellé]]).",
          "examples": [
            "compte, centre_de_cout"
          ]
        },
        "values": {
          "type": "string",
          "description": "Valeurs pré-remplies (objet JSON).",
          "examples": [
            "{\"compte\": \"606100\"}"
          ]
        },
        "assignee": {
          "type": "string",
          "description": "Rôle ou personne.",
          "examples": [
            "Comptabilité"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable objet.",
          "default": "formulaire"
        }
      },
      "required": [
        "fields"
      ],
      "additionalProperties": false
    },
    "props:log.write": {
      "title": "log.write — Écrire dans le journal",
      "description": "Ajoute une ligne au journal du job.",
      "type": "object",
      "properties": {
        "message": {
          "type": "string",
          "description": "Message (interpolé).",
          "examples": [
            "Lot de {{index}} facture(s) traité"
          ]
        },
        "level": {
          "type": "string",
          "description": "Niveau.",
          "enum": [
            "info",
            "warn",
            "error"
          ],
          "default": "info"
        }
      },
      "required": [
        "message"
      ],
      "additionalProperties": false
    },
    "props:queue.add": {
      "title": "queue.add — Ajouter à une file",
      "description": "Ajoute un élément à une file de travail ; une référence déjà présente est ignorée.",
      "type": "object",
      "properties": {
        "queue": {
          "type": "string",
          "description": "Nom de la file (ZOKAS Agent › Files de travail).",
          "examples": [
            "Factures à saisir"
          ]
        },
        "reference": {
          "type": "string",
          "description": "Référence unique (anti-doublon).",
          "examples": [
            "{{ligne.numero}}"
          ]
        },
        "data": {
          "type": "string",
          "description": "Données de l'élément (objet JSON ou variable).",
          "examples": [
            "{{ligne}}"
          ]
        },
        "priority": {
          "type": "string",
          "description": "Priorité (plus grand = plus tôt).",
          "default": "0"
        },
        "into": {
          "type": "string",
          "description": "Variable qui reçoit l'identifiant de l'élément."
        }
      },
      "required": [
        "queue"
      ],
      "additionalProperties": false
    },
    "props:queue.next": {
      "title": "queue.next — Prendre le prochain élément",
      "description": "Réserve le prochain élément d'une file pour ce job.",
      "type": "object",
      "properties": {
        "queue": {
          "type": "string",
          "description": "Nom de la file."
        },
        "into": {
          "type": "string",
          "description": "Variable élément.",
          "default": "item"
        }
      },
      "required": [
        "queue"
      ],
      "additionalProperties": false
    },
    "props:queue.done": {
      "title": "queue.done — Élément traité",
      "description": "Marque l'élément comme traité, avec un résultat facultatif.",
      "type": "object",
      "properties": {
        "item": {
          "type": "string",
          "description": "Variable de l'élément (sans accolades).",
          "default": "item"
        },
        "result": {
          "type": "string",
          "description": "Résultat enregistré."
        }
      },
      "required": [],
      "additionalProperties": false
    },
    "props:queue.fail": {
      "title": "queue.fail — Élément en échec",
      "description": "Marque l'élément en échec métier ; une nouvelle tentative est possible selon la file.",
      "type": "object",
      "properties": {
        "item": {
          "type": "string",
          "description": "Variable de l'élément.",
          "default": "item"
        },
        "error": {
          "type": "string",
          "description": "Motif.",
          "default": "échec métier"
        },
        "retry": {
          "type": "string",
          "description": "Autoriser une nouvelle tentative.",
          "default": "true"
        }
      },
      "required": [],
      "additionalProperties": false
    },
    "props:mcp.call": {
      "title": "mcp.call — Appeler un connecteur MCP",
      "description": "Appelle l'outil d'un serveur MCP externe (CRM, ERP, base de données…).",
      "type": "object",
      "properties": {
        "server": {
          "type": "string",
          "description": "Nom du connecteur (ZOKAS Agent › Connecteurs).",
          "examples": [
            "crm"
          ]
        },
        "tool": {
          "type": "string",
          "description": "Nom de l'outil.",
          "examples": [
            "search_contacts"
          ]
        },
        "args": {
          "type": "string",
          "description": "Arguments (objet JSON).",
          "examples": [
            "{\"query\": \"{{client}}\"}"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable de résultat.",
          "default": "resultat"
        }
      },
      "required": [
        "server",
        "tool"
      ],
      "additionalProperties": false
    },
    "props:flow.if": {
      "title": "flow.if — Condition",
      "description": "Évalue une condition ; si elle est fausse, saute l'étape suivante ou arrête le processus.",
      "type": "object",
      "properties": {
        "condition": {
          "type": "string",
          "description": "Expression : a OP b avec OP parmi > < >= <= == != contient ; ou une valeur seule (vraie si non vide, différente de « false » et de « 0 »).",
          "examples": [
            "{{facture.montant}} > {{seuil}}"
          ]
        },
        "otherwise": {
          "type": "string",
          "description": "Si la condition est fausse : sauter UNIQUEMENT l'étape suivante, ou arrêter le processus.",
          "enum": [
            "skip",
            "stop"
          ],
          "default": "skip"
        }
      },
      "required": [
        "condition"
      ],
      "additionalProperties": false
    },
    "props:flow.foreach": {
      "title": "flow.foreach — Pour chaque élément",
      "description": "Répète les « count » étapes qui suivent pour chaque élément d'une liste.",
      "type": "object",
      "properties": {
        "list": {
          "type": "string",
          "description": "Variable liste (nom sans accolades). Une chaîne JSON est acceptée.",
          "examples": [
            "factures"
          ]
        },
        "item": {
          "type": "string",
          "description": "Nom de la variable qui reçoit l'élément courant.",
          "default": "element",
          "examples": [
            "facture"
          ]
        },
        "count": {
          "type": "string",
          "description": "Nombre d'étapes SUIVANTES formant le corps de la boucle.",
          "default": "1",
          "examples": [
            "4"
          ]
        }
      },
      "required": [
        "list",
        "count"
      ],
      "additionalProperties": false
    },
    "props:flow.call": {
      "title": "flow.call — Exécuter un sous-processus",
      "description": "Exécute un autre processus publié avec ses propres variables, dans le même navigateur.",
      "type": "object",
      "properties": {
        "process": {
          "type": "string",
          "description": "Nom ou identifiant du processus publié.",
          "examples": [
            "Connexion ERP"
          ]
        },
        "variables": {
          "type": "string",
          "description": "Variables transmises (objet JSON).",
          "examples": [
            "{\"dossier\": \"{{dossier}}\"}"
          ]
        },
        "into": {
          "type": "string",
          "description": "Variable qui reçoit les variables finales du sous-processus.",
          "default": "sous_processus"
        }
      },
      "required": [
        "process"
      ],
      "additionalProperties": false
    },
    "props:flow.wait": {
      "title": "flow.wait — Attendre",
      "description": "Marque une pause (60 secondes au maximum).",
      "type": "object",
      "properties": {
        "seconds": {
          "type": "string",
          "description": "Durée en secondes (60 max).",
          "default": "1",
          "examples": [
            "5"
          ]
        }
      },
      "required": [
        "seconds"
      ],
      "additionalProperties": false
    },
    "step": {
      "title": "Étape",
      "type": "object",
      "required": [
        "type",
        "props"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "Identifiant unique dans le package."
        },
        "type": {
          "enum": [
            "web.open",
            "ui.click",
            "ui.type",
            "ui.key",
            "ui.read",
            "ui.table",
            "ui.image",
            "ui.type_secret",
            "file.list",
            "file.read",
            "file.search",
            "file.write",
            "file.move",
            "file.copy",
            "file.trash",
            "file.mkdir",
            "app.open",
            "desktop.type",
            "desktop.key",
            "desktop.click",
            "mail.read",
            "mail.attachments",
            "mail.send",
            "data.assign",
            "data.table",
            "http.call",
            "cmd.run",
            "ai.agent",
            "ai.classify",
            "doc.extract",
            "human.approve",
            "human.form",
            "log.write",
            "queue.add",
            "queue.next",
            "queue.done",
            "queue.fail",
            "mcp.call",
            "flow.if",
            "flow.foreach",
            "flow.call",
            "flow.wait"
          ],
          "description": "Type d’étape."
        },
        "label": {
          "type": "string",
          "description": "Libellé métier."
        },
        "props": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      },
      "additionalProperties": false,
      "allOf": [
        {
          "if": {
            "properties": {
              "type": {
                "const": "web.open"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:web.open"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.click"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.click"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.type"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.type"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.key"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.key"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.read"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.read"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.table"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.table"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.image"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.image"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ui.type_secret"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ui.type_secret"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.list"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.list"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.read"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.read"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.search"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.search"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.write"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.write"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.move"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.move"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.copy"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.copy"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.trash"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.trash"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "file.mkdir"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:file.mkdir"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "app.open"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:app.open"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "desktop.type"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:desktop.type"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "desktop.key"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:desktop.key"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "desktop.click"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:desktop.click"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "mail.read"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:mail.read"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "mail.attachments"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:mail.attachments"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "mail.send"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:mail.send"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "data.assign"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:data.assign"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "data.table"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:data.table"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "http.call"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:http.call"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "cmd.run"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:cmd.run"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ai.agent"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ai.agent"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "ai.classify"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:ai.classify"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "doc.extract"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:doc.extract"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "human.approve"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:human.approve"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "human.form"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:human.form"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "log.write"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:log.write"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "queue.add"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:queue.add"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "queue.next"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:queue.next"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "queue.done"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:queue.done"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "queue.fail"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:queue.fail"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "mcp.call"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:mcp.call"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "flow.if"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:flow.if"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "flow.foreach"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:flow.foreach"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "flow.call"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:flow.call"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "flow.wait"
              }
            }
          },
          "then": {
            "properties": {
              "props": {
                "$ref": "#/$defs/props:flow.wait"
              }
            }
          }
        }
      ]
    }
  }
}
