Skip to content

Repository files navigation

hypogum

A background agent that watches your screen, learns your routines, plans your day, and runs preparatory tasks before you start your work. Lives in your system tray.

UI note: hypogum's own React dashboard has been retired. Its companion app Molly Sachs is the interaction layer (voice + desktop UI) and talks to hypogum web (the local API on :8056) directly. hypogum here is API + agent + tray only.

Getting started

# 1. Python venv + deps
python -m venv .venv
.venv\Scripts\pip install -e .          # Windows
.venv/bin/pip install -e .              # macOS / Linux

# 2. Install an agent CLI. hypogum shells out to this for all LLM work
npm install -g opencode-ai
opencode auth login                     # or set API keys as env vars

# 3. Create the config file
cp hypogum.example.toml hypogum.toml

# 4. Run, with the venv's interpreter
.venv\Scripts\python dev.py             # Windows
.venv/bin/python dev.py                 # macOS / Linux

dev.py deliberately imports nothing from hypogum. It spawns every service — including the agent server, via python -m hypogum.agents.serve — as <venv python> -m …, and puts this checkout's src/ on PYTHONPATH first, ahead of site-packages. So import hypogum in any child resolves here regardless of what is installed: with hypogum uninstalled from the venv entirely, the whole stack still comes up, and so does the test suite (pythonpath in [tool.pytest.ini_options]).

That closes a hole that cost real time. dev.py used to import create_provider and Config just to build the agent-server command line, so a global interpreter with another checkout installed would silently drive that clone's code. Running the wrong checkout is now structurally impossible rather than something to detect.

The venv is still required — third-party dependencies and the hypogum console script come from pip install -e .. What PYTHONPATH removes is any dependence on that install being current. Preflight checks the venv rather than itself: that it exists, that its dependencies import, and that the db and web ports are free (another checkout answering on :8055 would otherwise be mistaken for our own).

Add --ingest to fire the first processing cycle immediately instead of waiting 10 minutes.

The API + agent + tray start; use the Molly Sachs app for the UI (it points at http://localhost:8056).

How it works

Observe

Every 60 seconds, the screen observer captures your monitor. Near-duplicate frames are discarded, so staring at code doesn't fill your disk. Active window titles are recorded alongside each screenshot. You can also drop notes into data/ingest/user-input/ or use the tray's Quick Note dialog — they're picked up next cycle.

Describe

A cheap multimodal model turns the recent screenshots and notes into one markdown product per observer — what was on screen, when, and what it looked like you were doing. Products land in data/ingest/pending/ and wait there.

Process

Every 10 minutes, whatever has piled up in pending/ is promoted into a batch under data/ingest/working/<batch_id>/, and six agents run on it at once. Each writes only its own part of the memory store and nowhere else:

Module Owns What it does
goals memory/goals/ (files) one page per goal, ticking progress against what the evidence actually shows
journal memory/journal/ (files) a diary, one file per day: each block of time, what you did, what came of it
user memory/USER.md + TASTE.md who you are (facts, experiences) and what you prefer (taste, domain by domain)
entities memory/entities/ (files) one page per recurring thing that isn't you: tools, services, people, datasets
calendar the calendar_events table records observed time, and plans the rest of your day into suggested
agent_tasks the agent_tasks table reads TASTE.md and writes runnable sub-agent tasks, one per row

When all six finish, the batch is archived to data/ingest/done/.

entities/ is the one that is not about you. Every other module writes about the user — what you did, what you're trying to do, who you are, what you prefer — while this one keeps a page per recurring thing: a service that keeps breaking, a tool that replaced another, a dataset you keep returning to, a person you collaborate with. A page is created on the second sighting, not the first, so it stays a few dozen pages rather than a few hundred stubs.

Splitting it this way is what makes the parallelism safe — no two agents touch the same file or the same row — and it means each prompt can be about one job instead of the whole ontology.

The diary is the long-range memory. journal/2026-07-28.md holds one entry per block of time, with a duration and an outcome, and it exists so the user module can read a fortnight at once and see what a single day cannot: ninety minutes twice a week on one thing is engagement, while six five-minute returns across four days is avoidance. In a schedule those look identical. The calendar_events table covers the same hours, but it is the schedule — rows, queried by date, including the future; the diary is the record, read many days at a time.

Prose is files; records are rows. Goals, entity pages, USER.md and TASTE.md are markdown, because they have no fixed shape and you read and edit them by hand. Calendar events and agent tasks are tables: every field was already declared in frontmatter, and they are queried by date range and status far more often than they are read. The two table-backed modules write through a CLI (hypogum calendar …, hypogum task …) rather than file tools. memory/calendar_view.md is the calendar's rendered read surface, regenerated each cycle.

Events still carry a markdown description — what was actually in the block, or, for a suggestion, the argument for it. The view shows its opening line, calendar get --id <id> has the whole thing, and the .ics export carries it verbatim so it shows up in your calendar app.

TASTE.md is the piece the rest leans on. It is written as user research rather than a fact table: sections are life domains (code, math, language learning, games, writing, tools, rhythm…) and each entry is a paragraph saying what you think, prefer, or dislike, with the conditions attached and dated evidence at the end. Its companion USER.md is shorter and answers a different question — who you are: name, languages, machines, the people in your week, and the things you have finished. The rule that keeps them apart is whether it survives a change of mind. The agent_tasks module reads both to decide what a sub-agent could produce that you would actually want — preparation for your next block, groundwork on a project, a capability you keep almost getting around to building, or material you would otherwise go collect yourself.

You review and accept suggestions — and browse/edit memory, launch agent tasks, etc. — from the Molly Sachs app, which drives everything through hypogum web (:8056). Accepting a suggested block flips its bucket to planned.

From a terminal:

hypogum calendar list --from 2026-07-20 --to 2026-07-27
hypogum calendar view          # re-render memory/calendar_view.md
hypogum calendar export        # data/calendar.ics, subscribable
hypogum task list --status pending

Upgrading a store that predates this? hypogum agent imports the old calendar_events/ and agent_tasks/ markdown into the tables on startup, moving the directories aside rather than deleting them. hypogum import-memory does the same thing on demand.

Full design notes — why files for prose and tables for records, how the agents write to a table, and the one-cycle staleness the parallel fan-out buys: design/memory-modules.md.

Reaching memory from outside the agent — the four things the API supports, why there is no create-a-page call, and why none of MCP/CLI/web may start a processor: design/memory-api.md. The HTTP surface Molly consumes, route by route: design/web-api.md.

Configuration

hypogum.toml is the single source of truth. Copy hypogum.example.toml to hypogum.toml and edit; every setting has a working default, so a short file is a legal file and deleting a key resets it. The only environment variable is HYPOGUM_CONFIG, which points at the file when it isn't beside the checkout — a path, not a setting.

Nothing is stored in the database, and there is no settings endpoint. Settings change by editing the file, by hand or from the tray, which writes to the same file through tomlkit so your comments survive. A running agent notices within a few seconds; cadences and observer toggles apply live, while ports and the provider are read once at start (nothing rebinds a listening socket because a file changed).

Modules are configured, not hardcoded. One [modules.NAME] table each — enable or disable it, point it at your own prompt file, give it its own model, or add a module hypogum has never heard of:

[modules.journal]
enabled = false                          # turn a shipped module off

[modules.user]
model = "anthropic/claude-sonnet-5"      # the hardest judgement, smallest output

[modules.reading]                        # a new module
enabled = true
owns    = "reading/"                     # must be a surface nothing else owns
prompt  = "module_reading.md"            # in <data_dir>/prompts/ or beside this file
summary = "what the user is reading and what they took from it"

API keys go in [llm.keys], either literally or as env:VARIABLE to read them from the environment at startup. hypogum.toml is gitignored and on the agents' read-deny list either way, but env: keeps secrets out of the file entirely.

The settings you're most likely to touch:

Variable Default Why
agent.provider opencode opencode or claude, claude support is experimental
calendar.timezone OS-local IANA name like America/Chicago
process.interval 600 Seconds between cycles
observe.screen_interval 60 Seconds between captures
observe.camera_enabled false Enable webcam capture
observe.pause_when_locked true Pause when workstation is locked
memory.waves parallel staged runs wave 0 to completion first, then wave 1
runner.concurrency 5 Below the number of enabled modules, the fan-out serializes
paths.data_dir data Relative to the config file; relocates the whole store

paths.data_dir defaults to data beside the config file and is the only path setting. Everything hypogum writes — memory/, ingest/, memory_index/, agent_workspace/, artifacts/, app.db — lives under it at a fixed subpath. The individual per-directory overrides were removed: agents are confined to a directory tree, and letting parts of the store live outside it broke that boundary. Move paths.data_dir to relocate the whole store.

By default, hypogum uses the following models:

Role Model API key env var
Describer (reads screenshots → text) gemini-3.1-flash-lite GOOGLE_API_KEY
Memory modules (all five) deepseek/deepseek-v4-pro DEEPSEEK_API_KEY
Worker (runs tasks) deepseek/deepseek-v4-pro DEEPSEEK_API_KEY

The cheap multimodal describer turns screenshots into text once per cycle, so the module agents can be text-only deepseek-v4-pro — no per-run vision calls.

Override one module without touching the rest with model in its own table — HYPOGUM_AGENT_MODULE_USER_MODEL is the one worth spending on, since the taste dossier is the hardest judgment call and the smallest output.

Set these in [llm.keys], or as env:VARIABLE to read them from the environment. All model choices are configurable.

Contributing

pip install -e ".[dev]"
python -m pytest tests/ -v
ruff check src/

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages