No description
Find a file
2026-10-29 01:47:34 +01:00
.ressources Mise à jour du projet 2026-10-26 23:17:20 +01:00
examples Autonomous agents 2026-10-27 00:45:53 +01:00
packages Add models list functionality and update related files 2026-10-29 01:47:34 +01:00
.gitignore Mise à jour 2026-10-29 01:10:36 +01:00
GUIDE_AUTONOMOUS.md Autonomous agents 2026-10-27 00:45:53 +01:00
GUIDE_PERMISSIONS.md Autonomous agents 2026-10-27 00:45:53 +01:00
package.json Mise à jour 2026-10-29 01:10:36 +01:00
pnpm-lock.yaml Mise à jour 2026-10-29 01:10:36 +01:00
pnpm-workspace.yaml Mise à jour du projet 2026-10-26 23:17:20 +01:00
README.md Add models list functionality and update related files 2026-10-29 01:47:34 +01:00
screenshot.png Update screenshot.png 2026-10-16 00:59:53 +02:00
settings.example.json Autonomous agents 2026-10-27 00:45:53 +01:00
SPECS_TECH.md Add models list functionality and update related files 2026-10-29 01:47:34 +01:00
tsconfig.base.json Migration Ink 2026-10-12 00:41:17 +02:00
tsconfig.json Migration Ink 2026-10-12 00:41:17 +02:00
VERSIONS.md Mise à jour 2026-10-29 01:10:36 +01:00

MAIA

MAIA

Assistant IA en ligne de commande : un seul agent (MAIA), le LLM se choisit parmi ceux de LiteLLM, l'interface utilise Ink (React pour CLI).

Démarrer

pnpm install                               # gestionnaire de paquets : pnpm@12.14.0 (champ packageManager, via Corepack)
mkdir -p ~/.maia && cp settings.example.json ~/.maia/settings.json   # puis renseigner l'URL de LiteLLM
pnpm dev                                  # le modèle est choisi dans la liste au démarrage
pnpm dev -- --model litellm:gpt-4o        # nom du modèle tel que LiteLLM le connaît
pnpm test                                 # test de la boucle agent (provider factice)
pnpm build && node packages/cli/dist/index.js
pnpm maia run ./.maia/agents/Main/AGENT.md --task "…"   # workflow autonome (voir « Workflows autonomes »)

Lancer MAIA depuis un autre dossier

pnpm dev s'exécute depuis la racine du dépôt : le dossier de travail est alors cette racine (./MAIA.md, ./.maia/skills… y sont cherchés). Pour utiliser MAIA sur un autre projet ./X, lancez-le depuis ./X :

pnpm build
# une fois : installe la commande `maia` (pnpm 12 n'a plus `pnpm link --global`)
mkdir -p ~/.local/bin && ln -sf "$PWD/packages/cli/dist/index.js" ~/.local/bin/maia && chmod +x packages/cli/dist/index.js
#   (~/.local/bin doit être dans le PATH ; alternative : `cd packages/cli && npm link`)
cd ~/X && maia                       # le dossier de travail est ~/X
# ou sans installer :  cd ~/X && node /chemin/vers/maia/packages/cli/dist/index.js
# ou en développement : MAIA_CWD=~/X pnpm --dir /chemin/vers/maia dev

Pourquoi pas pnpm --filter … dev : pnpm exécute alors le script depuis packages/cli, pas depuis le dossier où vous avez tapé la commande. MAIA tient compte de MAIA_CWD, puis de INIT_CWD (dossier où pnpm/npm a été lancé), puis de process.cwd(). C'est aussi vrai pour maia run et les chemins relatifs vers un AGENT.md. Le -- entre pnpm dev (ou pnpm maia) et les options est facultatif : s'il est transmis, MAIA l'ignore.

Architecture

Paquet Rôle
core Contrats (LLMProvider, Tool, AgentUI…), boucle agent, sessions, !, @fichier, §skill
providers Adaptateurs LLM : litellm (provider unique : requêtes au format OpenAI vers LiteLLM, voir ci-dessous)
ui Interface Ink/React (journal, saisie, listes de choix, confirmations, spinner)
agent L'agent MAIA : contexte (MAIA.md / CLAUDE.md), skills .maia/skills + outils Read/Glob/Grep/Write/Edit/Bash/TodoWrite/WebFetch/CurrentModel/ListModels
cli Point d'entrée : configuration, boucle de conversation

Résolution du modèle (pas de défaut : s'il n'est pas défini, il est demandé) : option --model > variable MAIA_MODEL

.maia/config.json (projet) > ~/.maia/config.json > défauts.

Il n'y a plus de notion de mode ni d'extensions : un seul agent, MAIA.

Spécifications techniques détaillées (contrats, formats, constantes) : SPECS_TECH.md.

Commandes

Saisie Effet
/help aide (commandes, skills)
/permissions affiche les règles de permissions chargées, par fichier : badges ALLOW, ASK, DENY suivis des règles
/model recharge la liste des modèles LiteLLM et change de modèle (conversation conservée)
/models affiche la liste des modèles disponibles auprès de LiteLLM (le modèle actif est signalé ●), sans changer de modèle
/clear supprime le contexte de la session et recharge MAIA.md / CLAUDE.md et les skills, comme au démarrage (la session garde son nom)
/createAgent [nom] crée un sous-agent depuis le modèle .ressources/AGENT.md : demande le niveau (projet ./.maia/agents ou global ~/.maia/agents) puis le nom
/agents liste les sous-agents chargés (nom, origine, description)
/mcp liste les serveurs MCP activés : OK (vert) ou FAIL (rouge)
/workflows [all] · /workflow <id> workflows autonomes en cours (ou tous) · état des agents d'un workflow (voir « Workflows autonomes »)
/compact résume la conversation et repart de ce résumé (voir « Compactage »)
/rename <nom> · /load [nom] · /new · /delete sessions (voir plus bas)
/exit (ou Ctrl+D) quitte (la session est sauvegardée)
!commande · @fichier · §skill · &agent commande shell, fichier injecté, skill (voir plus bas)

Option de lancement : --model <spec> (ex. litellm:gpt-4o).

Interface (Ink)

L'interface est une application React rendue par Ink (Ink 8 : React ≥ 19.2 ; vérifiez la version de Node qu'il exige). Le CLI garde une façade impérative (InkUI : info, select, readInput, confirm, withSpinner…) au-dessus d'un UIStore sans dépendance à React, ce qui rend la logique testable (store.test.ts). Les composants (saisie, liste, confirmation, spinner) sont écrits à la main ; seuls les badges viennent de @inkjs/ui. Un terminal interactif (TTY) est requis.

Présentation : la bannière d'accueil reprend le style « MAIA » : image à gauche, affichée telle quelle en couleurs réelles (demi-blocs ▀▄█ : 32 × 32 pixels, soit 32 colonnes × 16 lignes ; sans pixelisation). Elle vient de packages/ui/assets/mask.png ; python3 packages/ui/scripts/make-mask.py [taille] (Pillow) régénère src/mask-data.ts à une autre taille, titre « Maia » en gros caractères pixel avec ombre et dégradé arc-en-ciel à droite, puis trois textes (vert : « Assistant IA en ligne de commande… », rose : « Un seul agent, plusieurs modèles », puis la signature « Arnaud VINCENT » en jaune et « ClaudeCode » en orange). Terminal d'au moins 58 colonnes pour la version complète ; entre 25 et 57 : sans le masque ; en dessous : cadre à double bordure compact, titre centré ; une ligne vide sépare chaque message.

Messages du journal, avec le composant Badge de @inkjs/ui :

Badge Usage Texte
INFO (bleu) information bleu clair (LIGHT_BLUE)
WARN (jaune) avertissement par défaut
FAIL (rouge) erreur #DC8ADD (ERROR_TEXT)
ASK (vert) question posée à l'utilisateur (liste de choix, confirmation o/n), à gauche de la question gras

Les réponses de l'agent sont rendues en markdown (titres, listes, code, tableaux, gras…) avec markdansi. Tout est isolé dans packages/ui/src/markdown.tsx : pour le retirer, supprimer ce fichier, la dépendance markdansi et remplacer <Markdown /> par <Text> dans components.tsx. Le texte en cours de streaming reste brut ; la mise en forme apparaît quand la réponse est terminée. MAIA_NO_MARKDOWN=1 désactive le rendu sans toucher au code, et un échec de markdansi retombe sur le texte brut.

Les couleurs sont dans packages/ui/src/colors.ts. Une fois la réponse validée, la question est remplacée par la ligne ◆ Question : choix dans le journal.

Barre de contexte : sous la zone de saisie, en bas à droite, une barre de carrés ■ (de gauche à droite) montre la taille du contexte de la conversation par rapport à la taille maximale du modèle, à l'échelle 1 carré = 10 000 tokens (200 000 tokens → 20 carrés), avec le libellé « 45.7k / 200k · 23% ». Les carrés sont verts, jaunes à partir de 60 %, rouges à partir de 85 %. Si la barre ne tient pas dans le terminal (très grands contextes), l'échelle est agrandie par paliers de 10 000 et indiquée (1■ = 20k).

  • Taille maximale : après le choix du modèle (au démarrage, avec --model/config, et avec /model), MAIA demande au LLM « Taille maximale du contexte pour ce modèle » (réponse en tokens, lue par parseTokenCount : 128000, 128 000 tokens, 200k, 1,5 million…). La valeur est annoncée (INFO) ; une réponse par modèle et par exécution. Si la réponse est illisible, ou si la question échoue ou dépasse 60 s (Ctrl+C l'abandonne), un avertissement s'affiche et la barre est masquée. Un LLM peut se tromper sur sa propre limite : la valeur affichée en INFO permet de la vérifier.
  • Contexte utilisé = tokens de tout ce que la session enverrait au modèle : prompt système (MAIA.md…), définition des outils, messages (texte, appels d'outils et leurs résultats, +4 tokens de structure par message) et contexte en attente (résultats de commandes !, fichiers @). Le calcul est uniquement local et déterministe, avec js-tiktoken (vocabulaire o200k_base) ; la consommation renvoyée par le serveur (usage, dont total_tokens) n'est jamais utilisée pour la barre. C'est une somme des comptes de chaque élément : la valeur ne fait qu'augmenter quand la conversation s'enrichit, et ne diminue que si le contexte est réduit (/clear, /compact, tour interrompu, autre session). Le comptage est refait avant chaque saisie de l'utilisateur, après chaque réponse du LLM et chaque lot de résultats d'outils, et pendant la réponse du modèle (au plus 4 fois par seconde, avec le texte déjà reçu) ; la valeur est affichée à 100 tokens près (123.5k) ; le compte de chaque message est mémorisé, donc le recalcul reste instantané. Pour les modèles qui n'utilisent pas ce vocabulaire (Claude, Mistral…), la valeur est une bonne approximation, pas le compte exact du serveur.

La zone de saisie est encadrée par une bordure en pointillés (┈) au-dessus et au-dessous seulement, en greenBright, sans bords gauche ni droit. À sa gauche, le spinner arrow3 de cli-spinners s'anime en continu. Pendant l'attente de la réponse après validation de la saisie, « En réflexion » s'affiche avec le spinner dots10 à sa gauche, en yellowBright (même spinner pour les autres attentes : chargement des modèles, exécution d'une commande !).

Contexte Touches
Saisie Entrée : envoyer · Alt+Entrée ou Ctrl+J : saut de ligne (sans envoyer) ; Maj+Entrée aussi, mais seulement sur un terminal compatible avec le protocole clavier kitty (Kitty, WezTerm, Ghostty, iTerm2…) : ailleurs il est identique à Entrée et envoie le prompt · ←/→ : se déplacer · Début (Home) ou Ctrl+A : début du prompt · Fin (End) ou Ctrl+E : fin du prompt · ↑/↓ : historique · Ctrl+U/W : effacer · Ctrl+C ou Ctrl+D (saisie vide) : quitter · le collage multi-lignes est conservé
Liste (/model, /load, choix initial) ↑/↓ (ou j/k) · Entrée : valider · Échap : annuler
Sélection multiple (/delete) ↑/↓ (ou j/k) · Espace : cocher / décocher · Suppr : supprimer les sessions cochées (après confirmation) · Échap : annuler
Confirmation d'une action (Write, Edit, Bash…) o : autoriser · n ou Échap : refuser · Entrée seule est ignorée (réponse explicite exigée)
Pendant un tour de l'agent Ctrl+C : interrompt la requête et restaure la conversation avant cette demande

Dossier de base

Le dossier où MAIA est lancé (ex. ./X) est la base de tous les chemins relatifs : fichiers lus, créés ou modifiés par les outils (Read, Write, Edit, Glob, Grep, Bash), .maia/config.json, MAIA.md / CLAUDE.md et .maia/skills/. Un chemin complet est utilisé tel quel (~/… désigne le dossier personnel). Ce dossier est capturé une fois au démarrage, affiché (« Dossier de travail : … ») et transmis aux outils et aux providers ; les règles de permissions (Read(./.env), Edit(src/**)) sont elles aussi relatives à ce dossier. Seul ~/.maia (config, settings.json et skills utilisateur) est lu depuis le dossier personnel.

Commande ! : exécuter une commande shell

!commande exécute la commande directement, sans passer par le modèle. Deux syntaxes :

Saisie Commande exécutée
!ls · !ls -la src tout ce qui suit le !
!'ls -la' le texte entre guillemets simples (délimite la commande avec ses paramètres)
!'ls' -la src · !'ls -la' | wc -l le texte entre guillemets, puis ce qui suit est ajouté comme paramètres : ls -la src, ls -la | wc -l
!'echo "l\'ami"' \' donne un guillemet simple littéral dans la commande
!'ls -la erreur (guillemet non fermé) : rien n'est exécuté

Le reste du comportement :

  • elle tourne dans le dossier de lancement (voir « Dossier de base ») ; un cd n'a donc pas d'effet durable ;
  • stdout et stderr sont affichés dans l'ordre d'arrivée, avec le code de sortie en cas d'échec ; durée maximale 2 minutes ;
  • aucune confirmation n'est demandée (c'est vous qui la tapez) et les règles de permissions des outils ne s'appliquent pas ;
  • le résultat est ajouté au contexte de votre prochaine demande (jusqu'à 30 000 caractères, milieu tronqué au-delà), pour que le modèle sache ce qui a été lancé. /clear l'efface ;
  • Ctrl+C interrompt la commande ; ! seul affiche l'aide. Les programmes interactifs (vim, less, saisie au clavier…) ne sont pas pris en charge.

Références @fichier

@chemin/vers/fichier dans votre message injecte le contenu du fichier dans le contexte de la demande (en plus de le laisser dans le texte) :

Saisie Effet
explique @src/a.ts injecte src/a.ts
compare @a.ts et @ b.ts plusieurs fichiers ; la forme @ chemin (avec espace) est acceptée
lis @'mon dossier/a b.txt' guillemets simples pour un chemin avec espaces (\' : guillemet littéral)
résume @docs un dossier : la liste de ses entrées est injectée
@/chemin/complet.txt · @~/notes.md chemin complet, ou depuis le dossier personnel
  • Chemins relatifs au dossier de lancement (voir « Dossier de base »). Aucune confirmation : c'est vous qui citez le fichier, les règles de permissions des outils ne s'appliquent pas.
  • Un @mot qui ne désigne aucun fichier existant reste du texte ordinaire (@mention, @Override, jean@exemple.fr) ; un avertissement n'apparaît que s'il ressemble à un chemin (@src/x.ts introuvable).
  • Limites : 2 Mo par fichier, 50 000 caractères injectés par fichier (milieu tronqué au-delà) et 150 000 par message ; les fichiers binaires sont ignorés ; un même fichier cité plusieurs fois n'est injecté qu'une fois.
  • Le contenu est marqué comme donnée, pas comme instructions. Les @fichier écrits dans le corps d'une commande personnalisée ne sont pas encore traités (seuls ceux de votre saisie le sont).
  • Pas d'autocomplétion des chemins à la saisie pour l'instant.

Sessions (./.maia/.sessions)

Au lancement, ./.maia et ./.maia/.sessions sont créés dans le dossier d'exécution s'ils n'existent pas. Les sessions sont une fonctionnalité de l'application.

  • Sauvegarde automatique à la sortie (/exit, Ctrl+D, SIGTERM/SIGHUP). Une session sans aucun message n'est pas enregistrée.
  • Nom par défaut : session_{date}_{heure}_{minutes}_{secondes} (ex. session_2026-10-13_01_04_05).
  • /rename <nom> : renomme la session courante ; ce nom sera utilisé à la sauvegarde.
  • /load : sans argument, liste les sessions enregistrées (sélection) ; /load <nom> recharge cette session (le modèle courant est conservé).
  • /delete : liste les sessions enregistrées ; Espace coche ou décoche la session sous le curseur, Suppr supprime les sessions cochées du disque après confirmation (o/n). La session courante n'est pas proposée (/new d'abord pour la supprimer) ; les fichiers illisibles sont proposés aussi. Les traces de sous-agents et les sessions compactées (xxx__n) sont des sessions comme les autres : elles ne sont pas supprimées en cascade.
  • /new : nouvelle session, nouveau contexte (MAIA.md / CLAUDE.md et skills rechargés) ; l'utilisateur est averti que la session quittée est sauvegardée (sous son nom).

Chaque session est un fichier JSON : elle peut contenir des extraits de fichiers ou de sorties de commandes. .maia/ est ajouté au .gitignore.

Skills : appel direct avec §

§nom_du_skill [arguments] lance un skill de l'agent (ex. §sk_test) ; § seul liste les skills disponibles. L'agent expose ses skills via ModeInitResult.skills (contrat AgentSkill : name, description, invoke(args)), ce qui rend l'appel indépendant du format propre à l'agent. invoke retourne le prompt envoyé au LLM. Ce prompt contient les instructions du SKILL.md (.maia/skills/<nom>/) (avec $ARGUMENTS / ARGUMENTS:), sans passer par l'outil Skill du modèle.

Accès aux LLMs : LiteLLM

MAIA ne parle qu'à un serveur LiteLLM : toutes les requêtes de tous les agents sont envoyées au format OpenAI (chat/completions, en streaming, avec appels d'outils) vers le endpoint OpenAI de LiteLLM.

Au démarrage, le fichier ~/.maia/settings.json (dossier de l'utilisateur, pas celui de lancement) est chargé :

{
  "LiteLLM_URL": "http://localhost:4000",
  "OpenAI_EndPoint": "/v1/chat/completions",
  "LiteLLM_Models": "/v1/models"
}
  • LiteLLM_URL : URL du serveur LiteLLM.
  • OpenAI_EndPoint : suffixe ajouté à LiteLLM_URL pour envoyer une requête au format OpenAI.
  • LiteLLM_Models : suffixe ajouté à LiteLLM_URL pour récupérer la liste des modèles disponibles.
  • Optionnel : LiteLLM_API_Key (ou variable d'environnement LITELLM_API_KEY), envoyée en Authorization: Bearer …. Ce fichier peut donc contenir un secret : ne pas le versionner.
  • Optionnel : MaxIterations (entier, défaut 50) : étapes (appel du LLM puis des outils) par série avant que MAIA demande s'il faut continuer.
  • Optionnel : DefaultModel (ex. litellm:gpt-4o, ou un morceau de nom comme sonnet) : modèle des workflows autonomes (maia run) quand ni --model ni l'agent parent n'en indiquent.
  • Optionnel : MaxOutputTokens (entier, ex. 16000) : max_tokens envoyé à chaque requête. Sans lui, le serveur applique sa limite par défaut, parfois courte : un long fichier écrit par l'outil Write peut être coupé (le paramètre content arrive alors absent). MAIA affiche un avertissement quand la réponse est coupée (finish_reason length, ou sortie qui tombe pile sur une limite usuelle 4096/8192… avec un appel d'outil : certains serveurs « réparent » alors l'appel et annoncent tool_calls). Cas constaté avec anthropic--claude-4.5-sonnet : completion_tokens: 4096 et Write reçu avec path seul → fixer par ex. "MaxOutputTokens": 32000.
  • Diagnostic : MAIA_DEBUG_LLM=1 maia enregistre le flux brut renvoyé par LiteLLM dans maia-llm-debug.log (dossier temporaire ; MAIA_DEBUG_LLM=/chemin/fichier.log pour choisir le fichier).

Si le fichier est absent ou incomplet, MAIA s'arrête avec un message qui en donne un exemple.

Choix du modèle : au démarrage (sauf si --model ou MAIA_MODEL le définit), MAIA récupère les modèles auprès de LiteLLM et demande de choisir celui de la session. /model rouvre cette liste en cours de session (la conversation est conservée) ; /models l'affiche seulement, sans rien changer. Spécification explicite : litellm:<nom du modèle> (ou le nom seul).

Agent MAIA : contexte et skills

Au lancement, le contexte de départ est chargé depuis le dossier de lancement : MAIA.md s'il existe, sinon CLAUDE.md (s'il existe). Le fichier chargé est indiqué par un message INFO ; si aucun des deux n'existe, un WARN le signale. Seul ce dossier est regardé (pas les dossiers parents).

Les dossiers .claude ne sont plus lus (ni settings.json, ni commands/, ni skills/, ni .claude/CLAUDE.md, ni ~/.claude).

  • Skills : .maia/skills/<nom_du_skill>/SKILL.md (ou skill.md), chargés depuis trois emplacements. Le plus spécifique l'emporte en cas de même nom :

    1. le dossier utilisateur : ~/.maia/skills ;
    2. le dossier parent du dossier de lancement : ex. /X/.maia/skills pour un lancement dans /X/Y ;
    3. le dossier de lancement : ex. /X/Y/.maia/skills.

    Le fichier commence par un en-tête YAML : description (objectif du skill) et name (optionnel ; s'il est présent, il doit être identique au nom du dossier, qui fait foi). Seule la liste (nom + description) est mise dans le prompt ; le contenu est chargé à la demande par l'outil Skill, ou lancé directement avec §nom.

---
name: sk_test          # optionnel, = nom du dossier
description: Lance les tests du projet et résume les échecs
---
Instructions du skill… ($ARGUMENTS = arguments passés avec §sk_test)

Conséquence : il n'y a plus de variables d'environnement venant de la configuration de l'agent ni de slash-commandes personnalisées. Les permissions sont décrites dans la section suivante.

Permissions

Guide complet avec exemples (commande simple, commande avec tube, interdiction d'un rm caché) : GUIDE_PERMISSIONS.md.

Les permissions s'appliquent aux outils (intégrés ou non). Trois types de règles :

Type Effet
allow l'outil est utilisé sans approbation manuelle
ask l'utilisateur doit confirmer chaque utilisation (badge ASK, réponse o (oui), n (non) ou s (stop, voir ci-dessous))
deny l'outil ne peut pas être utilisé (l'agent reçoit un refus)

Ordre d'évaluation : deny, puis ask, puis allow. La première famille qui correspond décide, quelle que soit la précision des règles : un allow étroit comme Bash(aws s3 ls) ne fait pas exception à un deny large comme Bash(aws *), ni à un ask plus large. Sans règle applicable, c'est ask qui s'applique (pour tous les outils, y compris Read, Glob, Grep…).

Où les définir : ~/.maia/settings.json (utilisateur) et ./.maia/settings.json (dossier de lancement). Les listes des deux fichiers s'additionnent. Le fichier ../.maia/settings.json du dossier parent n'est pas pris en compte. Dans chaque fichier, la propriété permissions contient une liste par type, tous facultatifs :

"permissions": {
  "allow": ["Bash(allow stop *)"],
  "ask":   ["Bash(allow ask *)"],
  "deny":  ["Bash(allow deny *)", "Read(./.deny.md)"]
}

Format d'une règle : Outil (tous les appels de l'outil) ou Outil(motif) :

Outil Le motif porte sur Exemples
Bash la commande ; * = n'importe quelle suite ; cmd * couvre cmd avec ou sans arguments (mais pas cmdx) ; sans * : commande exacte Bash(allow stop *), Bash(git status), Bash(npm run test:*)
Read, Write, Edit, Glob, Grep le chemin (relatif au dossier de lancement, motifs glob) Read(./.deny.md), Edit(src/**)
WebFetch le domaine WebFetch(domain:docs.example.com), WebFetch(domain:*.example.org)
Skill le nom du skill Skill(pdf)
autres outils (ex. ajoutés plus tard) une valeur texte de l'entrée de l'outil (* accepté) mcp__db__query(SELECT *)

Précisions sur Bash :

  • commande composée (&&, ;, |) : un deny ou un ask s'applique dès qu'une des parties correspond, y compris cachée dans $(…), des backticks ou des parenthèses ; un allow ne s'applique que si toutes les parties sont couvertes ;
  • un allow ne couvre jamais une commande avec substitution ($(…), backticks) : elle repasse en ask ; une redirection de sortie (>, >>, 2>, &>) repasse en ask sauf si le fichier visé est autorisé par une règle Redirect(chemin) (2>&1 n'écrit dans aucun fichier : sans effet). deny: ["Redirect(.env)"] interdit d'écrire dans ce fichier par redirection, même avec Bash autorisé. Détails : GUIDE_PERMISSIONS.md § 8.

Au démarrage, MAIA indique (INFO) les fichiers chargés et le nombre de règles, ou qu'aucune règle n'est définie ; un JSON invalide ou une règle mal formée donne un WARN et est ignoré. Les deny sont du « meilleur effort » : ils ne remplacent pas un vrai bac à sable. Les commandes ! et les références @fichier tapées par l'utilisateur ne passent pas par ces règles.

Attention : ./.maia/settings.json d'un dépôt tiers peut contenir des règles allow larges ; relisez-le avant de lancer MAIA dans un projet que vous ne connaissez pas.

Réponse « stop » : à une demande d'autorisation ASK … (o/n/s=stop), s signifie « je ne réponds pas à la question, je veux interrompre pour donner d'autres informations ou faire autre chose ». L'action demandée n'est pas exécutée (le modèle en est informé), les autres actions qui suivaient dans la même réponse ne sont pas lancées (celles déjà autorisées vont à leur terme), puis MAIA rend la main avec le message « Itération interrompue (stop) ». Rien n'est annulé : la conversation et les résultats déjà obtenus restent dans le contexte, et ce que vous tapez ensuite est ajouté à la suite (contrairement à Ctrl+C, qui restaure la conversation d'avant la demande). « stop » n'existe que pour les confirmations d'outils de l'agent principal (pas pour « Continuer ? » à la limite d'itérations, ni pour les confirmations d'un sous-agent, où s est ignoré).

La commande /permissions affiche les règles actives, fichier par fichier, avec un badge ALLOW (vert), ASK (jaune) et DENY (rouge) suivi des règles du type, une par ligne ; seuls les types non vides sont montrés. Après avoir modifié un settings.json, /clear recharge la configuration.

Exemple pour éviter les confirmations sur les outils en lecture seule : voir settings.example.json.

Outils TodoWrite, WebFetch, CurrentModel et ListModels

  • TodoWrite : liste de tâches de la session (pending / in_progress / completed, forme activeForm pour la tâche en cours). Chaque appel remplace la liste ; le résultat (☐ ▶ ☑) est affiché à l'utilisateur et renvoyé au modèle. Un avertissement est ajouté si plusieurs tâches sont « en cours ».
  • CurrentModel : renvoie le modèle LLM utilisé par la session (valeur à jour après un /model). Sans paramètre, sans confirmation ; le modèle est lu au moment de l'appel.
  • ListModels : renvoie la liste des modèles disponibles auprès de LiteLLM (même source que /models, interrogée à chaque appel), en signalant celui de la session. Sans paramètre, sans confirmation. Permet à l'agent de répondre à « quels sont les modèles disponibles ? » ; il ne change pas de modèle (c'est /model, côté utilisateur). Disponible aussi pour les sous-agents et l'agent parent des workflows.
  • WebFetch : télécharge une URL http(s) (HTML converti en Markdown avec turndown, JSON mis en forme, texte brut ; 5 Mo et 30 s maximum, max_length caractères renvoyés). Pas de JavaScript exécuté, pas de PDF ni d'images.
    • Confirmation demandée par défaut ; règle allow possible : WebFetch(domain:docs.example.com) (voir « Permissions »).
    • Une redirection vers un autre domaine n'est pas suivie : l'outil renvoie la nouvelle URL, que le modèle doit rappeler (ce qui repasse par la confirmation).
    • Le contenu est signalé au modèle comme non fiable (risque d'injection de prompt) ; il ne passe par aucun second modèle de résumé.

Outils personnalisés (./.maia/tools)

L'utilisateur peut créer ses propres outils programmatiques : le LLM les appelle comme n'importe quel autre outil quand leur description correspond au besoin. Ils sont chargés au démarrage et rechargés par /clear et /new.

  • Où : ./.maia/tools (dossier de lancement) et ~/.maia/tools (utilisateur, valable pour tous les projets) ; jamais le dossier parent. À nom égal, l'outil du projet l'emporte. Un sous-dossier = un outil : ./.maia/tools/addition/TOOL.json (tool.json accepté aussi) et ses scripts. Un exemple complet est fourni dans examples/tools/addition ; exemple de sous-agent : examples/agents/code_reviewer (à copier dans ./.maia/agents).(à copier dans ./.maia/tools/).
  • TOOL.json :
{
  "name": "Addition",
  "description": "Faire une addition avec 3 chiffres. Renvoie un chiffre, la somme",
  "command": {
    "parameters": [
      { "name": "param1", "type": "number", "default": "0" },
      { "name": "param2", "type": "number", "default": "0" },
      { "name": "param3", "type": "number", "default": "0" }
    ],
    "run": "node ./test.js {{param1}} {{param2}} {{param3}}"
  }
}
Champ Règle
description Obligatoire : dit au LLM à quoi sert l'outil et quand l'appeler.
name Facultatif, le nom du dossier par défaut. Lettres, chiffres, _ et - (64 caractères maximum). Ne peut pas reprendre le nom d'un outil intégré (Read, Bash…) ni d'un autre outil personnalisé.
command.parameters Paramètres fournis par le LLM. type : "number" ou "string". default : valeur utilisée si le LLM n'en donne pas (ex. {{param2}} devient 0) ; sans default, le paramètre est obligatoire. description facultative par paramètre.
command.run Ligne de commande ; chaque {{paramètre}} est remplacé par sa valeur. ./ désigne le dossier de l'outil (./test.js = ./.maia/tools/addition/test.js) et c'est le dossier d'exécution.
  • Sécurité : pas de shell. La ligne run est découpée en arguments (guillemets simples ou doubles possibles) et exécutée directement : une valeur envoyée par le LLM reste toujours un seul argument (; rm -rf / ou $(cmd) sont reçus tels quels par le script, jamais interprétés). En contrepartie, run n'accepte ni tubes |, ni redirections >, ni variables $HOME ; pour cela, appeler un script (sh ./script.sh {{p}}). Le programme (premier mot) ne peut pas être un {{paramètre}}. Sous Windows, les scripts .cmd/.bat ne sont pas lancés directement (utiliser node, python, ou cmd /c ./script.cmd).
  • Exécution : timeout de 2 minutes, sortie limitée à 30 000 caractères ; un code de sortie non nul est renvoyé au LLM comme une erreur (avec la sortie d'erreur du script). Le script reçoit MAIA_CWD (dossier de lancement de MAIA, pour retrouver le projet) et MAIA_TOOL_DIR.
  • Permissions : un outil personnalisé exécute un programme, donc il demande confirmation par défaut. Les règles de permissions s'appliquent avec son nom : "Addition" dans allow l'autorise sans demander, "Addition(5)" cible une valeur de paramètre.
  • Erreurs : une définition invalide (description absente, type inconnu, {{param}} non déclaré, JSON cassé…) est signalée par un avertissement (WARN) et l'outil est ignoré ; les autres restent chargés. Une virgule en trop avant ] ou } est tolérée. Au chargement, les outils valides sont annoncés (INFO).

Exécution des outils : premier plan et parallèle

  • Le raisonnement du LLM reste bloquant : l'utilisateur attend la réponse, ce qui préserve l'intégrité du contexte.
  • 1 outil demandé : exécuté au premier plan (annonce, confirmation éventuelle, exécution, résultat).
  • 2 outils ou plus dans la même réponse : chaque outil part dans sa propre tâche, en arrière-plan, dès qu'il est autorisé. L'agent attend la fin de toutes les tâches, puis renvoie les résultats au LLM dans l'ordre où il les a demandés. Une erreur ou un refus n'annule pas les autres outils.
  • Le spinner indique « Outils en parallèle : n/total terminés (…) ».
  • Limites : ce sont des tâches asynchrones Node (pas des threads système ; les processus lancés par Bash ou les outils personnalisés tournent réellement en parallèle) ; 8 outils au plus en même temps (MAX_PARALLEL_TOOLS) ; les confirmations sont posées une par une ; les appels qui portent sur le même fichier (Read/Write/Edit) ou TodoWrite passent l'un après l'autre (Tool.lockKey).
  • Code : packages/core/src/tool-runner.ts, tests tool-runner.test.ts.

Compactage (/compact et automatique)

Le compactage résume toute la conversation de la session pour libérer du contexte.

  1. La conversation actuelle est sauvegardée telle quelle sous son nom.
  2. Le LLM reçoit la demande « Faire un résumé compacté de nos échanges… » (5 rubriques : Contexte du projet, Objectif actuel, Fichiers pertinents, Décisions/Conclusions, Prochaines étapes), limité à 4000 tokens. Les commandes ! et fichiers @ pas encore envoyés font partie de ce qui est résumé.
  3. Le contexte est supprimé comme avec /clear, mais MAIA.md / CLAUDE.md ne sont pas rechargés : le résumé les remplace comme contexte de départ (dans le prompt système). Outils, skills et permissions sont rechargés.
  4. La session continue sous le nom {nom d'origine}__{n} : xxx → xxx__1 → xxx__2… Après __1000, le suivant reprend à __1 et écrase ce fichier. Le fichier d'avant compactage reste intact ; le fichier xxx__n contient le résumé (startContext) et la suite de la conversation, et /load xxx__n le restaure.
  5. Le nombre de tokens est recalculé et la barre de progression mise à jour.

Déclenchement automatique : dès que le contexte dépasse Seuil_max = Max_Token_Context - (4000 + 500) (4000 = tokens du résumé, 500 = marge d'erreur). La taille maximale est celle demandée au LLM au choix du modèle ; si elle est inconnue (barre masquée), seul /compact fonctionne. Le contrôle a lieu avant l'envoi de chaque demande et après chaque tour.

L'appel est bloquant (pas de tâche de fond). Ctrl+C l'annule sans rien modifier ; en cas d'erreur, la conversation reste inchangée.

Code : packages/core/src/compact.ts, sessions.ts (nextCompactedName, startCompacted), packages/agent/src/init.ts (startContext), orchestration dans packages/cli/src/index.ts.

Contexte refusé par le serveur : si LiteLLM répond « Input is too long » (ou équivalent), MAIA compacte automatiquement la conversation (résultats d'outils tronqués à 1 500 caractères dans la demande de résumé, pour qu'elle tienne), puis reprend la demande une seule fois. Si le compactage échoue aussi, l'erreur d'origine est affichée (utilisez alors /new). Les sous-agents n'ont pas ce mécanisme.

Sous-agents (./.maia/agents)

MAIA peut confier une tâche à un sous-agent : un processus Node autonome, avec sa propre conversation (vide) et sa propre définition. L'agent principal le lance avec l'outil Agent (agent = nom, task = tâche complète : le sous-agent ne voit rien de la conversation principale). Seul le résumé final du sous-agent revient dans la conversation principale. Plusieurs appels Agent dans une même réponse s'exécutent en parallèle.

Définition : un dossier par agent, avec un fichier AGENT.md, dans ./.maia/agents/<nom>/ (dossier de lancement) ou ~/.maia/agents/<nom>/ (utilisateur) ; le projet l'emporte sur l'utilisateur à nom égal. Créer un agent : /createAgent (ou /createAgent mon_agent) demande le niveau (Projet / Global) et le nom, puis copie le modèle .ressources/AGENT.md dans <dossier agents>/<nom>/AGENT.md en y mettant le bon name: (jamais d'écrasement d'un agent existant). Le modèle explique chaque propriété en commentaires (#) ; les propriétés facultatives y sont commentées, supprimez le # pour les activer. Tapez ensuite /clear pour charger le nouvel agent. Le dossier .ressources est celui de MAIA (racine du projet MAIA, ou MAIA_RESSOURCES), pas celui de votre projet. Les commentaires # … en fin de ligne de l'en-tête sont acceptés (mettez la valeur entre guillemets si elle contient #).

Les agents chargés sont listés au démarrage (« Agents chargés : ») et avec /agents.

---
name: code_reviewer            # doit être le nom du dossier
description: Révise du code (sécurité, anti-patterns). Ne pas l'utiliser pour écrire du code.
model: sonnet                  # optionnel — défaut : modèle de l'agent principal
tools: Read, Grep, Write       # optionnel — défaut : mêmes outils que l'agent principal (sans Agent)
permissions:                   # optionnel — s'ajoutent aux règles héritées
  allow: [Read, Grep]
  deny: [Bash]
effort: medium                 # low | medium | high — défaut medium
skills: [api-docs, testing-guide]   # optionnel — défaut : tous les skills
omitMd: false                  # défaut false : MAIA.md / CLAUDE.md ajouté au contexte
trace: false                   # défaut false : true enregistre la conversation (voir plus bas)
---
# Instructions system
Tu es un expert en révision de code.

# Missions
- identifier les problèmes de sécurité
- détecter les anti-patterns
- suggérer des améliorations

# Constraints (optionnelle)
- ne modifie aucun fichier : lecture seule
- si une faille CRITICAL est trouvée, ne tente pas de la corriger : signale-la en premier pour que l'utilisateur décide
- si la demande sort du périmètre, arrête-toi et indique qui doit prendre le relais

# Output
Pour chaque problème : catégorie (CRITICAL / HIGH / MEDIUM / LOW), description, code concerné, solution recommandée.
  • Les trois sections « Instructions system », « Missions » et « Output » sont obligatoires (titres Markdown de n'importe quel niveau). La section « Constraints » (ou « Contraintes »), à placer après Missions, est facultative : les limites de l'agent, c'est-à-dire ce qu'il n'a pas le droit de faire et dans quelles conditions il doit s'arrêter pour demander l'intervention de l'utilisateur ou d'un tiers. Un sous-agent ne pouvant pas poser de question, devant un de ces cas il n'agit pas et le signale dans son résumé (ce qui bloque, ce qui est attendu, de qui). C'est une consigne donnée à l'agent ; les interdits techniques se garantissent avec tools et permissions ; un agent invalide est ignoré avec un avertissement qui dit pourquoi.
  • Héritage : sans tools, skills ou permissions, le sous-agent a les mêmes outils (y compris les outils personnalisés), skills et règles que l'agent principal. Les règles permissions de l'agent s'ajoutent aux règles héritées : deny > ask > allow reste vrai, un deny ou ask hérité n'est jamais levé. Un sous-agent ne peut pas lancer de sous-agent.
  • Résumé : la dernière réponse du sous-agent suit la section Output ; s'il épuise son budget d'étapes sans conclure, un appel supplémentaire lui demande ce résumé.
  • Effort : borne le nombre d'étapes du sous-agent (low 10, medium 25, high 50) et module son prompt.
  • Modèle : identifiant exact ou fragment (sonnet → premier modèle LiteLLM dont le nom contient « sonnet ») ; introuvable → modèle de l'agent principal, avec avertissement.
  • Autorisations : si le sous-agent doit demander une confirmation, la question s'affiche dans l'interface principale, préfixée [sous-agent nom] (une question à la fois). Ctrl+C interrompt aussi les sous-agents en cours.
  • Lancement par l'utilisateur : une saisie qui commence par & lance un sous-agent directement : &code_reviewer relis src/a.ts (le nom est reconnu sans tenir compte de la casse ; sans tâche, l'agent exécute sa mission telle que décrite). & seul liste les agents. Aucune confirmation de l'outil Agent n'est demandée (c'est vous qui lancez), mais les autorisations des outils du sous-agent suivent les règles habituelles. Le résumé s'affiche et est joint à votre prochaine demande, comme le résultat d'une commande !. Ctrl+C interrompt le sous-agent.
  • Fin de mission : un message INFO est affiché dans l'interface principale (durée, nombre d'appels d'outils) ; un échec est signalé par un avertissement et renvoyé à l'agent principal comme erreur d'outil. Délai maximal : 30 minutes.
  • Trace (trace: true) : la conversation du sous-agent est enregistrée dans ./.maia/.sessions/ sous le nom {session de l'agent principal}-{agent}, ex. session_2026-10-20_00_01_53-agent_1.json (_2, _3… si le nom est pris). Sans trace, rien n'est enregistré.

Serveurs MCP (distants)

MAIA peut utiliser les outils de serveurs MCP accessibles par une URL (Streamable HTTP, ou l'ancien transport SSE). Pas d'OAuth ; seuls les outils MCP sont utilisés (pas les ressources ni les prompts).

1. Déclarer les serveurs (global uniquement) : ~/.maia/mcp.json. La clé est le nom du serveur.

{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" },
      "excludeTools": ["delete_repository"]
    },
    "docs": { "url": "https://docs.example.com/mcp", "timeout": 60 }
  }
}
Propriété Rôle
url obligatoire (http:// ou https://)
type http (défaut, Streamable HTTP) ou sse (ancien transport)
headers en-têtes HTTP (jetons, clés d'API)
timeout secondes, défaut 30 : connexion et chaque appel
includeTools / excludeTools liste de noms d'outils (* accepté) retenus / retirés
enabled false : le serveur est ignoré
description commentaire libre

${VARIABLE} est remplacé par la variable d'environnement (dans url et headers) ; une variable absente donne un avertissement et une valeur vide.

2. Activer par projet : aucun serveur n'est actif par défaut. Il faut l'autoriser dans le settings.json du projet :

{ "permissions": { "allow": ["Mcp(github)"], "deny": ["Mcp(github,delete_repository)"] } }
  • Mcp(serveur) : active le serveur, tous ses outils sont autorisés sans confirmation ;
  • Mcp(serveur,outil) : un outil (* accepté, ex. Mcp(github,delete_*)) ; en deny l'outil n'est jamais proposé au modèle ; en ask il demande confirmation ;
  • Mcp(*) (= Mcp(*,*)) : tous les serveurs déclarés et tous leurs outils ; Mcp(*,outil) : cet outil sur tous les serveurs ;
  • allow: ["Mcp(github)"] + deny: ["Mcp(github,*)"] : serveur désactivé, MAIA ne cherche pas à s'y connecter.

Les permissions sont traitées avant toute connexion ; seuls les serveurs activés sont ensuite contactés (en parallèle). Un serveur en échec n'empêche pas MAIA de démarrer : avertissement, état FAIL.

3. Utilisation : les outils s'appellent mcp__serveur__outil. /mcp liste les serveurs activés : bandeau vert OK s'il est connecté, bandeau rouge FAIL (avec la cause en grisé) sinon. Les connexions sont coupées à la sortie de MAIA (/exit, Ctrl+D, SIGTERM/SIGHUP) et refaites à chaque /clear, /new, /load, /model et /compact. Les sous-agents n'utilisent pas les outils MCP.

Workflows autonomes (maia run)

Guide complet (créer, configurer, lancer, superviser, scripts et services, limites, dépannage) : GUIDE_AUTONOMOUS.md.

Un workflow = un agent parent (orchestrateur) lancé sans interface + les sous-agents qu'il lance. On le démarre en ligne de commande, à la main ou depuis un script, un service, cron… et on interroge MAIA pour connaître son état et celui de chacun de ses agents.

L'agent parent est un AGENT.md au même format qu'un sous-agent (sections Instructions system, Missions, Constraints (optionnelle), Output). Ses missions et ses contraintes peuvent faire appel aux autres agents par leur nom. Exemple complet : examples/agents/release_check.

maia run ./.maia/agents/release_check/AGENT.md --task "Vérifie la branche feature/x"
maia run X:/projX/.maia/agents/Main/AGENT.md --detach        # en arrière-plan : affiche l'identifiant et rend la main
  • Sous-agents disponibles : tous ceux du dossier .maia/agents qui contient l'agent parent (ex. X:/projX/.maia/agents/* pour X:/projX/.maia/agents/Main/AGENT.md) et tous les agents globaux (~/.maia/agents) ; à nom égal, celui du dossier du parent l'emporte. Le parent ne se lance pas lui-même.
  • Dossier de travail : le projet qui contient ce .maia/agents (X:/projX) ; si le AGENT.md est ailleurs, le dossier de lancement.
  • Modèle : --model, sinon le champ model du parent, sinon DefaultModel dans ~/.maia/settings.json, sinon le premier modèle de LiteLLM. Chaque sous-agent prend le modèle du parent, sauf s'il a son propre champ model.
  • En-tête du parent (en plus des champs habituels) : timeout (90s, 30m, 2h : durée maximale) et permissionMode.
  • Options : --task (ou le texte après le fichier), --model, --name (nom pour retrouver le workflow), --detach, --json, --permission-mode wait|deny|allow, --timeout, --quiet.
  • Codes de sortie : 0 terminé, 1 en erreur, 2 mauvaise utilisation, 130 arrêté. La progression s'écrit sur la sortie d'erreur, le résultat sur la sortie standard (--json : { id, state, label, exitCode, result, error }).

Autorisations sans interface (permissionMode, défaut wait) : les règles allow / ask / deny des settings.json s'appliquent comme d'habitude ; seule une demande ask change :

  • wait : la demande est enregistrée, l'agent passe en attente jusqu'à maia approve <id> <demande> (ou --deny) ; le travail déjà fait n'est jamais perdu ;
  • deny : refusée tout de suite (le modèle en est informé et continue autrement) ;
  • allow : acceptée tout de suite — à réserver à un environnement isolé.

Intervention humaine : l'agent parent dispose de l'outil RequestIntervention (« ce qui bloque, ce qui est attendu, de qui ») : c'est ainsi qu'une contrainte du type « si X, demande l'intervention de l'équipe sécurité » se réalise. Le workflow passe en attente jusqu'à maia reply <id> <demande> "réponse", puis le parent continue avec la réponse.

États (de chaque agent, et du workflow) :

État Sens
non exécuté agent cité dans les sections du parent mais pas encore lancé (ou workflow pas encore démarré)
en exécution il travaille
en attente il attend une réponse : autorisation (approve) ou intervention (reply)
terminé il a rendu son résultat
en erreur échec, délai dépassé, arrêt (maia stop), ou processus disparu

Le workflow est en attente si l'un de ses agents attend, en erreur si son agent parent a échoué ou si son processus a disparu.

Interroger MAIA :

maia workflows [--all] [--json]          # workflows en cours (--all : tous)
maia workflow <id> [--json] [--watch]    # état de chaque agent + demandes en attente (avec la commande pour y répondre)
maia logs <id> [agent] [-f]              # journal horodaté (-f : en continu)
maia result <id>                         # résultat final de l'agent parent
maia approve <id> r1 [--deny]            # répondre à une autorisation
maia reply <id> r2 "texte"               # répondre à une intervention
maia stop <id>                           # arrêter (sous-agents compris)

<id> : identifiant (wf_20261026_234112_a3f9), un morceau unique de celui-ci, le nom donné par --name, ou last. Dans MAIA interactif : /workflows (/workflows all) et /workflow <id>.

Registre (global) : ~/.maia/workflows/<id>/ — workflow.json, agents.json (états), events.jsonl (journal), requests/ (demandes et réponses), traces/ (conversation de chaque agent), result.md, run.log (mode --detach). Pas de serveur : les commandes lisent ces fichiers, écrits uniquement par le processus du workflow (les réponses et l'arrêt sont déposés dans des fichiers séparés).

Reste à faire

  • Provider LiteLLM : validation sur un vrai serveur (streaming SSE et appels d'outils testés seulement sur des flux simulés) ; compatibilité de @inkjs/ui avec Ink 8 à confirmer à l'usage (l'affichage des badges a été vu fonctionner par l'utilisateur).
  • Agent : hooks ; MCP : OAuth, ressources/prompts, outils MCP pour les sous-agents ; sous-agents : reasoning_effort du modèle non utilisé (l'effort borne les étapes), pas de sous-agent imbriqué.
  • Éventuellement rétablir sous .maia ce que supprimait l'abandon de .claude : variables d'environnement et slash-commandes personnalisées (le code des commandes existe encore dans packages/agent/src/commands.ts, il n'est plus appelé).
  • @fichier dans le corps d'une commande personnalisée ; autocomplétion des chemins @ à la saisie.
  • Liste à cocher (choix multiples) : n'existe pas encore ; elle recevrait le badge ASK.
  • Taille de contexte : lue en interrogeant le LLM (demande de l'utilisateur) ; la route /model/info de LiteLLM (max_input_tokens) serait plus fiable, et un réglage manuel dans settings.json pourrait corriger une réponse erronée.
  • Nerd Fonts (icônes) : proposé comme réglage optionnel dans settings.json, non réalisé.
  • Tests : les suites vitest et le typecheck sont à lancer après pnpm install (écrits sans avoir pu être exécutés dans l'environnement de développement).

Travaux réalisés

Journal des évolutions, dans l'ordre :

  1. Squelette : monorepo pnpm (TypeScript strict, tsup, vitest), contrats LLMProvider / Tool / AgentUI, boucle agent avec appels d'outils, agent par défaut de type « Claude Code » (outils Read, Write, Edit, Bash).
  2. Provider SAP AI Core puis choix du modèle au démarrage et commande /model. (Remplacé ensuite par LiteLLM, voir 12.)
  3. Outils Glob et Grep (JavaScript pur, sans ripgrep).
  4. Configuration de l'agent (lot 2) : fichiers de contexte, permissions, slash-commandes, skills. (Abandonné avec les dossiers .claude, voir 13.)
  5. §skill : appel direct d'un skill de l'agent.
  6. Dossier de lancement : tous les chemins relatifs partent du dossier où MAIA est lancé (MAIA_CWD > INIT_CWD > cwd).
  7. Interface Ink (remplace Clack.js) : bannière « MAIA » avec masque pixel-art et titre arc-en-ciel, messages en bleu clair, zone de saisie en pointillés greenBright avec spinner arrow3, attente « En réflexion » avec dots10 en yellowBright.
  8. Outils TodoWrite et WebFetch (lot 3, première partie).
  9. !commande (avec la syntaxe !'commande avec paramètres') et @fichier.
  10. Renommage en MAIA (titre « Maia ») et sessions : ./.maia/.sessions, sauvegarde automatique, /rename, /load (avec réaffichage du contenu), /new.
  11. Corrections : import manquant de normalizeSessionName, session.tools est une Map, en-tête de skill avec espace avant les deux-points (description : …).
  12. LiteLLM : un seul provider, requêtes au format OpenAI en streaming vers le endpoint OpenAI de LiteLLM ; configuration ~/.maia/settings.json (LiteLLM_URL, OpenAI_EndPoint, LiteLLM_Models, LiteLLM_API_Key optionnel). Suppression de SAP AI Core, Anthropic, OpenAI et Ollama.
  13. Renommage technique magiccode → maia (packages @maia/*, commande maia, variables MAIA_*, dossier .maia) ; textes « MagicCode » → « MAIA » ; bannière « Un seul agent, plusieurs modèles ».
  14. Skills dans .maia/skills (dossier de lancement, dossier parent, dossier utilisateur) ; les dossiers .claude ne sont plus lus.
  15. Badges @inkjs/ui : INFO, WARN, FAIL (texte d'erreur #DC8ADD) et ASK devant les questions.
  16. Suppression des modes et des extensions : un seul agent MAIA (packages/agent), contexte de départ MAIA.md sinon CLAUDE.md (message INFO, ou WARN si aucun).
  17. Outil CurrentModel : indique au modèle quel LLM est utilisé dans la session.
  18. /clear : supprime le contexte et recharge MAIA.md / CLAUDE.md (et les skills) comme au démarrage ; /new fait de même.
  19. Permissions granulaires : règles allow / ask / deny par outil (Outil ou Outil(motif)), évaluées dans l'ordre deny > ask > allow, ask par défaut ; définies dans ~/.maia/settings.json et ./.maia/settings.json (pas le dossier parent).
  20. /permissions : affiche les permissions chargées, par fichier et par type (badges ALLOW / ASK / DENY) ; les fichiers sont listés l'un sous l'autre au démarrage.
  21. Rendu markdown des réponses de l'agent via markdansi, dans un seul composant isolé (packages/ui/src/markdown.tsx), désactivable avec MAIA_NO_MARKDOWN=1.
  22. Image de la bannière : nouvelle image (visage à couronne), fond retiré, réduite à 32 × 32 en couleurs réelles (sans pixelisation ni réduction de palette), centrée verticalement à côté du titre ; données générées par packages/ui/scripts/make-mask.py.
  23. Barre de progression du contexte sous la zone de saisie (1 carré = 10 000 tokens) : taille maximale demandée au LLM après la sélection du modèle, contexte utilisé mis à jour après chaque réponse (consommation renvoyée par LiteLLM, sinon estimation) ; correction d'un import de type manquant dans ink-ui.tsx. Valeur alignée sur le contexte réel de la session : contexte en attente compté, tokens de raisonnement exclus, estimation calibrée sur le serveur et signalée par ~.
  24. Outils personnalisés : ./.maia/tools/<nom>/TOOL.json (dossier de lancement uniquement) ; paramètres number/string avec valeurs par défaut, run avec {{paramètre}} exécuté sans shell dans le dossier du tool (aucune injection possible), permission ask par défaut ; module packages/agent/src/custom-tools.ts, exemple examples/tools/addition ; exemple de sous-agent : examples/agents/code_reviewer (à copier dans ./.maia/agents).
  25. Threading async des outils : 1 outil au premier plan ; dès 2 outils, une tâche de fond par outil, attente de toutes les fins avant de répondre au LLM (résultats dans l'ordre demandé, 8 en parallèle au plus, verrou par fichier).
  26. Compactage : /compact et compactage automatique au seuil Max_Token_Context - (4000 + 500) ; résumé en 5 rubriques comme contexte de départ (à la place de MAIA.md / CLAUDE.md), session renommée xxx__n (cycle 1..1000), barre de contexte recalculée.
  27. Correction de la barre de contexte : comptage local avec js-tiktoken (o200k_base) de tout le contexte (système, outils, messages, contexte en attente), recalculé avant chaque saisie ; fin du mélange consommation du serveur / estimation calibrée (cause des variations) et du marqueur ~.
  28. Documentation : SPECS_TECH.md, spécifications techniques de toutes les fonctionnalités implémentées.
  29. Outil Write plus tolérant : message d'erreur qui liste les paramètres reçus (tous les outils), alias de content acceptés, description du paramètre précisée.
  30. Appels d'outils coupés ou mal assemblés : paramètre optionnel MaxOutputTokens, avertissement quand la réponse est coupée (max_tokens), assemblage plus tolérant des fragments d'arguments d'appels d'outils du flux SSE, journal de diagnostic MAIA_DEBUG_LLM.
  31. Coupure à 4096 tokens diagnostiquée (log MAIA_DEBUG_LLM) : détection d'une sortie tombant sur une limite usuelle avec un appel d'outil, message d'erreur de Write qui conseille d'écrire en plusieurs morceaux.
  32. Outil Bash via runShell : timeout et Ctrl+C arrêtent toute l'arborescence de processus (groupe de processus, SIGTERM puis SIGKILL) ; stdout et stderr fusionnés dans l'ordre d'arrivée, dernière ligne [code de sortie : N] ou état (délai dépassé, interrompu).
  33. Sous-agents : définitions ./.maia/agents/<nom>/AGENT.md et ~/.maia/agents (en-tête YAML : name, description, model, tools, permissions, effort, skills, omitMd, trace ; sections Instructions system / Missions / Output), outil Agent, processus autonome (fork + IPC), conversation vide, résumé final seul renvoyé au parent, confirmations relayées à l'interface principale, notification INFO de fin, trace optionnelle session_…-agent_1.json, commande /agents.
  34. Compactage sur « Input is too long » : réponse du serveur reconnue (isContextTooLongError), compactage d'urgence (résultats d'outils tronqués dans la demande de résumé), reprise de la demande une fois.
  35. Barre de contexte pendant le travail de l'agent : taille recomptée à chaque itération (après la demande, chaque réponse du LLM et chaque série de résultats d'outils) et barre affichée pendant les itérations, plus seulement à la saisie.
  36. Barre de contexte toujours affichée : en bas de l'écran en permanence (saisie, itérations, outils, confirmations, listes) ; si la taille maximale du contexte est inconnue, seul le nombre de tokens utilisés est montré.
  37. Zone de saisie toujours visible : grisée pendant le travail de l'agent (« Traitement en cours — Ctrl+C : interrompre »), et c'est elle qui porte les demandes d'autorisation (ASK … (o/n)), dans le même cadre en pointillés.
  38. Ligne d'état réservée au-dessus de la zone de saisie (« En réflexion… », outils en cours) : toujours présente, vide hors traitement, pour que la zone de saisie ne remonte ni ne descende.
  39. Limite d'itérations : 50 par défaut, réglable avec MaxIterations dans ~/.maia/settings.json ; à la limite, MAIA demande s'il faut continuer (o/n) pour une nouvelle série ; une demande tapée après un arrêt est fusionnée dans le dernier message utilisateur (alternance respectée).
  40. Guide des permissions : GUIDE_PERMISSIONS.md (syntaxe, exemples simples et complexes, interdiction d'une commande cachée dans un tube, limites).
  41. Signature dans la bannière : « Arnaud VINCENT (avec ClaudeCode) » sous « Un seul agent, plusieurs modèles » (ligne séparée par un espace ; nom en #EEF527, ClaudeCode en #F57D27).
  42. Lancement direct d'un sous-agent : une saisie qui commence par & (&Agent_1 tâche…) exécute le sous-agent sans passer par le LLM ; son résumé est affiché et ajouté au contexte.
  43. /createAgent (ex-/newAgent) : création guidée d'un sous-agent (niveau projet ou global) à partir du modèle commenté .ressources/AGENT.md ; commentaires de fin de ligne acceptés dans l'en-tête de AGENT.md, commentaires HTML des sections ignorés.
  44. Règles Redirect : autoriser, demander ou interdire les redirections de sortie (>, >>, 2>, &>) fichier par fichier ; sans règle, elles demandent confirmation ; 2>&1 et les > entre guillemets ne comptent pas ; guide mis à jour (§ 8).
  45. Serveurs MCP distants : déclaration globale ~/.maia/mcp.json (expansion ${VAR}, type http/sse, headers, timeout, includeTools/excludeTools), aucun serveur actif par défaut, activation par Mcp(serveur) dans le settings.json, Mcp(serveur,outil) pour retirer/confirmer un outil, Mcp(serveur,*) en deny désactive sans connexion ; connexion parallèle après traitement des permissions ; outils mcp__serveur__outil ; commande /mcp (bandeaux OK vert / FAIL rouge).
  46. Outils personnalisés utilisateur : chargés aussi depuis ~/.maia/tools ; à nom égal l'outil du projet (./.maia/tools) l'emporte, sans avertissement ; pris en compte par les sous-agents.
  47. Réponse « stop » aux demandes d'autorisation d'outil (o/n/s) : l'action n'est pas exécutée, l'itération s'arrête sans rollback (le contexte est conservé), puis la saisie suivante est ajoutée à la conversation ; sert à donner une information oubliée ou à changer de sujet.
  48. Section # Constraints des agents (facultative, après # Missions) : limites et conditions d'intervention de l'utilisateur ou d'un tiers ; incluse dans le prompt du sous-agent avec la consigne de s'arrêter et de le signaler dans son résumé ; modèle .ressources/AGENT.md et exemple code_reviewer mis à jour.
  49. Workflows autonomes : maia run AGENT.md (agent parent sans interface, sous-agents de son dossier .maia/agents + globaux, modèle hérité sauf model), --detach, registre global ~/.maia/workflows, états non exécuté / en exécution / en attente / terminé / en erreur, autorisations wait (défaut) / deny / allow, outil RequestIntervention, commandes workflows, workflow, logs, result, approve, reply, stop (texte ou --json), /workflows en session, DefaultModel dans settings.json ; exemple examples/agents/release_check.
  50. Guide des agents autonomes : GUIDE_AUTONOMOUS.md (préparer les agents, agent parent, sous-agents, lancement, autorisations sans interface, supervision, interventions, scripts / tâches planifiées / services, registre, limites, dépannage).
  51. Barre de contexte qui semblait figée : au-delà de 100k, la valeur n'était affichée qu'au millier près (123k) et un carré vaut 10 000 tokens, donc plusieurs étapes passaient sans changement visible ; affichage à 100 tokens près (123.5k, 1.049M) et mise à jour en direct pendant la réponse du modèle (toutes les 250 ms au plus).
  52. Touches Début / Fin (Home / End) dans la zone de saisie : curseur au début ou à la fin du prompt (comme Ctrl+A / Ctrl+E).
  53. Maj+Entrée dans la zone de saisie : saut de ligne, le prompt n'est pas envoyé (Alt+Entrée et Ctrl+J aussi, pour les terminaux qui n'envoient pas Maj+Entrée distinctement ; la séquence « ESC O M » envoyée par certains terminaux, qui s'affichait « OM », est aussi reconnue).
  54. Maj+Entrée avec Ink 8 : Ink 8 ne transmet plus que « Entrée » pour Maj+Entrée sur certains terminaux (le journal des touches montre \r + return, sans shift). Maia demande donc le protocole clavier kitty (kittyKeyboard: { mode: "auto" }), qui rend Maj+Entrée distinct sur les terminaux compatibles (Kitty, WezTerm, Ghostty, iTerm2, Windows Terminal récent…). Ailleurs (constaté sur le terminal de l'auteur : \r + return pour Entrée comme pour Maj+Entrée), les deux touches sont indiscernables : utiliser Alt+Entrée ou Ctrl+J, qui fonctionnent partout.
  55. Astuce Konsole (KDE) : Konsole n'envoie pas Maj+Entrée distinctement. Pour l'obtenir : Configuration → Modifier le profil courant → Clavier → Modifier, ajouter la ligne key Return+Shift : "\n", valider puis rouvrir l'onglet. Maj+Entrée envoie alors un saut de ligne brut (comme Ctrl+J), que Maia traite comme un saut de ligne. (Syntaxe non testée sur Konsole : en cas d'échec, MAIA_DEBUG_KEYS=1 montre ce qui est reçu.)
  56. Diagnostic des touches : MAIA_DEBUG_KEYS=1 pnpm maia écrit chaque touche reçue (texte + drapeaux Ink) dans ./.maia-keys.log, pour comprendre pourquoi une touche (Maj+Entrée…) n'est pas reconnue.
  57. Barre de contexte : js-tiktoken uniquement. L'essai avec usage.total_tokens du serveur a été retiré : la taille du contexte est toujours comptée localement avec js-tiktoken, la consommation du serveur ne sert qu'à détecter une réponse coupée.
  58. /delete : suppression d'une ou plusieurs sessions (Espace pour cocher, Suppr pour supprimer, confirmation), sauf la session courante.
  59. /models : liste des modèles disponibles auprès de LiteLLM (interrogé à chaque appel, LiteLLM_URL + LiteLLM_Models), modèle actif signalé ; aucun changement de modèle.
  60. Outil ListModels : l'agent (et les sous-agents, les workflows) peut lister les modèles disponibles auprès de LiteLLM.