mini-me depuis Node.js, avec le SDK ACP officiel : lib/minime.js
Un client Node.js qui utilise mini-me comme bibliothèque en pilotant mm -acp par ACP (ticket 188), construit sur le SDK ACP officiel pour TypeScript, @agentclientprotocol/sdk 2.2.0. Il a la même API que le client sans dépendance de ../nodejs/, qui parle JSON-RPC à la main : les exemples des deux dossiers sont les mêmes programmes. La bibliothèque est en JavaScript (ESM) avec des types JSDoc, donc sans étape de compilation. Voir ../README.fr.md pour les messages ACP qui se cachent derrière.
import { Agent } from "./lib/minime.js";
const agent = await Agent.start({ config: "agent.yaml" });
const { text } = await agent.complete("Explain this repository"); // sans stream
for await (const ev of agent.stream("And the tests?")) { // en stream
if (ev.kind === "text") process.stdout.write(ev.text);
}
const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);
await agent.complete("A long task", { signal: ac.signal }); // annulé
await agent.close();
Lancer les exemples
llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
llmman serve
cd demo.acp.sdk/nodejs-sdk
npm install # le SDK, et zod qu'il demande
node complete.js # sans stream
node stream.js # en stream, avec les appels d'outils
node cancel.js # annulé au bout de 3 secondes
node permission.js # une commande refusée, puis autorisée
Node 20 ou plus récent. mm doit être dans le PATH, ou désigné par MM_BIN. Les exemples utilisent agent.yaml (Qwen3.5 4B sur llmman, bash seulement, yolo: false) ; tout fichier d'agent fonctionne.
Pour utiliser la bibliothèque depuis un autre projet, tant qu'elle n'est pas publiée : npm install ../chemin/vers/demo.acp.sdk/nodejs-sdk, puis import { Agent } from "minime-sdk";.
L'API
await Agent.start({ config, cwd?, mm?, onPermission?, onStderr? }) |
lance mm -acp -config <config> (rendu absolu), puis initialize et session/new {cwd}. mm vaut par défaut $MM_BIN, puis mm. |
await agent.complete(prompt, { signal?, onEvent? }) |
un tour ; se résout en { text, stopReason, usage } une fois terminé. onEvent voit chaque événement au fil de l'eau. |
agent.stream(prompt, { signal? }) |
un tour sous forme d'itérateur asynchrone d'événements ; le dernier est done. Sortir de la boucle avant la fin annule le tour. |
signal (un AbortSignal) |
l'annuler envoie session/cancel ; le tour se termine avec stopReason: "cancelled" et le texte déjà reçu. |
onPermission(call) |
appelé quand l'agent veut lancer un outil qui demande une confirmation (yolo: false). Renvoyez true (autoriser une fois), false (refuser une fois) ou un optionId ACP ("allow_always", "never"). Sans lui, chacun de ces appels est refusé. Un callback qui lève une exception refuse. |
onStderr(line) |
le stderr de mm (bannière, avertissements, lignes [acp]). Sinon il est lu puis jeté ; sa dernière ligne est ajoutée aux erreurs. |
await agent.reset() |
une nouvelle session sur le même processus : la conversation repart de zéro. |
await agent.close() |
ferme le stdin de mm et attend qu'il se termine (le tue au bout de 5 s). L'appeler deux fois est sans effet. |
agent.sessionId |
l'identifiant de session ACP. |
eventOf(update), pickOption(options, choice) |
les deux fonctions pures, exportées pour les tests et pour qui traite lui-même des messages ACP. |
Les événements : {kind: "text", text}, {kind: "thinking", text}, {kind: "tool_call", id, title, toolKind, input}, {kind: "tool_result", id, status, output}, {kind: "done", stopReason, usage, text}.
Les règles à connaître :
- Un Agent = un processus mm = une conversation. Chaque appel la continue, comme dans un éditeur. Un seul tour à la fois par Agent (un second lève
already running) ; lancez plusieurs Agents pour travailler en parallèle. - Un tour que mm termine par une erreur est rejeté, avec les mots de mm et la dernière ligne de son stderr, par exemple
minime: Internal error (-32603): the model repeated the same bash call 6 times…. Si mm a planté, le message ditmm exited: exit status N. - mm retire de la conversation une question annulée : la question suivante n'en sait rien.
- Le callback de permission peut s'exécuter avant que l'événement
tool_callsoit lu. Le SDK répond aux requêtes de mm dès qu'elles arrivent, alors que les mises à jour de la session attendent dans une file que votre boucle les lise :stream.jsaffiche 🔐 avant 🛠️. Le client sans dépendance lit les deux dans l'ordre d'arrivée. - Le client n'annonce aucune capacité fichiers, terminal ou formulaire : mm n'en a pas besoin, et son outil
ask_usern'est pas proposé. Le SDK répond « method not found » à toute requête de ce genre venant de mm.
Les différences avec ../nodejs/
nodejs/ |
nodejs-sdk/ |
|
|---|---|---|
| Protocole | JSON-RPC écrit à la main (~300 lignes) | le SDK officiel : client(), ndJsonStream, buildSession(cwd).start(), ActiveSession.nextUpdate() |
| Dépendances | aucune | @agentclientprotocol/sdk 2.2.0 et zod (sa dépendance « peer ») |
| Validation des messages | aucune | le SDK vérifie les messages selon le schéma ACP |
| API | Agent.start, complete, stream, reset, close |
la même |
Tests
npm test # node --test test/*.test.js — 16 tests, sans modèle ni mm, ~1 s
test/fake-mm.js joue le côté mm d'ACP, comme un vrai processus enfant. Il écrit et lit lui-même les lignes JSON, avec les formats de messages de mm v1.0.3 (quatre options de permission, rawOutput.output, usage dans la réponse au prompt, available_commands_update après session/new) : des réponses, une réflexion, un appel d'outil avec sa permission, un tour lent à annuler, une erreur, un plantage.
Mesures (2026-11-01, llmman sur la machine de l'auteur, mm v1.0.3 compilé depuis l'arbre de travail, Node 22)
| Exemple | Résultat |
|---|---|
complete.js |
réponse en une phrase, end_turn, 663 tokens, 7,3 s |
stream.js |
find test -name "*.js" | wc -l autorisé par le callback (lecture seule), résultat 2, réponse streamée morceau par morceau, end_turn |
cancel.js |
histoire coupée au bout de 3,0 s (cancelled, 366 caractères gardés) ; la question suivante sur le même Agent a eu sa réponse aussitôt, sans connaître l'histoire, que mm a retirée |
permission.js |
pwd refusé : l'exemple n'affiche rien (le modèle a inventé /root, 3 fois sur 3) ; autorisé : le vrai répertoire |
Pas fait
- Pas de paquet npm publié : il faudrait livrer le binaire
mmavec, pour chaque plateforme. session/load/session/resume(sessions enregistrées), images et fichiers joints dans les prompts, formulairesask_user: non exposés.- Pas de délai maximum pour une annulation à laquelle mm ne répondrait jamais : le tour attend la réponse
cancelledde mm, comme ACP l'exige.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
|