Talk to your organization's data. An agentic data-analytics assistant built on SvelteKit + Svelte 5 + Vercel AI SDK v7, with Claude served through Microsoft Azure AI Foundry, querying StarRocks under each user's own grants. Ships with an admin console for knowledge, access policies, usage statistics and auditing, an OpenAI-compatible API, and an optional sandboxed code-execution capability.
- Chat over the warehouse: the agent writes and runs SQL against StarRocks as the logged-in user and returns markdown tables, Chart.js charts, Mermaid diagrams, and downloadable Excel or text reports.
- Documents: attach files to a conversation, or curate shared organization and department document libraries (text, PDF, spreadsheets) the assistant searches and analyzes.
- Knowledge and memory: admin-curated organization and department knowledge injected into every relevant user's assistant, plus opt-in per-user memory the assistant maintains about you.
- Learned skills: after succeeding at a non-trivial task, the assistant captures the procedure as a reusable playbook (tables, filters, pitfalls, verification) and follows it next time. Skills are per-user; the assistant can propose one for department or organization use, activating only after an admin approves it in the console.
- Code execution (beta): sandboxed Python (pandas, numpy, statsmodels, scikit-learn) plus the
basaltCLI, each run inside a disposable hardware-isolated microVM. Enabled per deployment in the admin console. - Connectors: users plug their own MCP servers (URL plus token) into the assistant; every call runs with the user's own credentials.
- Programmatic access: an OpenAI-compatible
/v1endpoint and personal API keys with scopes, so any client that speaks the OpenAI protocol (openai SDKs, LangChain, Open WebUI, LiteLLM) can use the full agent. - Embedded chat: an anonymous, read-only
/embedpage for intranet portals — visitors talk to the warehouse through a dedicated StarRocks service account scoped to curated views, with a message cap per conversation, a daily budget, and a restricted model tier. No accounts, no history, no advanced tools. Admins mint per-portal embed keys (/embed?key=emb_…), each carrying its own service account (encrypted server-side, never exposed), limits, framing origins, and usage attribution — individually revocable. - Scheduled runs (Programado): standing instructions the assistant executes automatically on a cadence (daily/weekly/monthly) with nobody logged in — headless runs on the user's stored warehouse credential, charged to their daily token budget, with run history in the sidebar and optional e-mail delivery. Created from a conversation ("agende isso toda segunda às 8h") or in the UI.
- Governance: claim-based access policies, per-user exceptions, per-department usage attribution, and an audit log of sign-ins, requests, key and connector changes, and admin actions.
Browser (Svelte 5) SvelteKit server (adapter-node) External
+------------------+ Bearer JWE +--------------------------------+
| / (chat) | -----------> | /api/auth/login (LDAPS) | --> AD / OIDC provider
| /organization | | /auth/callback (OIDC + PKCE) |
| admin console | <- UI stream | /api/chat . ToolLoopAgent | --> Claude (Azure AI Foundry)
| @ai-sdk/svelte | | queryDatabase | --> StarRocks (as the user)
+------------------+ | runPython (capability, beta) | --> microsandbox microVMs (KVM)
| <user MCP servers> | --> any MCP endpoint
External clients | /v1/chat/completions (OpenAI- |
(openai SDK, LangChain, ...) ---> | compatible, pai_ API keys) |
+--------------------------------+
state: DATA_DIR (JSON files) telemetry: Sentry (prod)
- Agent loop:
ToolLoopAgent(src/lib/server/agent.ts) runs multi-step tool calling server-side (up to 24 steps, or until the user's daily token budget is spent), with Claude adaptive thinking. - Prompt caching: the tool schemas and system block sit behind a 1-hour cache breakpoint; tools are sorted deterministically so the prefix is byte-stable across turns. Token accounting is cost-weighted (cache reads 0.1x, writes 1.25x).
- Two sign-in doors, either or both: OIDC (authorization code + PKCE,
OIDC_ISSUER) and username/password over LDAPS (LDAP_URL). Both produce the same session and the same profile claims (groups, department, title, cost center); the canonical username is the lowercasedpreferred_username. - Token: the server issues an encrypted JWT (JWE, A256GCM) carrying the user's credentials for per-user tool auth and their directory profile. Nothing in it is readable client-side.
- Per-user warehouse:
queryDatabaseopens StarRocks connections as the logged-in user, so StarRocks enforces each user's own grants. Read-only by default; admins can grantsqlWrite(DROP/TRUNCATE/ALTER always blocked by the SQL guard). - Access policies: rules over sign-in claims (AD groups, cost center, any attribute) that grant role, extra models, SQL write and daily token limits. Evaluated per request from the token, so moving someone between AD groups changes their access on the next request; there is no user-profile store. Grants compose most-permissively; a per-user record is the exception mechanism (an explicit block or personal limit always wins).
- Roles:
user,builder,admin, resolved live on every request fromDATA_DIR/access.json.ADMIN_USERSnames bootstrap platform admins (the SERVIDOR badge) who can never be blocked or demoted. Open mode (anyone with valid credentials may sign in) lasts only while no user records and no policies exist.
The whole admin surface lives on one page, admin-only, with immediate-apply sections:
- Conhecimento: organization and department knowledge blocks, standing instructions (
MAX_ORG_PROMPTchars, default 8000), and shared document libraries, edited per scope in a master-detail layout. - Modelos PDF: Typst report templates with a live compiled preview — the template carries page setup and branding (
#include "content.typ"marks the body slot), the assistant writes only the content. - Habilidades: review queue for skills proposed for shared use (approve, suspend, remove), plus the active shared-skill list per scope.
- Usuarios: access policies (claim-rule editor with a "matches you" preview), plus one table of everyone the platform has seen: explicit records with full controls, policy-admitted users read-only with a "create exception" action.
- Estatisticas: token usage tiles (today, month, active users, cache rate, via-API split), a 30-day daily chart, per-department share (donut) and per-policy usage. Usage is tagged at request time with the departments and policies that matched, so attribution stays correct over time.
- Auditoria: every user's API keys (with admin revocation), the MCP connector fleet, and a filterable activity log (sign-ins, chat and API requests with the credential used, admin changes, key lifecycle, connector changes). Append-only JSONL under
DATA_DIR/audit/, 6-month retention. - Capacidades: deployment-wide feature switches. Currently: code execution (beta) with a sandbox test button, embedded chat (beta) with embed keys, report e-mails (beta) with SMTP status, and scheduled runs (beta).
The catalog is deployment-defined in src/lib/server/models.ts, served via GET /api/models. Two execution paths:
anthropic(Azure AI Foundry or direct API): the built-in Claude entries (claude-sonnet-5default, Opus 4.8, Fable 5, Opus 5). This path keeps the Claude-only optimizations: prompt-cache breakpoints, adaptive thinking, and Anthropic server tools (web_search) where the Foundry workspace provisions them.openai-compatible(MODELS_EXTRAenv, JSON): any/v1chat-completions endpoint (Ollama, OpenAI, Groq, vLLM, OpenRouter). Each entry declaresid,label,baseUrl, and optionallyprovider(picker logo),apiKey/apiKeyEnv,model, andreasoningTagfor models that emit<think>blocks.
Each user's allowed models come from their record plus matching policies (admins get all). The model is picked per conversation and kept stable for the pane so the model-scoped cache survives.
Warehouse: queryDatabase, listTables (catalog served from the synced schema, kept out of the prompt for caching), getTableSchema. Output: renderChart (Chart.js), renderDiagram (Mermaid), generateExcel (result sets written server-side), generateDocument, generatePdf (model-authored Typst compiled in-process; data via dataQuery or a staged workspace dataPath, source inline or from a workspace sourcePath so iterations are delta edits, compile diagnostics fed back for self-correction), emailReport (capability, beta: branded HTML e-mail through one tested template — the model fills subject/greeting/markdown body and attaches a generated export; recipients domain-allow-listed, sends audited). Documents: searchDocuments, previewTable. UX: askUser (client-resolved option buttons, chat only). Memory (opt-in): saveMemory, forgetMemory. Code execution (capability, beta): in chat, workspace tools (sandboxLoadData, sandboxWriteFile, sandboxReadFile, sandboxEditFile, sandboxExec, sandboxPresentFile); on the stateless /v1 API, the self-contained runPython. Plus whatever tools the user's own MCP servers expose, prefixed by server name.
POST /v1/chat/completionsandGET /v1/models: OpenAI-compatible, streaming and non-streaming, withresponse_formatsupport (json_objectandjson_schema) for structured output, and client-tool passthrough:toolsin the request are offered to the model and returned astool_calls(finish_reason: "tool_calls") for the caller to execute locally — coding harnesses like OpenCode run their bash/edit tools client-side while the server's warehouse/document/sandbox tools keep executing inside the loop. Point any OpenAI-protocol client at the app's base URL with apai_key and it gets the full agent (warehouse, documents, code execution) under the caller's own permissions, limits and audit trail. Stateless: callers keep their own history.- Warehouse access for keys: an API-key request carries no interactive credential, so the server keeps one per user — OIDC deployments mint a StarRocks id_token from the stored refresh token; password (LDAP) deployments store the directory credential encrypted at rest (AES-256-GCM under
JWT_SECRET) on each sign-in. Signing out deletes it, which revokes API-key warehouse access until the next sign-in. - API keys (Settings, Chaves de API): shown once, SHA-256 stored, optional expiry (90-day default), a non-secret hint (
pai_ab...1234) for matching keys to scripts, and a scope:chat(data plane only, the default) orfull(acts fully as the owner). A key is its owner: blocking the user disables their keys on the next request.
Runs inside microsandbox microVMs: hardware isolation (libkrun/KVM). Each chat conversation gets a persistent workspace VM whose files survive across turns (idle VMs are stopped after 20 minutes and resume in ~300ms; the workspace is removed with its conversation). The assistant loads warehouse data with sandboxLoadData (read-only SQL as the requesting user, up to 20k rows written server-side into a workspace file, so datasets never pass through the model), then writes, delta-edits, and executes scripts with the file tools. Stateless /v1 calls use runPython in a disposable VM instead. The sandbox image (deploy/sandbox/Dockerfile) preinstalls the Python analysis stack and the basalt binary for columnar SQL over files.
Requirements: the admin toggle in Capacidades, and /dev/kvm access for the app container (see docker-compose.yml), or the microsandbox cloud backend via MSB_API_KEY. The image is pulled and one VM is boot-tested in the background on server start and on toggle-on, so user runs get warm boots (about 300ms) instead of the one-time image pull. Configure with MSB_IMAGE, MSB_MEMORY_MIB, MSB_CPUS, MSB_TIMEOUT_MS. Optionally point BASALT_SYNTAX_PATH at a copy of basalt's language.md to give the agent the complete dialect reference instead of letting it guess the syntax.
- Audit log: first-party, in the console (Auditoria); see above.
- Sentry (
@sentry/sveltekitwith the Vercel AI SDK integration): per-agent traces, errors, MCP monitoring. DSN-gated and disabled in dev unlessSENTRY_DEV=1. PII capture is off; prompts carry warehouse rows and personal context that must not leave.
conversations/<user>/, settings/<user>.json (MCP tokens AES-encrypted at rest), rag/<user>.json plus rag/_shared/{org,dept-*}.json, memory/<user>.json, usage/<user>.json, apikeys/<user>.json (hashes only), audit/<month>.jsonl, access.json (users, policies, capabilities, knowledge), exports/ (7-day, behind authenticated /api/exports), and schema.json (synced warehouse catalog).
- Env: copy
.env.exampleto.envand fill in the Foundry (or Anthropic) key,JWT_SECRET, StarRocks, and at least one sign-in door (OIDC_*orLDAP_*). SetADMIN_USERSto your bootstrap admin(s). - Users: none to create. Anyone who authenticates can sign in while in open mode; add policies or user records to gate access. StarRocks grants govern what each user can read.
- Sync the schema (gives the agent instant warehouse knowledge):
Re-run whenever the schema changes; the app picks it up without a restart.
STARROCKS_SYNC_USER=<user> STARROCKS_SYNC_PASSWORD=<pass> npm run sync-schema
- Run:
npm install npm run dev
- Docker:
adapter-node;docker-compose.ymlusesenv_file: .env, listens on0.0.0.0:3000, and keeps state in theperguntai-datanamed volume. CI buildsghcr.io/<owner>/perguntaion app-code pushes to main (.github/workflows/docker.yml); on the serverdocker compose pull && docker compose up -d. - Sandbox image:
.github/workflows/sandbox-image.ymlbuildsghcr.io/<owner>/perguntai-sandboxwhendeploy/sandbox/changes, or on demand with a version tag. Pin that tag inMSB_IMAGE; make the package public so the microsandbox SDK pulls it without credentials. The compose file carries the/dev/kvmdevice and themsbcachevolume the feature needs. - Schema sync in production: an external cron POSTs
/api/admin/sync-schemawithAuthorization: Bearer $SCHEMA_SYNC_TOKEN, or rundocker compose exec perguntai node scripts/sync-schema.js. - Kubernetes (target): a starter Helm chart lives in
deploy/helm/perguntai/; it assumes migration off the local-filesystem store. See the chart'sNOTES.txt.
| Command | |
|---|---|
npm run dev |
dev server |
npm run build / npm run preview |
production build / preview |
npm run check |
svelte-check + TypeScript |
npm run sync-schema |
regenerate schema.json from StarRocks |
npm run lint / npm run format |
prettier |
AGPL-3.0. Run it, self-host it, modify it freely; if you offer a modified version to others over a network, you must make your modified source available under the same license.