1a15108b56
El Stop worker recibe ahora el DoD de la tarea y lo usa como criterio para distinguir hecho de pendiente_revision/en_pausa: marca 'hecho' unicamente si el resultado descrito cumple el DoD punto por punto y esta verificado. Si el DoD no se cumple del todo, cae en pendiente_revision (resultado para revisar) o en_pausa (avance parcial). Si no hay DoD definido, mantiene el criterio anterior sobre el objetivo. Hace el estado 'hecho' mucho mas preciso. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
123 lines
5.5 KiB
Bash
Executable File
123 lines
5.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Worker del Stop hook: resuelve el estado de REPOSO de la tarea cuando el
|
|
# asistente para y cede el control. Clasifica con ask_llm (haiku, API directa;
|
|
# nunca `claude -p`, ver regla llm_invocation.md) y lo escribe en el goal JSON
|
|
# manteniendo el historial.
|
|
#
|
|
# El estado ACTIVO (mientras se trabaja: investigando/haciendo/testeando) lo
|
|
# marca el hook PostToolUse (goal_phase_active.sh), de forma determinista. Este
|
|
# worker SOLO produce estados de reposo: hecho, pendiente_revision, bloqueado,
|
|
# en_pausa.
|
|
#
|
|
# Args: <session_id> <transcript_path> <goal_json>
|
|
|
|
SID="$1"
|
|
TRANSCRIPT="$2"
|
|
F="$3"
|
|
|
|
PY="$HOME/fn_registry/python/.venv/bin/python3"
|
|
ASK="$HOME/fn_registry/python/functions/core/ask_llm.py"
|
|
|
|
[ -x "$PY" ] || exit 0
|
|
[ -f "$ASK" ] || exit 0
|
|
[ -f "$F" ] || exit 0
|
|
[ -f "$TRANSCRIPT" ] || exit 0
|
|
|
|
GOAL=$(jq -r '.goal // ""' "$F" 2>/dev/null)
|
|
[ -z "$GOAL" ] && exit 0
|
|
CUR=$(jq -r '.phase // ""' "$F" 2>/dev/null)
|
|
DOD=$(jq -r '.dod // ""' "$F" 2>/dev/null)
|
|
[ -z "$DOD" ] && DOD="(no definido)"
|
|
|
|
is_active() {
|
|
case "$1" in
|
|
investigando|planificando|haciendo|testeando|puliendo) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# Una pasada de abajo a arriba sobre el turno actual: ultima respuesta de texto
|
|
# del asistente + ultima peticion del usuario + si hubo trabajo (tool_use).
|
|
LAST=""
|
|
USER_MSG=""
|
|
HAS_WORK=0
|
|
while IFS= read -r line; do
|
|
t=$(printf '%s' "$line" | jq -r '.type // empty' 2>/dev/null)
|
|
if [ "$t" = "assistant" ]; then
|
|
if [ -z "$LAST" ]; then
|
|
txt=$(printf '%s' "$line" | jq -r '(.message.content // [])[]? | select(.type=="text") | .text' 2>/dev/null)
|
|
[ -n "$txt" ] && LAST="$txt"
|
|
fi
|
|
if printf '%s' "$line" | jq -e '(.message.content // [])[]? | select(.type=="tool_use")' >/dev/null 2>&1; then
|
|
HAS_WORK=1
|
|
fi
|
|
elif [ "$t" = "user" ]; then
|
|
ctype=$(printf '%s' "$line" | jq -r '.message.content | type' 2>/dev/null)
|
|
if [ "$ctype" = "string" ]; then
|
|
USER_MSG=$(printf '%s' "$line" | jq -r '.message.content' 2>/dev/null)
|
|
break
|
|
fi
|
|
if ! printf '%s' "$line" | jq -e '(.message.content // [])[]? | select(.type=="tool_result")' >/dev/null 2>&1; then
|
|
USER_MSG=$(printf '%s' "$line" | jq -r '(.message.content // [])[]? | select(.type=="text") | .text' 2>/dev/null)
|
|
break
|
|
fi
|
|
fi
|
|
done < <(tac "$TRANSCRIPT")
|
|
|
|
# Solo resolver reposo si hubo trabajo este turno o si veniamos de un estado
|
|
# activo (paramos tras currar). Charla sobre un reposo previo: no tocar.
|
|
if [ "$HAS_WORK" = "0" ] && ! is_active "$CUR"; then
|
|
exit 0
|
|
fi
|
|
|
|
LAST=$(printf '%s' "$LAST" | tail -c 4000)
|
|
[ -z "$LAST" ] && exit 0
|
|
USER_MSG=$(printf '%s' "$USER_MSG" | tail -c 1500)
|
|
|
|
SYS="El asistente acaba de PARAR y cede el control al usuario. Clasifica el estado de REPOSO en que queda la tarea. Responde UNA sola palabra, sin nada mas, de: hecho pendiente_revision preguntando bloqueado en_pausa sin_cambio. hecho=el objetivo esta completo y verificado; pendiente_revision=el asistente termino un trabajo y espera que el humano lo revise o apruebe (no hace una pregunta directa); preguntando=el asistente termina formulando una o varias PREGUNTAS concretas al usuario y necesita su respuesta o decision para continuar; bloqueado=no puede avanzar por un error o por falta de informacion/acceso; en_pausa=hizo un avance y espera la siguiente indicacion, sin estar terminado ni preguntar ni bloqueado; sin_cambio=el turno no altera el estado de reposo actual (charla irrelevante). Distingue: si la respuesta acaba con preguntas al usuario es 'preguntando'; si deja un resultado para que lo mire es 'pendiente_revision'. REGLA DEL DoD: se te da el DoD (definition of done) que define cuando la tarea esta TERMINADA. Marca 'hecho' UNICAMENTE si el resultado descrito por el asistente CUMPLE ese DoD de forma clara y verificada; compara punto por punto el resultado contra el DoD. Si el DoD no se cumple del todo, o no esta verificado, NO uses 'hecho': usa 'pendiente_revision' (dejas un resultado para que el humano lo revise) o 'en_pausa' (avance parcial). Si el DoD es '(no definido)', usa tu criterio: 'hecho' solo si el objetivo esta claramente completo y verificado."
|
|
|
|
PROMPT="OBJETIVO DE LA TAREA: ${GOAL}
|
|
|
|
DEFINITION OF DONE (DoD) — la tarea esta TERMINADA solo si esto se cumple:
|
|
${DOD}
|
|
|
|
ULTIMA PETICION DEL USUARIO:
|
|
${USER_MSG}
|
|
|
|
ULTIMA RESPUESTA DEL ASISTENTE:
|
|
${LAST}
|
|
|
|
Compara el resultado contra el DoD y responde con una sola palabra de la lista permitida:"
|
|
|
|
RAW=$("$PY" "$ASK" --model claude-haiku-4-5-20251001 --system "$SYS" "$PROMPT" 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
|
[ -z "$RAW" ] && exit 0
|
|
|
|
case "$RAW" in
|
|
*sin_cambio*|*sincambio*|*ninguna*|*charla*) exit 0 ;;
|
|
*pregunt*|*consulta*|*respuesta*) PHASE=preguntando ;;
|
|
*pendiente*revis*|*revis*|*aprob*) PHASE=pendiente_revision ;;
|
|
*bloque*) PHASE=bloqueado ;;
|
|
*hecho*|*complet*|*termin*|*done*) PHASE=hecho ;;
|
|
*pausa*|*pause*|*siguiente*) PHASE=en_pausa ;;
|
|
# Si por error devuelve un estado activo al parar, lo tratamos como pausa.
|
|
investigando|planificando|haciendo|testeando|puliendo) PHASE=en_pausa ;;
|
|
*) exit 0 ;;
|
|
esac
|
|
|
|
# Escribir la fase + mantener historial (append solo si cambia respecto al
|
|
# ultimo; se conservan los ultimos 12 estados).
|
|
TMP="${F}.tmp.$$"
|
|
if jq --arg p "$PHASE" '
|
|
.phase = $p
|
|
| .history = (
|
|
( .history // [] ) as $h
|
|
| ( if ($h | length) > 0 and ($h[-1] == $p) then $h else ($h + [$p]) end )
|
|
| .[-12:]
|
|
)
|
|
' "$F" > "$TMP" 2>/dev/null; then
|
|
mv "$TMP" "$F"
|
|
else
|
|
rm -f "$TMP"
|
|
fi
|
|
exit 0
|