Skip to content

fix: plafonner le streaming sans résultat dans les snapshots + timeout 60 s skippable (bouton « Passer le timeout ») - #384

Open
kryptonfly wants to merge 10 commits into
co-l:developfrom
kryptonfly:fix/streaming-snapshot-and-timeout
Open

kryptonfly wants to merge 10 commits into
co-l:developfrom
kryptonfly:fix/streaming-snapshot-and-timeout

Conversation

@kryptonfly

@kryptonfly kryptonfly commented Sep 27, 2026 •

Copy link
Copy Markdown

Contexte

Cinq correctifs identifiés sur l'instance locale (portés du bundle dist vers la source) :

  1. Snapshots : plafonner le streaming des appels sans résultat
    trimSnapshotStreamingOutput (fold-state.ts) plafonne désormais la sortie streaming des tool calls sans résultat : max 200 chunks / 100 Ko, la fin (tail) est conservée, flag streamingOutputTruncated + compteur truncatedStreams (additif, compatible avec les callers existants).

  2. run_command : timeout par défaut 120 s → 60 s
    shell.ts : timeout par défaut ramené à 60 s (description mise à jour).

  3. run_command : bouton « Passer le timeout » / « Skip timeout »

  • command.skipTimeout (message client, protocol.ts) + handler WS (server.ts) → ack skipped:true / NO_ACTIVE_TIMEOUT / INVALID_PAYLOAD.
  • skipCommandTimeout(toolCallId) (shell.ts) : chaque clic accorde une fenêtre de timeout complète supplémentaire (re-arm via onTimer), pas infini.
  • RunCommandView : bouton visible pendant status === 'pending', feedback client ✓ +60 s pendant 2,5 s.
  • ToolCallDisplay : timeout ?? 60_000 + passage du callId réel (fix du bug INVALID_PAYLOAD causé par args.id undefined).
  1. Plafonner les événements tool.output persistés par tool call
    createToolProgressHandler (tool-streaming.ts) limite désormais la persistance des chunks streaming à 500 événements par tool call (compteur par call, pas par session). Une commande très bavarde (ex. un process qui écrit en boucle serrée) peut émettre des milliers de chunks par seconde ; sans bornage, chaque chunk devenait un événement tool.output distinct et gonflait le log de session (observé : 172k événements / 472 Mo pour un seul appel, et le turn n'ayant jamais terminé, cleanupOldEvents n'a jamais pu les supprimer). Le tool.result final porte déjà la sortie complète (tronquée), les chunks persistés ne servent qu'à l'affichage live et à la récupération après crash — le plafonner borne le log sans perdre le résultat.

Tests

  • folding.test.ts : plafond (1100 chunks → tail conservé, truncatedStreams), petit stream intact.
  • shell-streaming.test.ts : skipCommandTimeout (id inconnu → false ; commande 1,2 s / timeout 1 s / skip à 400 ms → succès, registry nettoyé).
  • RunCommandView.test.tsx : bouton + envoi command.skipTimeout avec le callId, feedback, bouton absent si fini.
  • tool-streaming.test.ts : cap par appel (500 événements max, premier chunk conservé), compté par tool call et non par session.

Suite complète : 5832 tests passés ; typecheck server + web OK ; eslint + prettier OK ; builds tsup + vite OK.

Vérification sur l'instance en cours

WS testé : command.skipTimeout avec un id inactif → réponse NO_ACTIVE_TIMEOUT (case active). Session locale débloquée : un run_command en boucle VEH infinie (drtest.exe, ~2500 chunks/s) avait produit 172k événements tool.output (472 Mo) et le process était resté orphelin après un redémarrage du serveur, bloquant le turn (aucun tool.result). Ces patchs sont déjà appliqués sur le bundle dist local ; cette PR est la solution durable (les patchs dist seront écrasés par npm update -g openfox).

@github-actions github-actions Bot added the bug Something isn't working label Sep 27, 2026
run_command streams each stdout/stderr chunk as a separate tool.output event. A chatty command (e.g. a process printing in a tight loop) can emit thousands of chunks per second, bloating the session event log (observed: 172k events / 472 MB for a single call, and the turn never completed so cleanupOldEvents never ran). The final tool.result already carries the complete (truncated) output, so cap the persisted streaming chunks at 500 per call; the cap is per tool call, not per session.
Each command.skipTimeout click now extends the deadline by a fixed SKIP_TIMEOUT_WINDOW_MS (60s) instead of re-arming a fresh window sized to the command's own timeout. The UI mirrors the same fixed extension so the displayed total grows predictably. Shared constant lives in src/shared/constants.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant