Skip to main content
Context in ollim-bot moves through a cycle: the main session feeds into forks, forks produce pending updates, and pending updates flow back into the main session. Background forks receive additional context through a preamble that includes ping budgets, forward schedules, and tool restrictions.

Context by execution mode

What the agent sees depends on which mode it’s running in. This matters for both understanding agent behavior and configuring background tasks effectively.
Main sessionInteractive forkBackground fork (forked)Background fork (isolated)
System promptbuild_system_prompt() (profile + operational)Same as mainAssembled preambleAssembled preamble
Conversation historyFull (persistent, resumed across restarts)Forked from main at branch pointForked from mainNone (clean slate)
Pending updatesPopped (consumed)Peeked (read-only)Prepended to promptNone
MCP toolsAll 11 discord tools + docs serverSame as mainDiscord tools dynamically gatedDiscord tools dynamically gated
File toolsRead, Write, Edit, Glob, Grep (./**.md)Same as mainRead, Glob, Grep (./**.md)Read, Glob, Grep (./**.md)
Other toolsWebFetch, WebSearch, Task, Skill, CLISame as mainollim-bot help, ollim-bot tasks *ollim-bot help, ollim-bot tasks *
SkillsNot loadedNot loadedLoaded if declared in YAML skills fieldLoaded if declared in YAML skills field
ThinkingUser-controlled (/thinking)Adaptive by defaultConfigurable per job (thinking field)Configurable per job
PermissionsUser-controlled (dontAsk default)Inherits from mainDynamic gating per toolDynamic gating per tool
Preamble sectionsNoneNonePing, update, busy, budget/schedule, tool restrictions, skillsSame
The default background fork tool set (DEFAULT_BG_TOOLS) includes read-only file tools (Read, Glob, Grep scoped to ./**.md), the ollim-bot help and ollim-bot tasks * CLI commands, and the three reminder MCP tools (add_reminder, list_reminders, cancel_reminder). Discord MCP tools (ping_user, discord_embed, report_updates, follow_up_chain) are not in this static list — they are dynamically gated at runtime based on the job’s allow-ping and update-main-session flags. Jobs can expand the tool set via the allowed-tools YAML field — see Background forks.
The isolated mode tradeoff: no conversation history means less context to reason over, but also no risk of acting on stale context from hours ago. Use isolated: true for tasks that should evaluate the current state fresh — like checking if a deadline passed.

Overview

Every interaction starts in the main session. When a fork branches off, it inherits the main session’s conversation history. When a background task fires, it receives an assembled preamble with scheduling awareness. When either type of fork finishes, it can feed results back to the main session through pending updates — short summaries that get prepended to the next interaction. The flow forms a loop:
  1. Main session receives user messages with any pending updates prepended
  2. Interactive forks branch from main, peek at pending updates (read-only)
  3. Background forks branch from main (or start fresh if isolated), receive a preamble with budget and schedule context
  4. Forks write back to the main session via report_updates
  5. Main session pops those updates on the next interaction

Pending updates

Pending updates are the primary channel for passing context from forks back to the main session. They are stored in ~/.ollim-bot/state/pending_updates.json as a JSON array of timestamped messages. Each entry is a PendingUpdate with two fields:
FieldTypeDescription
tsstrISO 8601 timestamp in the configured timezone
messagestrSummary text written by the agent

Write path

Any fork can write a pending update by calling the report_updates MCP tool. The write is protected by an async lock for concurrent background fork safety — multiple forks can safely append updates without corruption. Updates are capped at 10 entries (MAX_PENDING_UPDATES). When a new update would exceed this limit, the oldest entries are dropped and a sentinel is prepended so the agent knows omission occurred — for example, “(3 earlier update(s) omitted — cap reached)”. The sentinel occupies one slot, leaving 9 slots for real entries. Whether a background fork is allowed or required to call report_updates depends on its update-main-session mode:
ModeBehavior
on_pingReport if the agent sent a ping or embed (default)
alwaysMust call report_updates before fork exits
freelyReporting is optional
blockedreport_updates returns an error
When blocked is combined with allow-ping: true, reporting is disabled but direct pings remain available — see Background forks for when this combination is useful. A stop hook (require_report_hook) enforces these modes — see the enforcement table for details. In on_ping mode, it blocks if the agent pinged but didn’t report.

Read path

The main session and forks read pending updates differently. The injected header varies by context to guide the agent’s behavior:
ContextHeaderPurpose
Main sessionRECENT BACKGROUND UPDATES (mention key findings in your response)Prompts the agent to surface findings
Fork (interactive or forked bg)RECENT BACKGROUND UPDATES (read-only — main session will also see these)Prevents the fork from acting on updates meant for the main session
When pending updates are injected into the main session, the bot also sends a user-visible note — “catching up on background activity…” — so you know background context is being included.
The main session pops pending updates — reads and clears the file atomically. The updates are prepended to the next message the agent sees, giving it context about what happened in the background.After the pop, the file is deleted. Each update is consumed exactly once.

Prompt tags

Every routine, reminder, and fork injects a tag at the start of its prompt. These tags tell the agent what type of session it’s in and which task triggered it.
Tag patternSession type
[routine:ID]Foreground routine (runs in main session)
[routine-bg:ID]Background routine (runs in a fork)
[reminder:ID]Foreground reminder (runs in main session)
[reminder-bg:ID]Background reminder (runs in a fork)
[webhook:ID]Webhook (always background)
[fork-started]Interactive fork (topic, no-topic, or bg resume)
[fork-timeout]Idle fork timeout prompt
[button]Non-fork button click dispatching a prompt
[system]Internal system prompt (e.g., fork report button)
The agent’s system prompt documents how to interpret these tags. For foreground tasks, only the tag and the task’s markdown body are injected. For background tasks, the tag is followed by the full preamble.

Background fork preamble

Background forks receive an assembled preamble between the prompt tag and the task’s markdown body. The preamble is built by build_bg_preamble in scheduling/preamble.py and contains several sections.

Preamble sections

Tells the agent whether ping_user and discord_embed are available. When allow-ping is false, the agent is told these tools are disabled.
Based on the update-main-session mode, tells the agent when and whether to call report_updates. Each mode has specific instructions (must report, report if pinged, optional, or blocked).
If the user is mid-conversation (the agent lock is held), the preamble instructs the agent to use report_updates instead of direct pings — unless critical=True. This prevents interruptions during active conversations.
Included only when allow-ping is true. Contains:
  • Current ping budget status (available tokens, capacity)
  • Upcoming schedule: a list of tasks within the schedule window, each with fire time, label, description, and file path
  • Refill estimation before the last routine or reminder
  • Guidance on when to ping vs. report (the “regret test”)
  • Note about critical=True bypass
Listed only when allowed-tools is configured. Shows which tools are available.
When a routine or reminder has a skills field referencing one or more skills, the preamble builder adds a REQUIRED SKILLS instruction block telling the agent to invoke each skill before proceeding. _merge_skill_tools() adds Skill(<name> *) patterns to the fork’s allowed_tools so the SDK permits the Skill tool. The SDK handles content loading, dynamic context injection, and allowed-tools enforcement natively. Skills propagate through chain reminders via ChainContext.

Forward schedule

The budget section includes a forward schedule built by build_upcoming_schedule. This gives the agent awareness of what other background tasks are coming, so it can make informed decisions about whether to spend a ping budget token now or save it. Schedule construction uses a dynamic window:
ParameterValuePurpose
Grace window15 minutesIncludes recently-fired routines
Base window3 hoursInitial forward-looking window
Min forward count3Extends window if fewer than 3 tasks found
Maximum window12 hoursHard upper limit on lookahead
Each entry in the schedule includes:
FieldDescription
fire_timeWhen the task fires
labelDisplay name (e.g., “Chain reminder (2/4)“)
descriptionFrom YAML or truncated message (60 char limit)
file_pathRelative path (e.g., routines/daily-review.md)
silentWhether allow-ping=False for this task
tag"this task", "just fired", or None

Reply-to-fork context

Replying to a message sent by a background fork starts an interactive fork that resumes from that background fork’s session. This is how context flows from a background task back into an interactive conversation. The mechanism relies on fork session tracking in sessions.py:
  1. Before a background fork runs, start_message_collector() initializes a contextvar-scoped list
  2. When streamer.py, ping_user, or discord_embed sends a Discord message, track_message(message_id) records it
  3. After the fork completes, flush_message_collector(fork_id, parent_id) writes the mapping to ~/.ollim-bot/state/fork_messages.json
  4. When a user replies to one of those messages, lookup_fork_session(message_id) returns a ForkLookup with the session ID (or None if expired/unknown) and an expired flag
  5. The bot creates an interactive fork that resumes from that session
Records in fork_messages.json have a 7-day TTL and are pruned on read. When a reply targets an expired record, the bot signals this to the user (“this session has expired — starting fresh with quoted context”) and falls back to quoting the message content instead. If the user replies to a fork message while already in an interactive fork, the reply is added as context to the current fork rather than opening a new one.
Replying to a non-fork message works differently — the replied-to message’s content is quoted and prepended as context (plain text from .content, or title + description + fields from the first embed, truncated to 500 characters).

Chain reminder context

Chain reminders inject additional context into their prompts. When a reminder has max-chain > 0, the preamble includes:
  • The current check number and total (e.g., “check 2/4”)
  • Whether follow_up_chain(minutes_from_now=N) is available
  • Instruction to acknowledge the follow-up nature in pings (e.g. “checking in again”) so the user knows it is intentional
  • On the final check: the preamble warns that follow_up_chain is not available and applies a regret heuristic — the agent pings only if the task is still unresolved and the user would regret missing it, otherwise it calls report_updates. If follow_up_chain is called anyway, the tool returns an error with recovery guidance prompting escalation via ping_user.
Chain reminders inherit tool restrictions and skills from the parent via ChainContext, so allowed-tools and skills propagate through the entire chain.

Next steps

Session management

Session IDs, lifecycle events, and compaction.

Background forks

Isolated mode, model overrides, and tool restrictions.

Forks

Interactive forks, exit strategies, and idle timeout.

Ping budget

Refill-on-read budget for background fork pinging.