diff --git a/vps_opencode_bot/opencode_bot.py b/vps_opencode_bot/opencode_bot.py
index 4c8ec80..5d669e6 100644
--- a/vps_opencode_bot/opencode_bot.py
+++ b/vps_opencode_bot/opencode_bot.py
@@ -376,6 +376,8 @@ def ok_sesion(chat):
def procesar_texto(chat, texto):
+ """Envia la peticion de forma asincrona y entrega el resultado por partes
+ (streaming), para que el usuario vea progreso como en el terminal."""
sid, err = ok_sesion(chat)
if not sid:
enviar(chat, f"❌ No hay sesión OpenCode disponible: {err}")
@@ -384,15 +386,68 @@ def procesar_texto(chat, texto):
modelo = st.get("model")
stop = typing(chat)
t0 = int(time.time() * 1000)
- try:
- data, err = enviar_mensaje(sid, texto, modelo=modelo)
- finally:
+
+ payload = {"parts": [{"type": "text", "text": texto}]}
+ if modelo:
+ payload["model"] = {
+ "providerID": modelo.get("providerID"),
+ "modelID": modelo.get("id"),
+ }
+ code, resp = oc_post(f"/session/{sid}/prompt_async", payload, timeout=40)
+ if code not in (200, 204):
stop.set()
- if data is None:
- enviar(chat, f"❌ Error del servidor OpenCode: {err}")
+ enviar(chat, f"❌ Error al lanzar OpenCode: {resp}")
return
- rtexto, _ = extraer_respuesta(data)
- # ficheros tocados en este turno
+
+ vistos_texto = set()
+ vistos_tool = set()
+ comienzo = time.time()
+ terminado = False
+ while time.time() - comienzo < 900:
+ time.sleep(3)
+ code, hist = oc_get(f"/session/{sid}/message?limit=4")
+ if code == 200 and isinstance(hist, list):
+ for item in reversed(hist[-4:]):
+ info = item.get("info") or {}
+ if info.get("role") != "assistant":
+ continue
+ for p in item.get("parts") or []:
+ t = p.get("type")
+ pid = p.get("id") or ""
+ if t == "text" and p.get("text") and pid not in vistos_texto:
+ vistos_texto.add(pid)
+ enviar(chat, formatear_html(p["text"]))
+ elif t == "tool" and pid not in vistos_tool:
+ vistos_tool.add(pid)
+ comando = ((p.get("state") or {}).get("input")
+ or {}).get("command")
+ if comando:
+ corto = comando.strip().replace("\n", " ")[:90]
+ try:
+ import requests as _rq
+ _rq.post(f"{API}/sendMessage",
+ data={"chat_id": chat, "text":
+ f"⚙️ {html.escape(corto)}",
+ "parse_mode": "HTML"},
+ timeout=10)
+ except Exception:
+ pass
+ # fin del turno: la sesion deja de estar busy (tras un margen inicial)
+ code2, sts = oc_get("/session/status", timeout=10)
+ transcurrido = time.time() - comienzo
+ if (code2 == 200 and isinstance(sts, dict)
+ and sts.get(sid, {}).get("type") != "busy"
+ and transcurrido >= 5):
+ terminado = True
+ break
+
+ stop.set()
+ if not terminado:
+ enviar(chat, "⏰ La tarea está tardando demasiado. Usa /salir "
+ "para cortarla y empezar de nuevo.")
+ return
+
+ # ficheros tocados en el turno
adjuntos = []
try:
code, hist = oc_get(f"/session/{sid}/message?limit=8")
@@ -401,14 +456,10 @@ def procesar_texto(chat, texto):
if os.path.isfile(r)]
except Exception as e:
log(f"adjuntos error: {e}")
- if rtexto:
- enviar(chat, formatear_html(rtexto))
if adjuntos:
enviar(chat, f"📎 {len(adjuntos)} fichero(s) del turno:")
for ruta in adjuntos[:6]:
enviar_doc(chat, ruta)
- if not rtexto and not adjuntos:
- enviar(chat, "🤔 (sin respuesta de texto. Revisa la sesión en el TUI)")
def cambiar_repo(chat, slug):