Conversaciones y streaming
Conversaciones
const conversation = await client.createConversation({
title: 'Soporte de checkout',
idempotencyKey: 'checkout-support-001',
});
const { conversations } = await client.listConversations();
const { messages, outputModules } = await client.getConversationMessages(
conversation.conversationRef,
);
Pasa un idempotencyKey a createConversation() si la llamada puede
reintentarse: un reintento con la misma clave devuelve la conversación existente
en lugar de crear un duplicado.
Las conversaciones se direccionan siempre por conversationRef. Ver
ExternalAgentConversation.
Transmitir un turno
const stream = await client.streamMessage({
conversationRef: conversation.conversationRef,
content: '¿Por qué se rechazó mi tarjeta?',
});
for await (const event of stream) {
switch (event.type) {
case 'message:delta':
appendToBubble(event.delta);
break;
case 'message:complete':
finalize(event.content, event.usage);
break;
case 'error':
showError(event.message, event.retryable);
break;
}
}
streamMessage() devuelve un RuntimeEventStream, un AsyncIterable de eventos
públicos normalizados. Además de content y conversationRef, admite
streamProfile, outputModuleTypes, providerGroundingDisplay, hostContext,
fileRefs, idempotencyKey y signal. Tipo completo:
StreamMessageInput.
Cancelar
const controller = new AbortController();
const stream = await client.streamMessage({
conversationRef,
content,
signal: controller.signal,
});
stopButton.onclick = () => controller.abort();
Un stream cancelado se expone como un RuntimeSdkError con código aborted; una
conexión que se corta a mitad de turno se expone como stream_interrupted.
Tipos de evento
RuntimeStreamEvent es una unión discriminada por type. Discrimínala de forma
exhaustiva, y mantén una rama default que ignore lo que no reconozca: los tipos
de evento nuevos son aditivos.
| Evento | Contiene |
|---|---|
message:start | messageRef, role |
message:delta | delta — concaténalo, no lo reemplaces |
message:complete | content, usage, references, files, artifacts, outputModules |
tool:start | name, displayName, inputPreview |
tool:result | output, references |
tool:error | code, message |
interaction:requested | interaction — el agente espera al usuario |
interaction:resolved | interactionRef, response |
approval:requested | approvalRef, title, description |
approval:resolved | approvalRef, decision |
artifact:created | artifact |
runtime:status | status — proyección de progreso |
error | code, message, retryable |
Recolectar en lugar de transmitir
Cuando no necesitas renderizado token a token, reduce el stream a un mensaje:
import { collectAssistantMessage } from '@agentshelf/external-agents-sdk';
const stream = await client.streamMessage({ conversationRef, content });
const collected = await collectAssistantMessage(stream);
console.log(collected.message.content, collected.usage);
collectAssistantMessage devuelve el message ensamblado más los usage,
references, files y artifacts que haya producido el turno. Sigue
consumiendo todo el stream, así que la cancelación con signal funciona igual.
Interacciones
Cuando el agente necesita datos a mitad de turno emite interaction:requested y
espera. Muestra la interacción y después resuélvela:
for await (const event of stream) {
if (event.type === 'interaction:requested') {
const answer = await promptUser(event.interaction);
await client.respondToInteraction({
conversationRef,
interactionRef: event.interaction.interactionRef,
response: answer,
idempotencyKey: `interaction-${event.interaction.interactionRef}`,
});
}
}
response es un objeto JSON con datos escalares públicos. La resolución se
confirma con un evento interaction:resolved posterior.
Las interacciones requieren capabilities.interactions; las aprobaciones
requieren capabilities.approvals. Comprueba la política antes de mostrar
cualquiera de las dos — ver
Políticas y capacidades.
Adjuntos
streamMessage() acepta fileRefs, que referencian archivos que ya has subido
con uploadFile(). Todavía no existe un contrato
tipado de adjuntos por turno más allá de pasar esas refs públicas.