Skip to content

fix(agent): no persistir ni reenviar turnos assistant vacíos - #73

Draft
santmun wants to merge 1 commit into
mainfrom
cursor/empty-assistant-poison-e5d2
Draft

santmun wants to merge 1 commit into
mainfrom
cursor/empty-assistant-poison-e5d2

Conversation

@santmun

@santmun santmun commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Qué cambia

Un turno solo-tool (sin texto) ya no deja una fila assistant vacía en D1, y el historial que se manda al LLM omite filas en blanco. Así no se envenena la conversación y las que ya estaban envenenadas se recuperan solas.

Por qué

Ticket de soporte #2C233C (Eduardo Cruz, bot v1.0.74).

Cuando el modelo cierra un turno solo con una tool (legítimo: pauseBot / pauseSuspectedBot piden no mandar mensaje), SupportAgent.processBuffer igual hacía msgs.append(convId, "assistant", ""). lastN(convId, 20) reinyectaba esa fila en cada turno. Anthropic responde messages: text content blocks must be non-emptyAI_NoOutputGeneratedError → el cliente ve "Algo falló de mi lado…" para siempre, hasta borrar la fila a mano.

Causa verificada en código (no solo hipótesis):

  • Persistencia incondicional en src/agent.ts tras runLlmTurn.
  • Historial armado con lastN sin filtrar content vacío.
  • chunkReply("") produce [""], que Telegram rechaza con 400.

Dos capas, porque son dos problemas:

  1. No persistir ni enviar un turno assistant sin texto. El turno solo-tool es válido; guardarlo como mensaje es lo que envenena.
  2. usableHistory omite filas vacías/whitespace al armar el historial (agente + co-pilot /suggest). Cura hilos ya envenenados sin cirugía en D1.

Fuera de alcance (nota del reporter): pauseSuspectedBot puede silenciar 24h a un cliente real sin aviso. No se tocó.

Cómo lo probaste

  • pnpm test pasa — 79 files, 567 tests
  • pnpm typecheck limpio
  • (si aplica) probado contra un bot real

Tests nuevos:

  • usableHistory / hasMessageText dropean "" y whitespace.
  • processBuffer con texto vacío + tool: no hace append("assistant", …) ni sendReply.
  • Historial con filas vacías: streamText no recibe bloques vacíos.
  • Co-pilot /suggest también omite filas vacías.

Log del turno solo-tool (el engine ahora sale sin persistir ni mandar):
[SupportAgent.processBuffer] tool-only turn, no reply sent, model=claude-haiku-4-5-20251001

Checklist

  • No toqué la carpeta member/ (config de cada quien)
  • El PR es de un solo tema (enfocado y chico)
  • Si tu agente (Claude) hizo el PR, revisaste el diff tú mismo antes de abrirlo
  • No hay secrets ni API keys en el código
Open in Web Open in Cursor 

Un turno solo-tool (p. ej. pauseBot) dejaba role=assistant content="" en D1.
Anthropic rechaza bloques de texto vacíos y lastN reinyectaba la fila, así
que cada turno siguiente fallaba para siempre. Ahora no se persiste ni se
envía un reply vacío, y usableHistory omite filas en blanco al armar el
historial para que hilos ya envenenados se recuperen solos.

Co-authored-by: santmun <santmun@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants