Skip to main content
Internal documentation for developers. This page covers the Personal Assistant API internals: auth patterns, streaming SSE implementation, rate limiting, routine scheduling, and the runtime architecture. Not intended for end users.

Route Map


Plan Gating

Every PA route starts with plan validation:

Auth Pattern

PA is user-bound at the conversation level, but org-bound at the capabilities level. User ID comes from req.session.user.id.

Runtime Architecture

Language, intent, and verified knowledge pipeline

LanguageAndLocaleResolver resolves the response language separately from the interface locale and persists durable conversation language metadata with a compare-and-swap update. A short ambiguous message never overwrites the stored language. Human-language intent routing is model-classified into canonical intent IDs. Only exact internal command IDs bypass that classifier. Invalid or low-confidence classifier output fails closed to GENERAL_ANSWER. The CANONICAL_CONTEXT_PIPELINE_V1 flow preserves the original route, query, hash, active tabs, and safe transient states. It then resolves VERIFIED_TEACHING_V1 or INTEGRATION_GUIDANCE_V1, adds provenance nodes to the ContextGraph, and sends that verified context to the answer model. Build-time coverage checks validate all 446 inventory surfaces; the generated catalog is not imported into the request path. Do not restore translated keyword arrays or substring matching for intents. Do not expose an external navigation target unless its exact destination has a verified source. Integration guidance is descriptive or links to verified configuration only; it is never promoted to an executable action.

Streaming Implementation

The chat endpoint uses ReadableStream with SSE format:

Routine Engine

Routines are stored in DB with a cron schedule. A background job (/api/crons/personal-assistant-routines or similar) triggers them on schedule.

Rate Limiting

The chat endpoint enforces rate limits from @zappway/lib/rate-limit:
Rate limit headers are included in ALL responses (streaming + non-streaming):
  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

i18n Context Injection

The chat route injects request context for i18n:
Critical: Always call cleanupReqCtx() in the finally block. Missing this causes locale context to leak between concurrent requests.

Privacy — Data Deletion


Known Issues / Gotchas

  • sourceIdentity is stripped from the input before passing to runtime.chat() — the dashboard always uses the authenticated user’s identity, never a client-supplied actor label.
  • Streaming abort handling: Both client abort (client_disconnected) and timeout (personal_assistant_stream_timeout) trigger abortController.abort(). The error code is preserved in the SSE error event for client-side differentiation.
  • State endpoint (GET /state) uses PersonalAssistantStateQuerySchema validation + i18n injection — same pattern as chat.
  • Conversation isolation: PA conversations are scoped to userId (not just organizationId). Users cannot see each other’s PA conversations even within the same org.