> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zappway.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API de asistente personal

> Referencia interna para la API del Asistente personal: arquitectura de rutas, control de planes, limitación de velocidad, implementación de transmisión, motor de rutinas y gestión de sesiones.

> **Documentación interna para desarrolladores.** Esta página cubre los aspectos internos de la API del Asistente personal: patrones de autenticación, implementación de transmisión SSE, limitación de velocidad, programación de rutinas y arquitectura de tiempo de ejecución. No está destinado a usuarios finales.

***

## Mapa de ruta

| Método                  | Punto final                                                      | Descripción                             |
| ----------------------- | ---------------------------------------------------------------- | --------------------------------------- |
| `POST`                  | `/api/personal-assistant/chat`                                   | Chat en streaming/no streaming          |
| `GET`                   | `/api/personal-assistant/state`                                  | Estado asistente actual                 |
| `GET`                   | `/api/personal-assistant/capabilities`                           | Lista de capacidades basada en planes   |
| `PATCH`                 | `/api/personal-assistant/settings`                               | Actualizar configuración                |
| `GET`                   | `/api/personal-assistant/usage`                                  | Estadísticas de uso                     |
| `POST`                  | `/api/personal-assistant/feedback`                               | Responder comentarios                   |
| `POST`                  | `/api/personal-assistant/context-help`                           | Ayuda contextual                        |
| `GET/POST/PATCH/DELETE` | `/api/personal-assistant/conversations/...`                      | Conversación CRUD                       |
| `GET/POST/PATCH/DELETE` | `/api/personal-assistant/routines/...`                           | Ejecución CRUD + de rutina              |
| `POST`                  | `/api/personal-assistant/routines/[id]/toggle`                   | Activar/desactivar rutina               |
| `GET`                   | `/api/personal-assistant/routines/[id]/executions`               | Historial de ejecución                  |
| `POST`                  | `/api/personal-assistant/routines/run`                           | Gatillo manual                          |
| `GET`                   | `/api/personal-assistant/approvals`                              | Aprobación de acciones pendientes       |
| `POST`                  | `/api/personal-assistant/actions/[id]/approve`                   | Aprobar acción                          |
| `POST`                  | `/api/personal-assistant/actions/[id]/reject`                    | Rechazar acción                         |
| `GET/PATCH`             | `/api/personal-assistant/opportunities/...`                      | Oportunidades                           |
| `GET/DELETE`            | `/api/personal-assistant/privacy`                                | Configuración de privacidad/eliminación |
| `GET/PATCH`             | `/api/personal-assistant/privacy/retention`                      | Política de retención                   |
| `GET`                   | `/api/personal-assistant/audit`                                  | Registro de auditoría                   |
| `GET`                   | `/api/personal-assistant/audit/export`                           | Exportar registro de auditoría          |
| `GET`                   | `/api/personal-assistant/integrations/[integration]/diagnostics` | Diagnóstico de integración              |

***

## Planificar puerta

Cada ruta PA comienza con la validación del plan:

```ts theme={null}
import { assertPersonalAssistantPlan } from '@zappway/lib/personal-assistant';

// Inside handler:
assertPersonalAssistantPlan(req.session);
// Throws 403 if plan doesn't include personal_assistant feature
```

***

## Patrón de autenticación

```ts theme={null}
withPermissionRoute(req, {
  permission: 'personal_assistant.read',    // all GETs
  permission: 'personal_assistant.execute', // chat, conversations
  permission: 'personal_assistant.manage',  // settings, routines
  permission: 'personal_assistant.audit',   // audit log
  authMode: 'lightweight',                  // for reads
}, handler)
```

La PA está **vinculada al usuario** en el nivel de conversación, pero **vinculada a la organización** en el nivel de capacidades. El ID de usuario proviene de `req.session.user.id`.

***

## Arquitectura de tiempo de ejecución

```ts theme={null}
import { PersonalAssistantRuntime } from '@zappway/lib/personal-assistant';

const runtime = new PersonalAssistantRuntime({ db: prisma });

// Get capabilities
const capabilities = runtime.capabilities(req.session);

// Chat (non-streaming)
const result = await runtime.chat({
  session: effectiveSession,
  input: { message, conversationId },
  signal: request.signal,
});

// Chat (streaming)
const result = await runtime.chat({
  session: effectiveSession,
  input: { message, conversationId },
  signal: abortController.signal,
  onDelta: (delta) => enqueue('chunk', { delta }),
});
```

***

## Implementación de transmisión

El punto final del chat utiliza `ReadableStream` con formato SSE:

```ts theme={null}
const stream = new ReadableStream({
  async start(controller) {
    const encoder = new TextEncoder();
    
    const enqueue = (event: string, data: unknown) => {
      controller.enqueue(encoder.encode(
        `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`
      ));
    };

    // Heartbeat every 15s
    const heartbeat = setInterval(() => {
      controller.enqueue(encoder.encode(': heartbeat\n\n'));
    }, 15_000);

    // Timeout at 90s
    const timeout = setTimeout(
      () => abortController.abort('personal_assistant_stream_timeout'),
      90_000
    );

    try {
      const result = await runtime.chat({ ..., onDelta: (delta) => enqueue('chunk', { delta }) });
      enqueue('done', result);
    } catch (error) {
      enqueue('error', { code: 'CHAT_FAILED', retryable: true });
    } finally {
      clearTimeout(timeout);
      clearInterval(heartbeat);
    }
  }
});

return new Response(stream, {
  headers: {
    'Content-Type': 'text/event-stream; charset=utf-8',
    'Cache-Control': 'private, no-store, no-transform',
    Connection: 'keep-alive',
  },
});
```

***

## Motor de rutina

```ts theme={null}
import { PersonalAssistantRoutineEngine } from '@zappway/lib/personal-assistant';

const engine = new PersonalAssistantRoutineEngine(prisma);

// List routines
await engine.listRoutines(userId, organizationId, locale);

// Create/update routine (schema: PersonalAssistantRoutineCreateSchema)
await engine.updateRoutine({ session, routineId, input });
```

Las rutinas se almacenan en la base de datos con una programación cron. Un trabajo en segundo plano (`/api/crons/personal-assistant-routines` o similar) los activa según lo programado.

***

## Limitación de velocidad

El punto final del chat aplica límites de velocidad desde `@zappway/lib/rate-limit`:

```ts theme={null}
const { limited, headers: rateLimitHeaders } = await checkRateLimit({
  session: req.session,
  feature: 'personal_assistant_chat',
});
if (limited) return 429;
```

Los encabezados de límite de velocidad se incluyen en TODAS las respuestas (transmisión + no transmisión):

* `X-RateLimit-Limit`
* `X-RateLimit-Remaining`
* `X-RateLimit-Reset`

***

## Inyección de contexto i18n

La ruta de chat inyecta contexto de solicitud para i18n:

```ts theme={null}
import { detectLocale, injectRequestContext } from '@zappway/lib/i18n-server';
const locale = detectLocale(request, req.session);
const cleanupReqCtx = injectRequestContext({ locale });
try {
  // ... handle chat
} finally {
  cleanupReqCtx?.(); // CRITICAL: cleanup to avoid memory leaks between requests
}
```

> **Crítico:** Llame siempre a `cleanupReqCtx()` en el bloque `finally`. Omitir esto hace que el contexto local se filtre entre solicitudes simultáneas.

***

## Privacidad: eliminación de datos

```ts theme={null}
// DELETE /api/personal-assistant/privacy
// Deletes ALL user-specific PA data:
// - Conversations + messages
// - Routines + execution history
// - Opportunities
// - Audit log entries
// - Action approvals
// Uses prisma.$transaction for atomicity
```

***

## Problemas conocidos / Problemas

* **`sourceIdentity`** se elimina de la entrada antes de pasar a `runtime.chat()`: el panel siempre usa la identidad del usuario autenticado, nunca una etiqueta de actor proporcionada por el cliente.
* **Manejo de abortos de transmisión:** Tanto el aborto del cliente (`client_disconnected`) como el tiempo de espera (`personal_assistant_stream_timeout`) activan `abortController.abort()`. El código de error se conserva en el evento SSE `error` para la diferenciación del lado del cliente.
* **El punto final estatal** (`GET /state`) usa validación `PersonalAssistantStateQuerySchema` + inyección i18n: el mismo patrón que el chat.
* **Aislamiento de conversación:** Las conversaciones PA tienen como alcance `userId` (no solo `organizationId`). Los usuarios no pueden ver las conversaciones PA de los demás, incluso dentro de la misma organización.
