diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3658648..96b992e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog - FlowDeck
+## v7.83.1 (2026-10-26) — Panneau Spark : version du service d'agents visible en mode sidebar
+
+- **Panneau Spark** (`static/js/draco-panneau.js`) : le pied du panneau
+ 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 = rien
+ d'affiché, aucune erreur visible. Carte fusion #64.
+
## v7.83.0 (2026-10-26) — Fusion : serveur MCP complété (ACC-5.1) et pont MCP vers ObsiGate (ACC-2.2)
- **ACC-5.1 — serveur MCP Flowdeck complété** (`app/routers/mcp_server.py`,
diff --git a/VERSION b/VERSION
index 22bce22..de3f8e8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-7.83.0
+7.83.1
diff --git a/app/main.py b/app/main.py
index 1f9fdb7..3ec4d37 100644
--- a/app/main.py
+++ b/app/main.py
@@ -216,7 +216,7 @@ def _lire_version() -> str:
# Littéral de version synchronisé avec le fichier VERSION :
-# version="7.83.0"
+# version="7.83.1"
# Patron historique des bumps (voir scripts/bump_version.py, qui cherche
# ce littéral) et contrôle de tests/test_pwa_offline.py. La version
# runtime reste lue dynamiquement depuis VERSION par _lire_version()
diff --git a/static/js/draco-panneau.js b/static/js/draco-panneau.js
index e4062ed..a7229a4 100644
--- a/static/js/draco-panneau.js
+++ b/static/js/draco-panneau.js
@@ -46,6 +46,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"; }
@@ -274,6 +275,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");
@@ -282,6 +284,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);
@@ -336,6 +339,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;
fetch(API + "/api/v1/me", { credentials: "include" })
@@ -812,6 +844,7 @@
root.classList.add("open");
appliquerTheme();
verifierIdentite();
+ chargerVersion();
setTimeout(function () { inputEl.focus(); }, 300);
}
function fermer() { if (root) root.classList.remove("open"); }