--- name: opencode-streaming-progress-mcp-surfacing-157 description: memory note opencode-streaming-progress-mcp-surfacing-157 metadata: type: project --- # OpenCode (CLI custom) — streaming progress & bulle MCP Décision/fix du ticket #157 (itération 2, 2026-08-09). Profil OpenCode/GLM sur la CLI custom. ## Cause racine (vérifiée en source) `opencode run --format json` (v1.17.18, `packages/opencode/src/cli/cmd/run.ts`) émet chaque event JSONL **en temps réel** sur stdout. Schéma ligne : `{type, timestamp, sessionID, ...data}`. Types : `step_start`, `step_finish`, `text` (part textuelle finalisée sous `.part.text`, pas un delta token), `tool_use` (**seulement à** `part.state.status=completed|error`, pas de start ; nom outil sous `.part.tool`), `reasoning` (thinking), `error`. Le parser IdeA `crates/infrastructure/src/session/opencode.rs::parse_jsonl_record` reconnaît déjà ces types — il n'était pas cassé. Les 2 vraies causes : 1. `OpenCodeSession` ne surchargeait pas `send_with_tap` (défaut `domain/ports.rs:1380` ignore le tap) → `send` bufferisait tout le stdout jusqu'à l'EOF → silence total pendant le tour, burst à la fin. 2. La bulle `kind:Mcp`/`source:IdeaLocal` n'était émise que par `openai_compat.rs::apply_tool_calls`. OpenCode taggait les `tool_use` en `kind:Tool` générique. ## Fix (backend-only, commit 836137a3, branche `fix/ticket157-opencode-progress-mcp-surfacing`) - Override `send_with_tap` sur `OpenCodeSession` (pattern codex.rs/claude.rs) : stdout ligne par ligne, projection live au tap des non-terminaux, Final assemblé à la fin (jamais au tap). - Tagging MCP : `tool_use` préfixé `idea_` → `ReplyProgress{source:IdeaLocal, kind:Mcp, stage:Completed, tool_name}` (remplace la Progress Tool provider-native). - Protection replay : session continuée = premier messageID tamponné puis supprimé si un nouveau arrive. ## Règles durables pour les prochains travaux - **Tout adapter `AgentSession` qui peut streamer DOIT surcharger `send_with_tap`** (le défaut ignore le tap). Sans override, la CLI custom est muette jusqu'au Final. codex/claude/openai_compat l'ont ; OpenCode maintenant aussi. - **Bulle MCP dédiée** : n'existe que quand IdeA sait que l'outil est un outil MCP IdeA. Pour les profils provider-orchestrés (OpenCode/Codex/Claude), les outils tournent DANS le provider ; IdeA ne peut les tagger Mcp que heuristiquement (préfixe `idea_`). Pour openai_compat (IdeA orchestre), c'est natif (`IdeaLocal/Mcp`). - **opencode JSON est granulaire et honnête** : pas de deltas de tokens ni de tool-start. Ne pas simuler de faux stream. step_start/step_finish + text(finalisé) + tool_use(completed) = le maximum exposé. - **Dette** : events `reasoning` (thinking) d'OpenCode ignorés par le parser (`Ignored`). Ticket dédié à créer si on veut afficher le thinking. - **Réserve replay** : session continuée à messageID unique sans replay observable → tap muet jusqu'à l'EOF (`hold_until_new_message`) pour éviter un historique non rétractable. Cas frais = streaming immédiat. ## Validation `cargo test -p infrastructure session::opencode` = 20 passed (3 nouveaux tests streaming/mcp/replay). QA VERT, pas de régression (échecs larges mcp_server/orchestrator pré-existants sur base `a3d94d4d`). Vérif live user en attente (AppImage rebuild `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`). Branche non mergée dans develop en attente du live vert.