68106d7a5a
Read-only natural-language query over the curated nl_query endpoint, answered in-thread. Two entry points (room-per-purpose model): a dedicated Q&A room (MATRIX_QUERY_ROOM) where every top-level message is a question, plus the ?/@bot trigger in the intake room as a cross-room convenience. Both routes hit the same handle_query -> crm_client.nl_query -> POST /api/query/nl; translation runs on the box's local model, nothing leaves the box, and there is no write path so no approval gate applies. Pure logic (trigger parsing, answer rendering) in query.py with offline tests; async room wiring in bot.py (live-smoke only, per the bot's convention). Bot-side only, ships on the Spark via git pull + restart. Depends on the box-side /api/query/nl endpoint, which lands with the v93 s9pk (reminders + W2): until v93 is installed the Q&A surface 404s, so the bot deploy is staged to follow that install.
84 lines
4.4 KiB
Bash
84 lines
4.4 KiB
Bash
# Ten31 agentic system — environment template.
|
|
# Copy to .env (gitignored) and fill in. Secret values NEVER go in .env.example.
|
|
|
|
# ── Claude (frontier reasoning; Agent SDK uses an API key, not claude.ai login) ──
|
|
ANTHROPIC_API_KEY=
|
|
|
|
# ── Spark Control gateway (local model services; reads + dense embeds) ──
|
|
# HTTPS with the Start9 self-signed cert -> clients must skip TLS verification.
|
|
SPARK_CONTROL_URL=https://<spark-control-host>:<port>
|
|
SPARK_CONTROL_VERIFY_TLS=false
|
|
|
|
# ── Qdrant (direct, for ingest: create collection + upsert points) ──
|
|
# Plain HTTP on the trusted LAN, no auth currently.
|
|
QDRANT_URL=http://<spark2-host>:6333
|
|
|
|
# ── X (Twitter) API for Scout/Analyst enrichment (NOT a CRM key) ──
|
|
X_API_KEY=
|
|
|
|
# ── CRM (ingest opens the SQLite file directly, read-only) ──
|
|
CRM_DB_PATH=./data/crm.db
|
|
CRM_DEV_DB_PATH=./data/crm_dev.db
|
|
|
|
# ── Daily activity digest (Phase B) ──
|
|
# The daily digest (each team member's activity per investor + a by-investor view,
|
|
# summarized LOCALLY on Spark — never Claude) is controlled from Settings → Admin
|
|
# (stored in the DB). These env vars only SEED the first-boot default before an
|
|
# admin sets it; once the policy row exists, the admin panel wins. The "Send Digest
|
|
# Now" button works regardless. Leave blank to default to off / 6 PM.
|
|
CRM_DIGEST_ENABLED=
|
|
# Local (box-time) hour 0-23. Default 18 (6 PM).
|
|
CRM_DIGEST_SEND_HOUR=18
|
|
|
|
# ── Daily-digest sender ──
|
|
# The digest mailer prefers Gmail domain-wide delegation (the service account that
|
|
# already powers email capture; its grant includes gmail.compose, which can send) and
|
|
# falls back to SMTP below. For the Gmail/DWD path it sends impersonating this domain
|
|
# user; if unset, it uses the first active admin's email.
|
|
CRM_DIGEST_SENDER=
|
|
|
|
# ── Daily-digest outbound SMTP fallback (dev override of the per-package mailbox) ──
|
|
# On the Start9 box these are set by the "Configure Digest SMTP" action (written
|
|
# to /data/secrets/smtp/* and exported by docker_entrypoint.sh). For dev, set them
|
|
# here. SMTP_SECURITY is one of: starttls (587) | tls (465) | none.
|
|
SMTP_HOST=
|
|
SMTP_PORT=587
|
|
SMTP_SECURITY=starttls
|
|
SMTP_FROM=
|
|
SMTP_USERNAME=
|
|
SMTP_PASSWORD=
|
|
|
|
# ── Matrix intake bot (backend/matrix_intake/, runs as its own process on the Spark) ──
|
|
# Parses a typed message in a dedicated Matrix room into a proposed fundraising-grid
|
|
# add/edit (local Qwen via Spark Control above), then writes through the CRM API only
|
|
# after in-thread human approval. Reuses SPARK_CONTROL_URL / CRM_CHAT_MODEL above.
|
|
MATRIX_HOMESERVER=https://<homeserver>
|
|
MATRIX_USER=@intake-bot:<homeserver>
|
|
MATRIX_ACCESS_TOKEN=
|
|
MATRIX_DEVICE_ID=ten31-intake-bot
|
|
MATRIX_INTAKE_ROOM=!<roomid>:<homeserver>
|
|
# Dedicated room for reviewing CRM-drafted email-activity proposals (the proposed grid notes the
|
|
# Email Capture panel shows). The bot posts a review card per pending proposal here and relays the
|
|
# in-thread yes/no/edit back to the CRM, in sync with the web panel. Separate from the intake room
|
|
# so high-volume email proposals don't drown the conversational intake. Leave empty to disable the
|
|
# whole email-review poll loop. The bot must be a member of this room. Needs the server side in the
|
|
# s9pk (≥ v0.1.0:89) and the bot's CRM user set to role 'bot' (see docs/guides/matrix-intake.md).
|
|
MATRIX_EMAIL_REVIEW_ROOM=!<roomid>:<homeserver>
|
|
# Dedicated read-only Q&A room (W2): every top-level message here is answered as a natural-language
|
|
# query (translated on the box's LOCAL model — nothing leaves the box), no '?'/'@bot' trigger needed.
|
|
# The '?'/'@bot' trigger still also works in the intake room. Leave empty to disable the dedicated
|
|
# room (questions then go via the intake-room trigger). The bot must be a member of this room. Needs
|
|
# the server side in the s9pk (POST /api/query/nl) and the bot's CRM user set to role 'bot'.
|
|
MATRIX_QUERY_ROOM=!<roomid>:<homeserver>
|
|
# CRM write-back: the bot logs in as a DEDICATED service user (admin-created CRM user;
|
|
# the CRM has no service-key path, so it uses normal Bearer-JWT auth).
|
|
CRM_API_BASE=http://127.0.0.1:8080
|
|
CRM_BOT_USERNAME=
|
|
CRM_BOT_PASSWORD=
|
|
# Set to false only if CRM_API_BASE is https with a self-signed cert.
|
|
CRM_API_VERIFY_TLS=true
|
|
# Ten31 team-member names (comma-separated), fed to the parser so a teammate's name reads as
|
|
# the person DOING outreach, not the prospect ("Jonathan is chatting with Wyoming" → Wyoming).
|
|
# Optional; first names as actually used in the room. Leave empty to disable the framing.
|
|
INTAKE_TEAM_ROSTER=
|