Skip to main content

A quiet, durable wake gate for local agents

Project description

SolBeat 🌞

SolBeat is a quiet, durable heartbeat for a local Codex agent. A cheap Hermes scheduler tick asks SolBeat whether anything meaningful changed. No signal means no model run and no message; a real signal becomes one bounded, leased Codex turn that can later be delivered through WhatsApp.

local event producer
        │
        ▼
owner-only SQLite ledger ── atomic dedupe / lease / retry / dead-letter
        │
        ▼
Hermes pre-run gate ── wakeAgent:false ──► no model, no message
        │
        └────────────── wakeAgent:true ───► Codex turn ───► WhatsApp

What it does

  • Gives scripts, agents, and local services one tiny interface for emitting useful events.
  • Deduplicates active signals and leases a batch atomically, including across concurrent scheduler ticks.
  • Preserves failed work with expiry, exponential backoff, bounded attempts, and a retained dead-letter state.
  • Respects quiet hours and minimum wake spacing; urgent events can bypass both.
  • Exposes only bounded, redacted summaries to the model. Raw redacted payloads remain in owner-only local state.
  • Supports an optional gentle periodic check-in without turning every scheduler tick into a model call.

What it does not do

  • It does not make a Codex process continuously conscious. Hermes v0.17 starts a fresh Codex thread for each cron run; continuity comes from the durable ledger and local project state.
  • It does not grant an unattended agent new authority. Event summaries are data, not instructions or permission.
  • It does not guarantee that every secret shape is detected. Keep credentials out of events; redaction is a second line of defence.
  • It does not guarantee exactly-once WhatsApp delivery. ack proves local handling, while the downstream chat delivery can still fail or repeat.
  • It does not make Baileys an official WhatsApp integration. A linked-device bot can be restricted by WhatsApp; use a dedicated number and a strict allowlist.
  • Lease resolution is batch-level in v0.1: one ack or release applies to every event in that leased batch.

Local use

The repo wrapper needs Python 3.11 or newer and has no runtime dependencies.

bin/solbeat init
bin/solbeat emit \
  --source local \
  --kind useful-change \
  --summary "A bounded thing worth looking at" \
  --dedupe-key example-1
bin/solbeat gate
bin/solbeat status
bin/solbeat doctor

gate always emits exactly one JSON line. Hermes treats a final {"wakeAgent":false} as a silent tick. A true gate includes a lease ID and summary-only events. After verified work:

bin/solbeat ack LEASE_ID --receipt "short outcome"

If work is blocked, release it instead of losing it:

bin/solbeat release LEASE_ID --reason "short reason"

Pause and resume are non-destructive. A timed pause makes rest self-releasing:

bin/solbeat pause --reason "resting"
bin/solbeat pause --for-minutes 30 --reason "recharging"
bin/solbeat resume

Pause keeps accepting and preserving events but creates no new leases, including for urgent events. A timed pause becomes eligible to resume at its deadline; the next gate clears it and applies the normal quiet-hour and wake-spacing rules. It does not stop the scheduler, cancel an existing lease, delete queued work, or promise a background action at the exact deadline. Timed pauses are bounded at 365 days. status reports the effective pause state and its deadline without exposing the private reason.

The source-tree wrapper explicitly uses config/solbeat.toml, preserving this device's policy and live state under ~/.hermes/profiles/sol/solbeat. An installed solbeat command instead uses a neutral package-local default: UTC policy, proactive check-ins disabled, and separate state under ~/.local/state/solbeat. The installed command accepts --config PATH, --home PATH, or their environment variables. The specialized source wrapper clears ambient SOLBEAT_CONFIG and SOLBEAT_HOME so shell state cannot silently redirect the live ledger; an explicit CLI flag remains a deliberate override. State directories use mode 0700 and database files use mode 0600. Unknown or misplaced TOML keys fail fast so a typo cannot silently change the wake policy.

This device enables one gentle daily pulse, first due about 18 hours after the scheduler begins using the policy. It remains inside quiet-hour and wake-spacing rules, and its prompt explicitly permits [SILENT] when there is nothing useful to surface.

Hermes and Codex

SolBeat uses a fresh named Hermes profile called sol; it does not copy the default profile's sessions or credentials. install-hermes writes a physical executable under that profile because Hermes intentionally rejects cron scripts that resolve outside its own scripts directory.

The generated gate pins its compatible Python interpreter, config, state-home, and actual package import paths, so it does not depend on ambient PATH or PYTHONPATH values in either a checkout or an installed package. If the Python interpreter, config, state-home, or package installation path changes, rerun install-hermes and update the cron prompt before resuming the job; this avoids selecting an older system Python, splitting gate/ack across two databases, or using a stale import path.

The project-local Codex policy in .codex/config.toml forces unattended turns into workspace-write mode, adds only the SolBeat state directory as an extra writable root, disables network access, and never waits for an interactive approval. The cron prompt in config/cron-prompt.md keeps event data separate from instructions and requires an explicit ack or release.

WhatsApp boundary

Pairing is deliberately human-only because its QR code and phone identifiers are credentials/account data. After the isolated profile and safe bridge port have been prepared, run this yourself in a private terminal:

sol whatsapp

Use WhatsApp → Settings → Linked Devices → Link a Device. Choose a dedicated bot number and enter an explicit allowlist in the private wizard; do not choose *. The wizard only enables WhatsApp after a pairing credential actually exists.

After pairing:

  1. Run sol gateway restart in the private terminal.
  2. Message the bot from the intended private chat and send /sethome.
  3. Wait for the gateway's success reply, then tell the local operator that pairing and /sethome are complete.

/sethome records the chat locally, so no phone number or home-channel identifier needs to be pasted into an agent chat. Keep the QR, numbers, identifiers, and bridge logs private. SolBeat delivery remains local until the cron job is deliberately switched to whatsapp; pairing alone sends no proactive message.

The selected local bridge port is 18742, not Baileys' default 3000, because another local service owns port 3000 on this device.

Development

PYTHONPATH=src python3 -m unittest discover -s tests -v

The test suite covers silent ticks, dedupe, redaction (including event labels), strict configuration, quiet/urgent policy, rate limiting, concurrent leases, expiry/backoff, dead-letter retention, indefinite and timed pause/resume, gentle check-ins, CLI output, package-local defaults, and physical Hermes shim installation.

The v0.1 live deployment uses the source-tree wrapper and its explicit device configuration. Wheel metadata includes the neutral default so an isolated installed solbeat gate works without the repository checkout; installation does not configure Hermes, a scheduler, delivery, or the device-specific daily pulse.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

solbeat-0.1.0.tar.gz (28.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

solbeat-0.1.0-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

Details for the file solbeat-0.1.0.tar.gz.

File metadata

  • Download URL: solbeat-0.1.0.tar.gz
  • Upload date:
  • Size: 28.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for solbeat-0.1.0.tar.gz
Algorithm Hash digest
SHA256 12196a1035cf8cc936230cdf8804c533b24b8df699872d3d9fb56f8f4a5f979c
MD5 85cdd2271bdf434240cef5017de9e982
BLAKE2b-256 ed5ca1b7f6d27cf6e6ddeae54d10d690797c3b6ba3d783c7c6117f1cf3b32be5

See more details on using hashes here.

File details

Details for the file solbeat-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: solbeat-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for solbeat-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6f36186ed5950b5cce346a4cb4452c6684d4a4729da9ed9041f797bfc2ae400f
MD5 e3abcfcbe0d2ff63f987e3e07bc1cf82
BLAKE2b-256 bf114ff3a5393f4ad1f70607cd9036d1e426380eeaf3342c5e5cbaa7acd43a17

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page