Files
ten31-database/start9/0.4/startos/utils.ts
T
Keysat 323f016f64 Add daily activity digest — Phase B (v0.1.0:77)
Sends a once-a-day internal email to all active admins summarizing each team
member's email activity per investor, plus a team-wide by-investor view
(inbound + outbound, deduped). Narratives are generated on the LOCAL Spark
model, never Claude — the digest is intentionally un-anonymized, so substance
stays on Ten31 infra. This is an internal ops email, exempt from the
'agents draft, humans send' rule (which governs outward LP contact).

- backend/digest_builder.py: per-user + per-investor activity queries
  (soft-delete filtered), per-user Spark narrative with a deterministic
  fallback, two-section plain-text body, and the DB-backed policy resolver.
- backend/email_integration/digest_scheduler.py: always-on daily thread that
  re-reads the policy each cycle and sends once/day; window cursor in
  app_settings so a missed day rolls forward.
- server.py: POST /api/admin/digest/send-now and GET/PATCH
  /api/admin/digest/policy; scheduler wired into main().
- Control lives in Settings -> Admin (enable toggle + send-time dropdown),
  not StartOS actions; env vars only seed the first-boot default.
- Tests: backend/test_digest_builder.py.
2026-06-15 22:32:27 -05:00

52 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Informational constants shared across the startos/ modules.
// The authoritative id, title and version for the package come
// from manifest/index.ts (id, title) and versions/ (version).
export const PACKAGE_ID = 'ten-database'
export const PACKAGE_TITLE = 'Ten31 Database'
// ExVer form of the current 0.4 wrapper release (upstream 0.1.0, wrapper rev 44).
// * 0.3.5 wrapper: 0.1.0.38 (legacy, aarch64)
// * First 0.4: 0.1.0:39 (shipped seed snapshot for migration)
// * Cleanup: 0.1.0:40 (seed removed + multi-threaded server + abuser auto-ban)
// * 0.1.0:41 (frontend persists auth across refreshes)
// * 0.1.0:42 (Gmail integration) / 0.1.0:43 (Gmail POST-body hotfix)
// * 0.1.0:44 (Phase-0 ingest + MCP server in image; build-index action)
// * 0.1.0:45 (Phase-1 thesis system; dual approval; merge review; in-app index)
// * 0.1.0:46 (packaging fix: ship full backend so migrations run + endpoints work)
// * 0.1.0:47 (soft-delete instead of hard-delete; source-count diagnostics)
// * 0.1.0:48 (entity model: investors vs people; fixes double-count)
// * 0.1.0:49 (Architect: Claude thesis generation + Thesis Workshop screen)
// * 0.1.0:50 (Set Anthropic API Key UI action — no terminal needed)
// * 0.1.0:51 (entity-resolution fix: people double-count + duplicate queue)
// * 0.1.0:52 (grid/contacts unification: contact_id link + grid as front door)
// * 0.1.0:53 (seed v5 thesis into the Architect Workshop)
// * 0.1.0:54 (unification polish: LinkedIn in grid inline contact editor)
// * 0.1.0:55 (Architect grounding boundary: redaction/re-hydration privacy gate)
// * 0.1.0:56 (Thesis Workshop redesign: edit/choose/delete + approve-as-current)
// * 0.1.0:57 (redaction fix: magnitude regex no longer eats the word after an amount)
// * 0.1.0:58 (seed 5 Architect positioning framings into the Workshop as candidate options)
// * 0.1.0:59 (Email Capture admin panel + matched email into the grounding corpus)
// * 0.1.0:60 (Email Capture: single-mailbox enroll field for testing)
// * 0.1.0:61 (Email Capture: live backfill progress + auto-refresh)
// * 0.1.0:62 (fix backfill crash on no-Reply-To emails; Sync now retries errored mailboxes)
// * 0.1.0:63 (System Status: storage usage — DB, attachments, backups, disk free)
// * 0.1.0:64 (email-activity agent: propose->review->approve grid notes; sync ~15 min)
// * 0.1.0:65 (Email Capture: per-mailbox captured/matched counts)
// * 0.1.0:66 (LP Objections page: UI trigger for the Architect grounding pass)
// * 0.1.0:67 (remove LP Objections page — generic/unverifiable; pivot to proactive outreach)
// * 0.1.0:68 (Outreach Draft Assistant — tailored LP drafts via thesis + redaction boundary)
// * 0.1.0:69 (follow-up radar — deterministic "needs attention" list + one-click draft)
// * 0.1.0:70 (outreach voice upgrade — per-user voice from own emails + transparency; active-thread context)
// * 0.1.0:71 (voice by-purpose larger sample + Tier-B: create Gmail draft w/ in-thread reply)
// * 0.1.0:72 (stage v2.0 reserve-asset thesis spine as Workshop candidates)
// * 0.1.0:73 (replace old settlement spine with v2.0 reserve-asset spine across Architect + outreach prompts, seed constants, and docs; promote v2.0 to the working approved spine + soft-retire old settlement nodes, reversibly, node-level only)
// * 0.1.0:74 (security/privacy hardening — full-eval P0+2×P1: close /assets/ path traversal, add NER backstop to the outreach redaction boundary, filter deleted_at on get-by-id)
// * 0.1.0:75 (Phase-A digest SMTP: per-package "Configure Digest SMTP" action writes /data/secrets/smtp/*; entrypoint exports SMTP_*; backend smtp_send.py + admin "send test email" endpoint + Settings→Admin "Send Test Digest Email" button)
// * 0.1.0:76 (digest send via Gmail DWD: backend/email_integration/gmail_send.py uses the existing service account's gmail.compose scope for users.messages.send; digest_mailer prefers Gmail DWD and falls back to SMTP; the admin test endpoint + Settings button route through it — no app password needed when Gmail is enabled)
// * Current: 0.1.0:77 (daily activity digest — Phase B: digest_builder builds by-team-member [per-user Spark narrative, never Claude] + by-investor [inbound+outbound, deduped] sections; always-on digest_scheduler reads a DB-backed policy; enable/send-time in Settings→Admin via GET/PATCH /api/admin/digest/policy; POST /api/admin/digest/send-now + "Send Digest Now" button)
export const PACKAGE_VERSION = '0.1.0:77'
export const DATA_MOUNT_PATH = '/data'
export const WEB_PORT = 8080
export const IMAGE_ID = 'main'
export const VOLUME_ID = 'main'