Skip to main content
All persistent data lives in ~/.ollim-bot/. The directory is created on first use and automatically managed as a git repository — storage.py auto-commits after most writes. The root contains agent-facing files (routines, reminders, webhooks, spec symlinks) that the agent reads and writes directly. Infrastructure state (sessions, credentials, ping budget, etc.) lives in the state/ subdirectory, keeping the agent’s working directory clean.
~/.ollim-bot/
├── routine-reminder-spec.md →       # Symlink to docs/routine-reminder-spec.md
├── webhook-spec.md →                # Symlink to docs/webhook-spec.md
├── routines/
│   └── *.md                         # Recurring routine specs
├── reminders/
│   └── *.md                         # One-shot reminder specs
├── webhooks/
│   └── *.md                         # Webhook specs
└── state/
    ├── bot.pid                      # PID file for single-instance check
    ├── credentials.json             # Google OAuth credentials (from Cloud Console)
    ├── token.json                   # Google OAuth token (auto-generated)
    ├── sessions.json                # Current session ID (plain string)
    ├── session_history.jsonl        # Append-only session lifecycle events
    ├── pending_updates.json         # Background fork updates queued for main session
    ├── inquiries.json               # Button inquiry prompts (7-day TTL)
    ├── fork_messages.json           # Discord message → fork session mapping (7-day TTL)
    └── ping_budget.json             # Ping budget state (ephemeral, no git commit)

Session files

All files in this section and below live in the state/ subdirectory. These files track the agent’s conversation state across restarts.
FileFormatDescription
sessions.jsonPlain stringActive Agent SDK session ID. A raw string, not JSON. Read on startup to resume.
session_history.jsonlJSONLAppend-only log of session lifecycle events. One JSON object per line.

Session events

Each line in session_history.jsonl contains:
FieldTypeDescription
session_idstrThe Agent SDK session ID
eventstrOne of: created, compacted, swapped, cleared, interactive_fork, bg_fork, isolated_bg
timestampstrISO 8601 datetime in the configured timezone (OLLIM_TIMEZONE)
parent_session_idstr | nullSet for compacted, swapped, and fork events
session_history.jsonl
{"session_id": "sess_abc", "event": "created", "timestamp": "2026-02-24T10:00:00-08:00", "parent_session_id": null}
{"session_id": "sess_def", "event": "bg_fork", "timestamp": "2026-02-24T10:05:00-08:00",
 "parent_session_id": "sess_abc"}
Compaction is detected two ways: the /compact command detects it via the compact_boundary SystemMessage from the SDK, while save_session_id() catches SDK auto-compaction by detecting session ID changes. A _swap_in_progress flag suppresses false compacted events during swap_client().

Fork and inquiry files

These files track ephemeral Discord interaction state. Both use a 7-day TTL — entries older than 7 days are pruned on every read.
FileFormatDescription
fork_messages.jsonJSON arrayMaps Discord message IDs to fork session IDs, enabling reply-to-fork.
inquiries.jsonJSON objectMaps 8-char hex keys to inquiry prompts. Persists button actions across restarts.

fork_messages.json

fork_messages.json
[
  {
    "message_id": 1234567890123456789,
    "fork_session_id": "sess_fork_abc",
    "parent_session_id": "sess_main",
    "ts": 1740000000.0
  }
]
FieldTypeDescription
message_idintDiscord message snowflake ID
fork_session_idstrAgent SDK session ID for the fork
parent_session_idstr | nullSession the fork branched from
tsfloatUnix timestamp for TTL pruning

inquiries.json

inquiries.json
{
  "a1b2c3d4": {"prompt": "Can you summarize my emails?", "ts": 1740000000.0}
}
FieldTypeDescription
(key)str8-character hex UUID
promptstrThe full prompt string for the button inquiry
tsfloatUnix timestamp for TTL pruning

Pending updates

Background forks write summaries to pending_updates.json via the report_updates MCP tool. The main session reads and clears these on its next turn.
pending_updates.json
[
  {
    "ts": "2026-02-24T10:15:00-08:00",
    "message": "Checked your morning emails — 3 need replies, none urgent."
  }
]
FieldTypeDescription
tsstrISO 8601 datetime in the configured timezone (OLLIM_TIMEZONE)
messagestrSummary from the background fork
Concurrent background forks use an asyncio.Lock to protect the read-modify-write cycle on this file, preventing lost updates.

Ping budget

ping_budget.json stores the refill-on-read token bucket state for proactive pings. This file is not git-committed — it is ephemeral state that resets naturally.
ping_budget.json
{
  "capacity": 5,
  "available": 3.7,
  "refill_rate_minutes": 90,
  "last_refill": "2026-02-24T09:30:00-08:00",
  "critical_used": 1,
  "critical_reset_date": "2026-02-24",
  "daily_used": 2,
  "daily_used_reset": "2026-02-24"
}
FieldTypeDefaultDescription
capacityint5Maximum pings in the bucket
availablefloatCurrent fractional ping count
refill_rate_minutesint90Minutes per 1 ping refill
last_refillstrISO 8601 datetime of last refill computation
critical_usedintCritical pings used today (resets daily)
critical_reset_datestrISO date for critical_used reset
daily_usedintTotal pings used today
daily_used_resetstrISO date for daily_used reset
On every load(), time-based refill is computed and daily counters are reset if the date has changed. The updated state is saved back immediately.

Scheduling directories

Routines, reminders, and webhooks are each stored as .md files in their own subdirectory. Filenames are auto-generated slugs from the message content (max 50 characters). The id field in YAML frontmatter is authoritative — filenames are for human readability.
DirectoryContentsDescription
routines/*.mdRecurring cron-scheduled routines
reminders/*.mdOne-shot and chainable reminders
webhooks/*.mdWebhook endpoint specs with prompt templates
All three use the same storage format: YAML frontmatter delimited by ---, followed by a markdown body. See File formats for the full field specs.
routines/morning-briefing.md
---
id: abc123
cron: "30 8 * * 1-5"
description: Morning briefing
---
Review my tasks and calendar for today, then give me a summary.
The agent has direct file access to routines/ and reminders/ — it creates and manages these files without going through CLI commands.

Google credentials

FileSourceDescription
credentials.jsonManual — Google Cloud ConsoleOAuth 2.0 client creds
token.jsonAuto-generated on first auth flowOAuth refresh and access tokens
Neither file is git-committed. token.json is auto-generated when you first authenticate.

Process lock

bot.pid contains the PID of the running bot process. On startup, _check_already_running() reads this file and checks /proc/<pid>/cmdline to prevent duplicate instances. The file is deleted on exit via atexit. On startup, _ensure_spec_symlinks() in main.py creates symlinks in ~/.ollim-bot/ pointing to specification documents in the project’s docs/ directory:
SymlinkTargetReferenced by
routine-reminder-spec.mddocs/routine-reminder-spec.mdSystem prompt (routines/reminders section)
webhook-spec.mddocs/webhook-spec.mdSystem prompt (webhooks section), webhook handler
The system prompt directs the agent to “enter a fork and read routine-reminder-spec.md” (or webhook-spec.md) when it needs the full YAML specification. Because the agent’s working directory is ~/.ollim-bot/, the symlinks make these files accessible by name without hardcoding paths to the source tree. Symlinks are only created if the target doesn’t already exist — they survive bot restarts and are never overwritten.

Git tracking

The ~/.ollim-bot/ directory is initialized as a git repository. storage.py auto-commits after writes to most files. Files that are not git-committed:
FileReason
state/ping_budget.jsonEphemeral state that refills over time
state/bot.pidProcess lock deleted on exit
state/credentials.jsonSensitive OAuth client credentials
state/token.jsonSensitive OAuth tokens
state/sessions.jsonFrequent overwrites on every agent turn
state/fork_messages.jsonEphemeral mapping with 7-day TTL
state/pending_updates.jsonEphemeral cross-fork messaging
state/inquiries.jsonEphemeral button state with 7-day TTL
All other files — session history, routines, reminders, and webhooks — are auto-committed on every write.
All writes use atomic temp-file-then-rename (tempfile.mkstemp + os.replace) to prevent data corruption. Do not manually edit files while the bot is running.

Next steps