Skip to main content
Cuando un agente necesita confirmacion del usuario antes de ejecutar una accion sensible, emite un evento de accion HITL (Human-in-the-Loop). El SDK te da control completo para manejar estas acciones programaticamente.

Tipos de accion

TipoDescripcionEjemplo
consentCheckbox + Aceptar/Rechazar”Acepto los terminos para procesar mi solicitud”
confirmSi / No”¿Confirmas que quieres agendar la cita?”
selectElegir entre opciones”¿Que tipo de consulta necesitas?”
formFormulario con campos”Completa tus datos para continuar”

Flujo basico

  1. Envias un mensaje via stream()
  2. El agente detecta que necesita confirmacion
  3. El stream emite un evento action y se pausa
  4. Tu app muestra la accion al usuario
  5. El usuario responde
  6. Envias la respuesta con respondToAction() para reanudar
import { Thaliq, ActionResponseBuilder } from '@thaliq/sdk';

const thaliq = new Thaliq({ apiKey: 'tq_live_xxx' });

// 1. Enviar mensaje
const stream = thaliq.agent.stream('Transfiere $500 a Juan');

// 2. Consumir eventos
for await (const event of stream) {
  if (event.type === 'content.delta') {
    process.stdout.write(event.delta);
  }
}

// 3. Verificar si hay accion pendiente
if (stream.pendingAction) {
  console.log(stream.status);                    // 'awaiting_action'
  console.log(stream.pendingAction.type);        // 'confirm'
  console.log(stream.pendingAction.message);     // '¿Confirmas la transferencia?'

  // 4. Responder a la accion
  const resumed = thaliq.agent.respondToAction({
    conversationId: stream.conversationId!,
    originalMessage: 'Transfiere $500 a Juan',
    actionResponse: ActionResponseBuilder.accept(stream.pendingAction.instructionId),
  });

  // 5. Consumir la respuesta reanudada
  const response = await resumed.finalResponse();
  console.log(response.message);
}

ActionResponseBuilder

Helpers para construir respuestas a cada tipo de accion:
import { ActionResponseBuilder } from '@thaliq/sdk';

// Consent / Confirm — aceptar
ActionResponseBuilder.accept(instructionId);

// Consent / Confirm — rechazar
ActionResponseBuilder.reject(instructionId);

// Select — elegir una opcion
ActionResponseBuilder.select(instructionId, 'wire');

// Form — enviar valores
ActionResponseBuilder.submitForm(instructionId, {
  amount: '500',
  currency: 'USD',
  recipient: 'Juan',
});

Patron completo (todos los tipos)

const stream = thaliq.agent.stream('Configura la transferencia');
for await (const event of stream) {
  if (event.type === 'content.delta') process.stdout.write(event.delta);
}

if (stream.pendingAction) {
  const action = stream.pendingAction;
  let actionResponse;

  switch (action.type) {
    case 'consent':
    case 'confirm':
      const accepted = await askUser(action.message);
      actionResponse = accepted
        ? ActionResponseBuilder.accept(action.instructionId)
        : ActionResponseBuilder.reject(action.instructionId);
      break;

    case 'select':
      // action.options = [{ label: 'Wire', value: 'wire' }, ...]
      const selected = await showOptions(action.options!);
      actionResponse = ActionResponseBuilder.select(action.instructionId, selected);
      break;

    case 'form':
      // action.fields = [{ name: 'amount', label: 'Monto', type: 'number' }, ...]
      const values = await showForm(action.fields!);
      actionResponse = ActionResponseBuilder.submitForm(action.instructionId, values);
      break;
  }

  const resumed = thaliq.agent.respondToAction({
    conversationId: stream.conversationId!,
    originalMessage: 'Configura la transferencia',
    actionResponse,
  });

  // Usa finalResponse() para obtener texto + metadata en una sola lectura
  const response = await resumed.finalResponse();
  console.log(response.message);
  console.log(response.insights);
}
Consumo unico del stream. El stream retornado por respondToAction() sigue la misma regla: solo puede ser consumido una vez. Usa finalResponse() para obtener tanto el texto como la metadata en una sola lectura.
// ❌ ERROR
const text = await resumed.text();
const meta = await resumed.finalResponse(); // StreamError!

// ✅ CORRECTO
const response = await resumed.finalResponse();
const text = response.message;
const meta = response.metadata;

Que pasa al rechazar

El comportamiento al rechazar depende de la configuracion de la instruccion:
onRejectComportamiento
stopEl agente se detiene y muestra un mensaje de rechazo
continueEl agente continua sin ejecutar la tool
fallbackEl agente ejecuta una accion alternativa definida en la instruccion
Las acciones HITL se configuran desde la plataforma en la seccion Instrucciones. Cada instruccion puede tener una accion asociada a una tool especifica. Ver Instrucciones para mas detalles.

respondToAction

Firma completa del metodo:
thaliq.agent.respondToAction(options: RespondToActionOptions): AgentStream
ParametroTipoRequeridoDescripcion
conversationIdstringSiID de la conversacion
originalMessagestringSiMensaje original que disparo la accion
actionResponseActionResponseSiRespuesta construida con ActionResponseBuilder
onEvent(event) => voidNoCallback por evento
signalAbortSignalNoSignal para cancelar
Retorna un AgentStream que puedes consumir con for await, text(), o finalResponse().

PendingAction

El objeto pendingAction contiene toda la informacion necesaria para renderizar la UI:
interface PendingAction {
  type: 'consent' | 'confirm' | 'select' | 'form';
  instructionId: string;     // ID de la instruccion (necesario para responder)
  message: string;           // Mensaje a mostrar al usuario
  options?: ActionOption[];  // Opciones (solo para 'select')
  fields?: ActionField[];    // Campos (solo para 'form')
  onReject?: 'stop' | 'continue' | 'fallback';
}

interface ActionOption {
  label: string;
  value: string;
}

interface ActionField {
  name: string;
  label: string;
  type: 'text' | 'number' | 'email' | 'textarea' | 'select';
  required?: boolean;
  placeholder?: string;
  options?: ActionOption[];  // Solo para fields de tipo 'select'
}