sc.Scan() rend false aussi bien a la fin normale d'un flux qu'a la
premiere erreur de lecture, et sc.Err() n'etait JAMAIS consulte. Une
connexion reinitialisee en plein milieu, ou une ligne plus longue que
le tampon, etait donc indiscernable d'une fin propre : le tour
s'arretait sans un mot pendant que le journal de llama-server affichait
un « stop processing » parfaitement normal. C'est le symptome rapporte
— l'agent enchaine quelques commandes puis rend la main sans avoir
termine ni commente.
Pire : des appels d'outils accumules a moitie auraient ete EXECUTES
avec des arguments tronques. On abandonne desormais le tour en nommant
la cause, le texte deja recu reste affiche, et un stop volontaire reste
silencieux. Trois tests couvrent les trois cas ; celui du flux coupe
echoue bien sur l'ancien code.
Tampon du scanner porte de 1 a 8 Mio au passage : la cause la plus
probable d'une ligne trop longue est un appel d'outil demesure, et
jusqu'ici il partait dans le meme silence.
Sans rapport, trouve en faisant tourner la suite : renameAside tirait
son nom du seul UnixNano, dont la granularite Windows peut rendre deux
fois la meme valeur — deux ecartements se disputaient alors le meme
nom, le second ecrasant le binaire mis de cote par le premier. Un
compteur atomique les departage.
Deux boutons d'export cote a cote ne laissaient aucun choix : tout ou
rien. Un bouton unique ouvre desormais une fenetre ou l'on choisit le
format (Markdown a lire / JSON a retraiter), ce qu'on emporte
(raisonnements, appels d'outils, sorties d'outils, ou journal
d'affichage cote JSON) et la portee (tout le fil ou les N derniers
echanges). Une ligne dit ce que l'export contiendra, et l'en-tete du
fichier signale un export partiel : relu plus tard, il ne doit pas
passer pour le fil complet.
Memes options en ligne de commande : --last N, --no-reasoning,
--no-tools, --no-results, --messages-only. Sans option, l'export reste
complet, endpoint compris.
« clear chat » descend en derniere position et seul sur sa ligne :
c'est la seule action destructive du panneau, elle n'a rien a faire
sous le doigt qui visait « refresh ».
Au passage, issue #20 : le prompt systeme et la description de l'outil
mem_search annoncaient encore la memoire sous MEMORY/, nom d'avant la
0.8. Le modele lisait donc un chemin qui n'existe plus. Corrige, avec
les deux commentaires restes au meme nom.
Bug 1 (chat bloque, remonte par Nathan) : trois causes cumulees.
- runShell naissait d'un contexte independant du tour : stop n'arretait rien.
La commande herite desormais du contexte du tour, et la boucle d'outils
s'interrompt des que l'arret est demande.
- cmd.WaitDelay : une commande laissant un process detache gardait les tubes
ouverts, donc Wait ne revenait JAMAIS (ni delai, ni stop).
- Reset debloque toujours l'etat de generation : « nouvelle conversation » etait
le geste tente quand le chat coince, il ne servait a rien. Un tour perime ne
libere plus le tour courant.
Trouve en testant : le workspace disparu cassait TOUTES les commandes suivantes
(chdir: no such file or directory) jusqu'au redemarrage ; il est recree au besoin.
Bug 2 (raisonnement, remonte par Nathan) : l'UI ecrivait « off » en EFFACANT la
ligne REASONING, or sans drapeau le moteur suit le gabarit du modele, qui
raisonne. L'UI ecrit maintenant on|off et « off » part en --reasoning off, apres
avoir demande au binaire s'il connait le drapeau (ik_llama.cpp ne le connait pas
et refuserait de demarrer). Presets existants inchanges ; l'editeur dit desormais
« non precise : le modele decide » quand la cle est absente.
- Le mode agent redevient une vraie fermeture : une surcharge de requete ne peut
plus RALLUMER l'agent (donc bash/write/edit/MCP) quand la machine l'a coupe.
- Une lecture ratee de la cle de pilotage ne se confond plus avec « aucune cle » :
requireWebAuth ferme (503) au lieu d'ouvrir l'API.
- healthCheck : timeout de 3 s. Sans lui, un moteur qui accepte la connexion sans
repondre laissait /api/chat/send bloque indefiniment.
- Acces distant : la session de tunnel ecoute enfin un contexte. Un redemarrage ne
fait plus cohabiter deux sessions ; le backoff se remet a zero apres une session
qui a tenu.
- Flux SSE : selection des evenements neufs par dichotomie au lieu de relire tout
le journal a chaque token et pour chaque appareil.
- Cache de lecture de la configuration, invalide par ecriture locale, par mtime et
par age : fin des ~100 ouvertures de base par tour agentique.
- Erreurs moteur classees par type (errors.Is / net.Error) et non par sous-chaine
anglaise ; les 3 compactions partagent un seul chemin ; champs morts de chatReq
retires ; ReadHeaderTimeout sur les deux serveurs HTTP.
- suppression des migrations d'agencement, d'unites et de dossier de donnees
- store bbolt unique (ajean.db) : config, prefs, conversation, cles, drapeaux
- arborescence backends/ bin/ presets/ memory/ models/ workspace/
- plus aucune variable, unite ou asset nomme jean
Le compactage du contexte detruisait ce dont l'IA avait besoin pour continuer
une recherche, ce qui la faisait boucler ou repartir de zero.
- Compactage : le resume est desormais fabrique a partir du torse ORIGINAL. On
degraissait les resultats d'outils (page web -> marqueur) AVANT de resumer :
le resumeur ne voyait plus aucune information trouvee, donc le resume n'en
contenait aucune et l'IA relancait les memes recherches sans fin. Les
resultats sont seulement raccourcis (1200 car.) pour le resumeur ; le torse
degraisse ne sert plus que de repli si le resume echoue.
- Compactage : la demande EN COURS est preservee telle quelle. Pendant une
boucle d'outils il n'y a aucun message user recent, elle tombait donc dans le
torse et se dissolvait dans le resume, tandis que le TOUT PREMIER message de
la conversation restait epingle en tete : l'IA repondait a la question du
debut et abandonnait la recherche.
- Compactage : prompt du resumeur refait (findings, sources deja consultees,
etat d'avancement) et plafond du resume porte de 1500 a 2200 caracteres.
- Appels repetes : l'avertissement passe EN TETE du resultat et, des la 2e
redemande identique, le contenu n'est plus renvoye. Colle a la fin d'un
resultat de plusieurs milliers de caracteres, il passait inapercu.
- Marqueur d'effacement du compactage : dit explicitement de ne PAS rappeler
l'outil (l'ancien texte se lisait comme une invitation a re-telecharger).
- Outils web : sortie bornee a 8000 caracteres comme le shell. Un
web_read(limit=500) pouvait injecter 25000 caracteres d'un coup et declencher
les compactages en cascade.
- Bulles d'outils : la borne d'affichage passe de 4000 a 12000 caracteres, donc
l'etiquette « ~N tok » cesse d'afficher toujours ~1004 (la valeur du plafond)
et donne la vraie taille. Le chemin « appel identique non rejoue » envoyait
lui le resultat brut sans troncature.
Socle du renommage jean -> AJEAN, sans aucun impact visible pour le parc
installe. Trois briques :
- module github.com/nathaninline/ajean, cmd/ajean, internal/ajean,
package ajean. Purement interne.
- AjeanHome() remplace JeanHome(). Les defauts deviennent
%ProgramData%\ajean et /etc/ajean, avec migration du dossier existant
au premier lancement. La migration est un os.Rename dans le dossier
parent de l'ancien chemin : intra-volume, donc atomique et instantane
meme avec des .gguf de plusieurs dizaines de Go. Si le rename echoue
(handle ouvert sous Windows, droits, volume en lecture seule) on
CONTINUE sur l'ancien chemin et on retentera : jamais de copie, jamais
de suppression, le pire cas est "rien n'a bouge".
$AJEAN_HOME et $JEAN_HOME restent tous deux honores, ainsi que
/etc/default/ajean et /etc/default/jean : un chemin impose a la main
est un ordre, on ne le migre pas.
- l'auto-MAJ accepte desormais ajean-<os>-<arch> ET jean-<os>-<arch>,
dans cet ordre. Indispensable dans les deux sens : les binaires deja
installes ne connaissent que jean-*, et ce binaire-ci doit pouvoir
s'installer depuis une release anterieure au renommage.
Couvert par des tests, dont le repli quand un handle ouvert bloque le
rename sous Windows.