diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5946c5..4ae32a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 0.37.1 — 2026-10-26
+
+### Ajouté — version du service d'agents dans le panneau Spark (mode sidebar)
+
+Carte fusion #64. Le pied du panneau Spark (`static/js/draco-panneau.js`)
+affiche désormais la ligne discrète et permanente « Service d'agents
+vX », comme la console pleine page. La version est lue **en direct**
+dans la santé du service (`GET /healthz` de l'origine que le panneau
+connaît déjà), à l'ouverture du panneau, avec cache de session — jamais
+une valeur codée en dur ; service injoignable ou panneau non configuré
+= rien d'affiché, aucune erreur visible.
+
## 0.37.0 — 2026-10-26
### Ajouté — tri « En attente de revue » sur le dashboard
diff --git a/app/config.py b/app/config.py
index 81f73ba..114a858 100644
--- a/app/config.py
+++ b/app/config.py
@@ -177,7 +177,7 @@ from urllib.parse import urlparse
BASE_DIR = Path(__file__).resolve().parent.parent
-APP_VERSION = "0.37.0"
+APP_VERSION = "0.37.1"
def _env_bool(name: str, default: bool) -> bool:
diff --git a/pyproject.toml b/pyproject.toml
index 130caba..0882358 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kanbante"
-version = "0.37.0"
+version = "0.37.1"
description = "Kanban project manager integrated with Gitea"
requires-python = ">=3.11"
dependencies = [
diff --git a/static/js/draco-panneau.js b/static/js/draco-panneau.js
index 4ce4e1c..42d8a1f 100644
--- a/static/js/draco-panneau.js
+++ b/static/js/draco-panneau.js
@@ -43,6 +43,7 @@
var etatConnu = false;
var root = null, msgsEl = null, etatEl = null, inputEl = null, sendBtn = null;
var titreEl = null, tiroirEl = null;
+ var versionEl = null, versionConnue = "", versionMaj = 0;
function domaine() {
try { return localStorage.getItem("draco_domain") || "pro"; } catch (e) { return "pro"; }
@@ -268,6 +269,7 @@
' ' +
' ' +
'
Conversation réelle, sous-agents (J3) et missions longues (J5) vers les exécuteurs configurés ; toute écriture dans les apps passe par une approbation.
' +
+ ' ' +
'';
document.body.appendChild(root);
msgsEl = root.querySelector(".draco-panneau-msgs");
@@ -276,6 +278,7 @@
sendBtn = root.querySelector("button[type=submit]");
titreEl = root.querySelector(".d-titre");
tiroirEl = root.querySelector(".draco-discussions");
+ versionEl = root.querySelector(".draco-panneau-version");
root.querySelector(".fermer").addEventListener("click", fermer);
root.querySelector('[data-disc="liste"]').addEventListener("click", basculerTiroir);
root.querySelector('[data-disc="nouvelle"]').addEventListener("click", nouvelleDiscussion);
@@ -330,6 +333,35 @@
titreEl.title = titreEl.textContent;
}
+ /* ── Version du service : lue en direct dans la santé de
+ * l'agent (GET /healthz → champ « version », la même source que
+ * la console) à l'origine que le panneau connaît déjà. Cache de
+ * session (5 min), rafraîchie à l'ouverture du panneau, au même
+ * rythme que la vérification d'identité. Jamais de valeur codée
+ * en dur : source injoignable ou réponse illisible = la ligne
+ * reste masquée — pas de fausse version, pas d'erreur visible. */
+ function afficherVersion() {
+ if (!versionEl) return;
+ if (versionConnue) {
+ versionEl.textContent = "Service d'agents v" + versionConnue;
+ versionEl.hidden = false;
+ }
+ }
+ function chargerVersion() {
+ if (!API || !versionEl) return;
+ if (versionConnue && Date.now() - versionMaj < 300000) { afficherVersion(); return; }
+ fetch(API + "/healthz")
+ .then(function (r) { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); })
+ .then(function (d) {
+ if (d && typeof d.version === "string" && /^[0-9]+\.[0-9]+\.[0-9]+/.test(d.version)) {
+ versionConnue = d.version;
+ versionMaj = Date.now();
+ afficherVersion();
+ }
+ })
+ .catch(function () { /* service injoignable : rien d'affiché */ });
+ }
+
function verifierIdentite() {
if (etatConnu) return;
if (!API) {
@@ -812,6 +844,7 @@
root.classList.add("open");
appliquerTheme();
verifierIdentite();
+ chargerVersion();
setTimeout(function () { inputEl.focus(); }, 300);
}
function fermer() { if (root) root.classList.remove("open"); }