# Analyse Polling Matrix-NodeRed-OpenCode Implementation

**Retour intéressant de Claude.ai**

Oui, le polling est une approche valable, et c'est même ce que fait un client tiers comme filet de sécurité quand son flux SSE se coupe. Le ticket du plugin que j'ai cité décrit une récupération après coupure de flux : interroger `GET /api/session/{id}/message` et terminer sur un élément `assistant` suivi d'un `idle {outcome}`. Chaque requête est courte, donc aucun timeout long à gérer.

## Principe

Tu lances le prompt, puis tu boucles toutes les 3 à 5 secondes. À chaque tour, tu fais deux choses.

**1\. Récupérer les nouveaux messages**  
`GET /api/session/{id}/message?order=desc&limit=20`. Tu gardes dans le contexte du flow la liste des `id` déjà affichés, et tu n'affiches que les nouveaux, remis dans l'ordre chronologique. La doc précise que les éléments gardent l'ordre demandé d'une page à l'autre, avec une pagination par `cursor.next` / `cursor.previous`. Avec `limit=20` en `desc`, une page suffit tant que la session ne produit pas plus de 20 messages entre deux tours.

**2\. Savoir si c'est fini**  
Je te conseille de combiner deux endpoints :

*   `GET /api/session/active` : il liste les exécutions de session au premier plan que ce processus possède, et une session absente du résultat est inactive.
*   `GET /api/session/{id}/inbox` : il liste le travail en file d'attente durable pas encore livré.

La session est terminée quand elle est absente de `active` **et** que l'inbox est vide. Le second test sert à éviter une fausse fin. C'est un raisonnement de ma part, pas un comportement vérifié : entre l'admission du prompt et le démarrage de l'exécution, la session pourrait ne pas apparaître dans `active`, alors que l'inbox contient encore ton message.

Autre option : lire `GET /api/session/{id}` et regarder `time.idle` et `outcome`. Ces champs figurent dans le schéma `Session.Info` décrit par le ticket de migration du client Android. Je ne sais pas précisément comment ils évoluent pendant un tour, à tester.

## Limite à connaître

Le polling donne des messages par paliers (texte d'un step, appel d'outil terminé, etc.), pas un affichage token par token. Les deltas sont des événements éphémères du SSE, et le polling n'y a pas accès. Je n'ai pas vérifié à quelle fréquence la projection `/message` est mise à jour pendant qu'un message assistant est en cours. Regarde ce que renvoie une vraie session au fil du tour.

## Squelette Node-RED

```
[inject: toutes les 4 s, actif seulement pendant un échange]
        │
   [function: garde "polling" + garde-fou durée max]
        │
   GET /api/session/active ──► GET .../inbox ──► GET .../message?order=desc&limit=20
        │
   [function: diff + décision]
        ├─► sortie 1 : nouveaux messages → affichage / dashboard
        └─► sortie 2 : terminé → flow.set("polling", false)
```

Fonction de diff et de décision (à adapter après avoir inspecté une vraie réponse, car la forme exacte des messages est supposée) :

```javascript
// msg.active, msg.inbox, msg.messages = payloads des 3 requêtes précédentes
const sid   = flow.get("sessionID");
const seen  = flow.get("seen") || [];
const started = flow.get("startedAt");

const items = (msg.messages.data || []).slice().reverse(); // ordre chronologique
const fresh = items.filter(m => !seen.includes(m.id));
flow.set("seen", seen.concat(fresh.map(m => m.id)));

const activeIds = Object.keys((msg.active && msg.active.data) || {});
const isActive  = activeIds.includes(sid);
const inboxEmpty = ((msg.inbox && msg.inbox.data) || []).length === 0;
const done = !isActive && inboxEmpty;
const timedOut = Date.now() - started > 10 * 60 * 1000; // garde-fou 10 min

return [
  fresh.length ? { payload: fresh } : null,
  (done || timedOut) ? { payload: { done, timedOut } } : null
];
```

Pour la sortie 2, il suffit d'y brancher un nœud `change` qui met `flow.polling` à `false`.

## Points d'attention

*   **Initialisation** : au moment du POST prompt, stocke `sessionID`, `startedAt`, et pré-remplis `seen` avec les messages déjà présents, sinon tu réaffiches tout l'historique au premier tour.
*   **Attente utilisateur** : si l'agent attend une permission ou un formulaire, la session ne se termine pas. Ajoute `GET /api/session/{sid}/form` et la liste des permissions en attente (`GET /api/session/{sid}/permission`, d'après le sommaire de l'API) dans la boucle, sinon ton flux paraîtra figé.
*   **Erreurs** : une exécution qui échoue doit se voir dans les messages ou dans `outcome`. Teste avec un modèle volontairement mal configuré pour voir à quoi ça ressemble.
*   **Instabilité de l'API** : `GET /api/session/active` est dans la section « session » sans le préfixe `experimental`, mais l'ensemble de la surface V2 reste décrite comme expérimentale.

Si tu veux, je peux te préparer le JSON complet du flow (création de session, envoi, boucle de polling, affichage). Il me faudrait juste savoir comment tu veux afficher les messages (dashboard Node-RED, debug, MQTT, autre).