> ## 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 assistente pessoal

> Referência interna para a API do Personal Assistant — arquitetura de rotas, controle de plano, limitação de taxa, implementação de streaming, mecanismo de rotina e gerenciamento de sessão.

> **Documentação interna para desenvolvedores.** Esta página aborda os aspectos internos da API do Personal Assistant: padrões de autenticação, implementação de SSE de streaming, limitação de taxa, agendamento de rotina e arquitetura de tempo de execução. Não se destina a usuários finais.

***

## Mapa de rotas

| Método                  | Ponto final                                                      | Descrição                             |
| ----------------------- | ---------------------------------------------------------------- | ------------------------------------- |
| `POST`                  | `/api/personal-assistant/chat`                                   | Bate-papo com streaming/não streaming |
| `GET`                   | `/api/personal-assistant/state`                                  | Estado atual do assistente            |
| `GET`                   | `/api/personal-assistant/capabilities`                           | Lista de capacidade baseada em plano  |
| `PATCH`                 | `/api/personal-assistant/settings`                               | Atualizar configurações               |
| `GET`                   | `/api/personal-assistant/usage`                                  | Estatísticas de utilização            |
| `POST`                  | `/api/personal-assistant/feedback`                               | Responder aos comentários             |
| `POST`                  | `/api/personal-assistant/context-help`                           | Ajuda contextual                      |
| `GET/POST/PATCH/DELETE` | `/api/personal-assistant/conversations/...`                      | Conversa CRUD                         |
| `GET/POST/PATCH/DELETE` | `/api/personal-assistant/routines/...`                           | Rotina CRUD + execução                |
| `POST`                  | `/api/personal-assistant/routines/[id]/toggle`                   | Ativar/desativar rotina               |
| `GET`                   | `/api/personal-assistant/routines/[id]/executions`               | Histórico de execução                 |
| `POST`                  | `/api/personal-assistant/routines/run`                           | Gatilho manual                        |
| `GET`                   | `/api/personal-assistant/approvals`                              | Aprovações de ações pendentes         |
| `POST`                  | `/api/personal-assistant/actions/[id]/approve`                   | Aprovar ação                          |
| `POST`                  | `/api/personal-assistant/actions/[id]/reject`                    | Rejeitar ação                         |
| `GET/PATCH`             | `/api/personal-assistant/opportunities/...`                      | Oportunidades                         |
| `GET/DELETE`            | `/api/personal-assistant/privacy`                                | Configurações/exclusão de privacidade |
| `GET/PATCH`             | `/api/personal-assistant/privacy/retention`                      | Política de retenção                  |
| `GET`                   | `/api/personal-assistant/audit`                                  | Registro de auditoria                 |
| `GET`                   | `/api/personal-assistant/audit/export`                           | Exportar registro de auditoria        |
| `GET`                   | `/api/personal-assistant/integrations/[integration]/diagnostics` | Diagnóstico de integração             |

***

## Planejar portão

Cada rota PA começa com a validação do plano:

```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
```

***

## Padrão de autenticação

```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)
```

O PA é **vinculado ao usuário** no nível de conversação, mas **vinculado à organização** no nível de capacidades. O ID do usuário vem de `req.session.user.id`.

***

## Arquitetura de tempo de execução

```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 }),
});
```

***

## Implementação de streaming

O endpoint do chat usa `ReadableStream` com 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',
  },
});
```

***

## Mecanismo de Rotina

```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 });
```

As rotinas são armazenadas no banco de dados com uma programação cron. Um trabalho em segundo plano (`/api/crons/personal-assistant-routines` ou similar) os aciona dentro do cronograma.

***

## Limitação de taxa

O endpoint do chat impõe limites de taxa de `@zappway/lib/rate-limit`:

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

Os cabeçalhos de limite de taxa estão incluídos em TODAS as respostas (streaming + não streaming):

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

***

## Injeção de contexto i18n

A rota de chat injeta contexto de solicitação 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:** Sempre chame `cleanupReqCtx()` no bloco `finally`. A falta disso faz com que o contexto de localidade vaze entre solicitações simultâneas.

***

## Privacidade — Exclusão de dados

```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 conhecidos/pegadinhas

* **`sourceIdentity`** é removido da entrada antes de passar para `runtime.chat()` — o painel sempre usa a identidade do usuário autenticado, nunca um rótulo de ator fornecido pelo cliente.
* **Tratamento de interrupção de streaming:** A interrupção do cliente (`client_disconnected`) e o tempo limite (`personal_assistant_stream_timeout`) acionam `abortController.abort()`. O código de erro é preservado no evento SSE `error` para diferenciação do lado do cliente.
* **Endpoint de estado** (`GET /state`) usa validação `PersonalAssistantStateQuerySchema` + injeção i18n — mesmo padrão do chat.
* **Isolamento de conversa:** as conversas PA têm como escopo `userId` (não apenas `organizationId`). Os usuários não podem ver as conversas de PA uns dos outros, mesmo dentro da mesma organização.
