Back to home

Architecture

How Echolect is wired.

A meeting has one fast path for live, in-the-moment answers, and slower side paths for screenshots, web research, speaker naming, summaries, and project memory. The live session only ever receives the new transcript delta; everything durable is a file on disk. Each box below is a real session, IPC channel, or artifact.

LLM session STT · Deepgram Disk artifact External service persistent session one-shot
01

Meeting start — STT path & live-session spin-up

Starting a meeting opens the overlay, minimizes the dashboard, and spins up two persistent Deepgram streams (mic + system audio) alongside the warm live LLM session. Finalized lines are de-duplicated, speaker-labeled, written to transcript.md, and bridged to the UI.

flowchart TD
  classDef llm fill:#241f3a,stroke:#8B7CF6,color:#d8d2fb;
  classDef stt fill:#10302e,stroke:#54C7C0,color:#bdeae6;
  classDef disk fill:#332a16,stroke:#E7B24C,color:#f0dca6;
  classDef ext fill:#202531,stroke:#6b7280,color:#cdd2da;
  MS["meeting:start"]
  MS --> OVw["create overlay window"]
  MS --> DASHmin["minimize dashboard"]
  MS --> AP["new AudioPipeline"]
  AP --> STF["transcript.md"]:::disk
  AP --> APstart["pipeline.start()"]
  MS --> SLS["startLiveSession"]:::llm
  subgraph CAP["overlay renderer — audio capture"]
    MIC["mic getUserMedia"]:::stt
    SYS["system / screen-share audio"]:::stt
    MIC --> DSm["downsample 16k mono"]:::stt
    SYS --> DSs["downsample 16k mono"]:::stt
  end
  DSm -->|"audio:data (mic)"| AD["audio:data handler"]
  DSs -->|"audio:data (system)"| AD
  AD --> FEED["feedAudio()"]:::stt
  APstart --> MC["micClient ⌁"]:::stt
  APstart --> SC["systemClient ⌁"]:::stt
  FEED --> MC
  FEED --> SC
  MC -->|"wss nova-3"| DGm["Deepgram"]:::ext
  SC -->|"wss nova-3"| DGs["Deepgram"]:::ext
  DGm --> ORr["onResult (mic)"]:::stt
  DGs --> ORs["onResult (system)"]:::stt
  ORr --> DEDUP{"dup of system? 50% / 4s"}:::stt
  DEDUP -->|drop| X[" "]
  DEDUP -->|keep| FIN
  ORs --> FIN["final line — Me / Them"]:::stt
  FIN --> TFILE["append transcript.md"]:::disk
  FIN --> BR["bridge → overlay + dashboard"]
          
02

The warm live session — what context goes in

The live session starts pre-loaded: a behavior prompt, your personal context, the project's rolling context, and a read-only scope over the meeting directory and project codebase. It's warmed up once, then streams answers back to the overlay — and the same handles are reused by research.

flowchart LR
  classDef llm fill:#241f3a,stroke:#8B7CF6,color:#d8d2fb;
  classDef disk fill:#332a16,stroke:#E7B24C,color:#f0dca6;
  classDef cfg fill:#1b2230,stroke:#3a4252,color:#cdd2da;
  subgraph KNOW["system prompt"]
    MP["mode prompt — project or general"]
    PC["personal-context.md"]:::disk
    PJ["project context.md"]:::disk
    FL["files readable on demand (add-dirs)"]
  end
  KNOW --> SESS
  SESS["live session ⌁ — fast model · cwd = meeting dir · Read / Grep / Glob · add-dir: meeting + codebase"]:::llm
  SESS --> WARM["warm up — Reply: READY"]
  SESS -->|stream| TOK["assist:token / assist:turn → overlay"]
  GLOB["globals — liveSession · meetingDir · addDirs · knowledge"]:::cfg
  SESS -.reused by research.-> GLOB
          
03

Asking — intents, the delta, screenshots & research

An intent or a typed question composes a turn from only the new transcript delta plus any buffered screenshot/research findings, then sends it to the warm session. Screenshots and research run in their own sessions so the live thread never blocks; their findings buffer in and fold into your next ask.

flowchart TD
  classDef llm fill:#241f3a,stroke:#8B7CF6,color:#d8d2fb;
  classDef disk fill:#332a16,stroke:#E7B24C,color:#f0dca6;
  HOT["global hotkey"] --> PILL["intent pill — Answer / Suggest / Ask back / Explain"]
  PILL --> ASK["ask()"]
  TYPE["typed question"] --> ASK
  ASK --> BC["buildContext()"]
  BC --> D1["transcript delta since last cursor"]
  BC --> D2["unsent screenshot marks"]
  BC --> D3["unsent research marks"]
  D1 --> COMP["compose turn"]
  D2 --> COMP
  D3 --> COMP
  COMP -->|"assist:send"| LSND["liveSession.send()"]:::llm
  LSND -->|tokens| BUB["answer bubble (streams)"]
  SHOT["screenshot hotkey / pill"] --> GRAB["grab frame from screen stream"]
  GRAB --> SVPNG["screenshot.png"]:::disk
  SVPNG --> ANA["analyze — fast model · Read / Grep / Glob"]:::llm
  ANA --> SMARK["buffer screenshot mark"]
  SMARK -. next ask .-> D2
  RES["research toggle / command"] --> DOR["doResearch()"]
  DOR --> RSESS["new session — heavy model · WebSearch / WebFetch / Read"]:::llm
  RSESS -->|"research:token"| RBUB["research bubble"]
  RSESS --> RMARK["buffer research mark"]
  RMARK -. next ask .-> D3
          
04

Meeting stop — reconcile, summarize & roll up

On stop, the side processes tear down, then two heavy passes run in the background: speaker reconciliation rewrites the transcript with real names, and the summary is generated and written. For project meetings, the summary is then merged and condensed into context.md — leaving your own notes untouched.

flowchart TD
  classDef llm fill:#241f3a,stroke:#8B7CF6,color:#d8d2fb;
  classDef disk fill:#332a16,stroke:#E7B24C,color:#f0dca6;
  STOP["meeting:stop"]
  STOP --> KILL["stop pipeline · live session · overlay · restore dashboard"]
  STOP --> REC["reconcileSpeakers — heavy model · rewrite transcript with real names"]:::llm
  REC --> GS["generateSummary — heavy model · cwd = meeting dir"]:::llm
  GS --> RD["reads transcript + context + chat + screenshots"]:::disk
  RD --> FMT["TITLE + summary markdown"]
  FMT --> WSUM["write summary.md"]:::disk
  FMT --> RNAME["auto-apply AI title — rename meeting"]
  FMT --> NOTI["notify dashboard — summary ready"]
  FMT --> ISP{"project meeting?"}
  ISP -->|yes| UPC["updateProjectContext — heavy model · no tools"]:::llm
  UPC --> MRK["read rolling-summary block"]
  MRK --> REWRITE["merge + condense newest summary"]
  REWRITE --> WCTX["write context.md — your notes untouched"]:::disk
          
05

Every session at a glance — model, scope & tools

Echolect routes work through distinct sessions, each with the right model tier, working directory, read scope, and toolset. Live and screenshot work use a fast model to stay responsive; research, summaries, and rollups use a heavier model. Either runs on the Claude or OpenAI Codex CLI.

flowchart LR
  classDef llm fill:#241f3a,stroke:#8B7CF6,color:#d8d2fb;
  L1["LIVE ASSIST ⌁ — fast · cwd = meeting · add-dir: meeting + codebase · Read / Grep / Glob"]:::llm
  L2["RESEARCH • — heavy · cwd = meeting · + WebSearch / WebFetch"]:::llm
  L3["SCREENSHOT • — fast · cwd = meeting · Read / Grep / Glob"]:::llm
  L4["SUMMARY • — heavy · cwd = meeting · Read / Grep / Glob"]:::llm
  L5["CONTEXT ROLLUP • — heavy · cwd = project · no tools"]:::llm
  L6["AI EDIT ⌁ — dashboard · caller-supplied dirs / tools"]:::llm
          

Want the real thing?
Every box above is in the source — read it, or run it yourself.

Read the source Download