Persistencia: cuando confirm_order encola create_order y la orden tiene
shipping/zona/window, runTurn snapshot a context.last_delivery
{is_delivery, shipping_address, matched_zone, pending_location, delivery_window}.
pipeline preserva ese campo cuando resetea por stale (>24h), igual que
external_customer_id.
Agente lo ve via working_memory.last_delivery en cada turno.
Nueva tool reuse_last_delivery() que copia shipping_address + matched_zone
(+ pending_location) al order actual. Pickup-only sólo setea is_delivery=false.
systemPrompt: instrucciones para que el bot proactivamente ofrezca "te lo
mandamos al mismo lugar que la última vez (dirección, zona, $)" cuando
last_delivery existe y todavía no se eligió método de envío. Cliente puede
aceptar (reuse_last_delivery) o pedir otra dirección/retiro. delivery_window
NO se asume — siempre se vuelve a preguntar día/hora.
Smoke E2E: cliente recurrente con conversación stale 25h+
- 1ra orden: 1kg vacío → location → mar 12h → confirma.
- DB: context.last_delivery con zona Centro Test + dirección + ventana.
- 2da orden: "hola, 500g bondiola" → bot: "¿al mismo lugar (Av. Corrientes
1234, Centro, $1.500)?" → "sí" → "¿qué día? La última fue mar 12h, puede
ser otro" → "jueves 11hs" → orden cerrada sin re-pedir pin.
Header: "Bot Ops Console" → "Piaf Console" (index.html + ops-shell).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
53 lines
1.8 KiB
JavaScript
53 lines
1.8 KiB
JavaScript
/**
|
|
* reuse_last_delivery — copia los datos de envío de la última orden del cliente
|
|
* (working_memory.last_delivery) al order actual: shipping_address, matched_zone,
|
|
* is_delivery (y opcionalmente pending_location + delivery_window).
|
|
*
|
|
* El agente lo usa cuando proactivamente ofrece "te lo mandamos al mismo lugar
|
|
* que la última vez?" y el cliente confirma. Si no hay last_delivery, devuelve
|
|
* error y el agente pide la dirección/ubicación de cero.
|
|
*/
|
|
|
|
export async function reuseLastDeliveryTool(_args, ctx) {
|
|
const last = ctx.last_delivery;
|
|
if (!last) {
|
|
return {
|
|
ok: false,
|
|
error: "no_last_delivery",
|
|
hint: "El cliente no tiene una entrega previa registrada. Pedile la ubicación de cero.",
|
|
};
|
|
}
|
|
|
|
// Pickup-only no necesita copiar nada de envío.
|
|
if (last.is_delivery === false) {
|
|
ctx.order = { ...ctx.order, is_delivery: false };
|
|
return { ok: true, is_delivery: false, hint: "El cliente la última vez retiró por el local." };
|
|
}
|
|
|
|
// Delivery: si no hay matched_zone tampoco podemos reusar (no se cerró bien).
|
|
if (!last.matched_zone) {
|
|
return {
|
|
ok: false,
|
|
error: "no_zone_in_last_delivery",
|
|
hint: "La última entrega no tenía zona registrada. Pedí la ubicación al cliente.",
|
|
};
|
|
}
|
|
|
|
ctx.order = {
|
|
...ctx.order,
|
|
is_delivery: true,
|
|
shipping_address: last.shipping_address || ctx.order.shipping_address || null,
|
|
matched_zone: last.matched_zone,
|
|
// Reusar también la ubicación si la tenemos (evita re-pedir el pin).
|
|
pending_location: last.pending_location || ctx.order.pending_location || null,
|
|
};
|
|
|
|
return {
|
|
ok: true,
|
|
is_delivery: true,
|
|
shipping_address: ctx.order.shipping_address,
|
|
matched_zone: last.matched_zone,
|
|
last_delivery_window: last.delivery_window || null,
|
|
};
|
|
}
|