time-gap is a tiny Hermes plugin that tells the model when a conversation has meaningfully aged. It stays completely silent during a continuous chat, then adds one private, coarse-grained hint when the configured threshold is crossed—or when the user's calendar rolls past midnight.
The model notices that time passed. The conversation still feels natural.
See it in one glance
10:00 You "Let's continue with option B."
10:01 Assistant "I'll draft the migration plan."
10:06 You "Start with the schema." → no note; conversation is fresh
· · · two days pass · · ·
Friday You "Can we ship this today?" → private time-gap context added
The model receives an ephemeral hint like this at API-call time:
[time-gap: about 2 days have passed since the previous exchange]
It is instructed to factor the hint in, never quote it. That is enough to make the model reconsider stale plans, dates, deadlines, availability, and other time-sensitive context.
Why selective time awareness?
Stamping every turn adds noise and makes chat feel mechanical. time-gap treats silence as the default, so the signal only appears when it carries information.
| Situation | What happens |
|---|---|
| Active back-and-forth | Nothing is injected |
Gap reaches min_gap_seconds |
A coarse elapsed-time hint is injected |
| Chat crosses local midnight | A date-rollover hint is injected, even for a short gap |
| New session / no assistant reply | Nothing is injected |
Excluded platform, such as cron |
Nothing is injected |
| Missing DB, read error, or clock hiccup | Fails open; the turn continues untouched |
How it works
flowchart LR
A["pre_llm_call"] --> B{"Enabled for this platform?"}
B -- No --> S["Stay silent"]
B -- Yes --> C["Read last assistant timestamp<br/>from state.db"]
C --> D{"Gap threshold or<br/>local date rollover?"}
D -- No --> S
D -- Yes --> E["Append private context<br/>to current user turn"]
E --> F["Call the model"]
- Restart-safe: elapsed time comes from the last persisted assistant message, not in-process memory.
- Prompt-cache friendly: the note rides on the current user turn and never touches the cached system-prompt prefix.
- Transcript-clean: injected context is used for the API call only and is never written back to session history.
- Timezone-aware: midnight follows Hermes' configured timezone through
hermes_time.get_timezone, not UTC. - Writer-friendly:
state.dbis opened with SQLitemode=ro, including correct reads from an active WAL.
Install
Clone the plugin directly into Hermes and enable it:
cd ~/.hermes/plugins
git clone https://github.com/Randool/time-gap.git
hermes plugins enable time-gap
This creates the following plugin directory:
~/.hermes/plugins/time-gap/
├── __init__.py
└── plugin.yaml
To customize its behavior, edit the generated entry in ~/.hermes/config.yaml and reload Hermes:
plugins:
enabled:
- time-gap
entries:
time-gap:
enabled: true
min_gap_seconds: 7200
notify_date_change: true
exclude_platforms:
- cron
debug: false
The defaults are intentionally conservative: a 120-minute threshold, date-change detection enabled, and cron excluded.
Configuration
| Option | Default | Purpose |
|---|---|---|
enabled |
true |
Master switch for the plugin |
min_gap_seconds |
7200 |
Minimum elapsed time before a note is injected |
notify_date_change |
true |
Also trigger when the user's local calendar date changes |
exclude_platforms |
[cron] |
Platforms that should never receive a time-gap note |
debug |
false |
Write decisions to the plugin-local debug.log |
Human-friendly, intentionally imprecise
The hint gets coarser as the gap grows:
| Elapsed gap | Example wording |
|---|---|
| Under an hour | about 40 minutes |
| Several hours | about 5 hours |
| Around one day | about a day |
| Multiple days | about 3 days |
Exact wall-clock time is never injected. If precision matters, the model can re-ground using the current system time.
Design guarantees
- The last assistant reply defines the previous completed exchange. A newly persisted user turn cannot accidentally reset the clock.
- Gap and date rollover are independent triggers. A 10-minute pause across midnight can matter even when the normal threshold is two hours.
- Multi-day messages avoid redundant wording:
about 3 daysalready implies a date change. - Every failure path returns no context rather than blocking a user turn.
Test
The suite covers session isolation, WAL visibility, platform exclusions, graded phrasing, midnight rollover, disabled settings, and fail-open behavior.
python -m pytest tests/test_time_gap.py -q
From a Hermes source checkout, use:
python -m pytest plugins/time-gap/tests/test_time_gap.py -q