> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ollim.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Background forks

> Configure disposable agent sessions for routines and reminders with model overrides and tool restrictions.

Background forks run agent tasks on disposable sessions that don't pollute
your main conversation. Routines with `background: true` and all
reminders (which default to background) execute this way — the agent
does its work, optionally pings you or reports findings, and the fork
is discarded.

## Overview

When a background routine or reminder fires, the bot creates
a temporary session. The agent's text output is discarded — it
communicates only through notifications, embeds, and reporting to
the main session. After the task completes (or times out — default
30 minutes, configurable via [`/config bg_fork_timeout`](/core-usage/slash-commands#config)),
the session is thrown away.

Background forks run concurrently with your main conversation and
with other background forks — they never interrupt what you're doing.

<Note>
  Each background fork can send at most 1 non-critical notification.
  When you're mid-conversation, non-critical notifications are blocked
  and the agent reports findings to the main session instead.
  `critical: true` bypasses all limits — per-session cap, busy state,
  and ping budget.
</Note>

## Execution modes

Background forks support two execution modes that determine how the
agent session is created.

<Tabs>
  <Tab title="Forked (default)">
    The default mode branches from your current main session. The
    agent has access to your full conversation history and context.

    ```yaml title="routines/daily-review.md" theme={null}
    ---
    id: daily-review
    cron: "0 9 * * 1-5"
    background: true
    description: Morning task review
    ---
    Check my Google Tasks and calendar for today.
    Ping me with a summary if there's anything actionable.
    ```

    Forked mode is best when the task needs conversational context —
    the agent knows what you've been working on and can make informed
    decisions.
  </Tab>

  <Tab title="Isolated">
    Setting `isolated: true` starts a fresh session with no
    conversation history.

    ```yaml title="routines/email-triage.md" theme={null}
    ---
    id: email-triage
    cron: "0 8 * * 1-5"
    background: true
    isolated: true
    model: haiku
    thinking: false
    description: Morning email triage
    ---
    Check Gmail for unread messages.
    Send an embed if anything needs attention.
    ```

    Isolated mode is best for self-contained tasks that don't need
    prior context — email triage, calendar checks, or data lookups.
    Using `model: haiku` and `thinking: false` reduces cost for
    simple tasks.
  </Tab>
</Tabs>

| Field      | Default | Description                                              |
| ---------- | ------- | -------------------------------------------------------- |
| `isolated` | `false` | No conversation history instead of forking               |
| `model`    | —       | Model override (works in both forked and isolated modes) |
| `thinking` | `true`  | Extended thinking override                               |

## Communication

Background forks have two output channels: **pinging** (sending a visible
Discord message) and **reporting** (queueing a summary for the main
session). These are controlled independently.

### Pinging

The agent's text output is discarded. To reach you, it must call
`ping_user` (plain text) or `discord_embed` (rich embed). These calls
are subject to the [ping budget](/scheduling/ping-budget) — each
non-critical ping consumes one token from the budget.

`ping_user` is **background-fork-only**; `discord_embed` is
**available in all contexts**. See
[Discord tools](/extending/mcp-tools#output-tools) for full parameter
reference.

Set `allow-ping: false` to disable both tools entirely. Unlike the
ping budget, this is absolute — `critical: true` does **not** bypass
it. Use this for tasks that should never produce visible output.

### Reporting to the main session

The `report_updates` tool queues a short summary to
`~/.ollim-bot/state/pending_updates.json`. These updates are prepended to the
next main session interaction, giving the agent context about what
happened in the background.

The `update-main-session` field controls when and whether the agent
must report:

| Mode      | Behavior                           | Enforcement                                        |
| --------- | ---------------------------------- | -------------------------------------------------- |
| `on_ping` | Report if a ping or embed was sent | Task blocked until reported                        |
| `always`  | Must report regardless             | Task blocked until reported                        |
| `freely`  | Reporting is optional              | None                                               |
| `blocked` | Reporting disabled                 | `report_updates` and `follow_up_chain` unavailable |

<Tip>
  Use `update-main-session: always` for tasks where the main session needs
  the outcome regardless of whether a ping was sent — like checking if a
  deadline passed.
</Tip>

When `update-main-session` is `blocked` and `allow-ping` is `true`, the
agent is told: no summary is passed to the main session (the main
conversation won't know this task ran), but you can still ping the user
directly on Discord for time-sensitive items. This combination is useful
for tasks that should notify the user when needed but don't need to feed
context back to the main session.

## Permission mode

Background forks always run with `permission_mode="default"` — they
never inherit the main session's permission mode. Even if you've set
`bypassPermissions` or `acceptEdits` on the main session, background
forks are unaffected. This ensures tool gating and **ping budget**
enforcement can't be bypassed by background tasks. See
[Permissions](/core-usage/permissions#background-forks) for the full
permission model.

## Tool restrictions

Background forks have limited tool access for safety — the agent can
only use a set of default tools plus any you explicitly add.

| Field           | Default       | Description                               |
| --------------- | ------------- | ----------------------------------------- |
| `allowed-tools` | Default tools | Additional tools merged with the defaults |

### Default tools

Every background fork can read documentation files and run `ollim-bot`
CLI commands. These tools are always available:

* `Bash(ollim-bot help)`
* `Bash(ollim-bot tasks *)`
* `Read(./**.md)`
* `Glob(./**.md)`
* `Grep(./**.md)`
* `mcp__discord__add_reminder`
* `mcp__discord__list_reminders`
* `mcp__discord__cancel_reminder`

Pinging and reporting tools (`ping_user`, `discord_embed`,
`report_updates`, `follow_up_chain`) are controlled separately by the
job's `allow-ping` and `update-main-session` flags — see
[Communication](#communication) above and
[Permissions](/core-usage/permissions#background-forks) for details.

### Adding tools

List additional tools in `allowed-tools` using the same format as the
defaults above. You only need to list tools beyond the defaults —
anything already in the default list is ignored:

```yaml title="routines/email-check.md" theme={null}
---
id: email-check
cron: "0 8,12 * * 1-5"
background: true
isolated: true
model: haiku
allowed-tools:
  - "Bash(ollim-bot gmail *)"
description: Email check (restricted tools)
---
Check Gmail for unread messages from today.
Send an embed summary if anything needs attention.
```

<Warning>
  Don't list Discord tools — `ping_user`, `discord_embed`, `report_updates`, or `follow_up_chain` — in `allowed-tools`. The bot strips them automatically and controls their availability through `allow-ping` and `update-main-session` instead.
</Warning>

Chain reminders inherit tool restrictions — `follow_up_chain`
propagates `allowed-tools` and `skills` to child reminders automatically.

### Pattern validation

The bot validates `allowed-tools` patterns at startup and again when
each job fires. Unsafe patterns are rejected and the job is skipped:

| Pattern                              | Result                                                        |
| ------------------------------------ | ------------------------------------------------------------- |
| `Bash(*)`                            | Error — too broad, specify a command prefix                   |
| `Bash(ollim-bot gmail * ; rm -rf /)` | Error — command chaining blocked                              |
| `Edit(**)`                           | Error — matches paths inside the protected `state/` directory |

If a pattern fails validation, the bot logs the error so you can fix
it.

## Reflections

After every background fork finishes, the bot spawns a small Haiku
meta-fork that writes a short execution trace to disk. Reflections are
a per-job history you can scroll through when debugging.

<Note>
  Reflections are an internal log, not telemetry. Files stay in your
  data directory — nothing is sent externally.
</Note>

### Where traces are written

Each trace lands in a per-job subdirectory under the data directory:

```text theme={null}
~/.ollim-bot/reflections/<item-id>/<timestamp>.md
```

`<item-id>` is the routine, reminder, or webhook `id`. `<timestamp>`
is UTC in `YYYY-MM-DDTHH-MM-SSZ` format — hyphens replace colons so
the filename is safe on every filesystem. One file per background
fork run.

### What a trace contains

The reflection prompt pre-fills the deterministic fields in Python;
Haiku only generates the narrative **Trace** line:

| Field          | Source                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------- |
| `Status`       | `completed`, `timed out after N minutes`, or `failed: <error>` — from the fork outcome                  |
| `Report filed` | `yes` if the fork called `report_updates`, otherwise `no`                                               |
| `Errors`       | The exception type and message, or `none`                                                               |
| `Trace`        | 1-3 factual sentences from Haiku — what the task was meant to do and what the status indicates happened |

The reflection fork receives the job's `description`, the prompt tag
(e.g. `[routine-bg:morning-checkin]`), and the `report_updates`
message if one was sent. It runs isolated with only a scoped
`Write(reflections/**/*.md)` tool.

### Timing and failure handling

The reflection fork runs in the parent fork's `finally` block, after
contextvars reset. It has a 60-second timeout. Failures — including
cancellation — are logged and swallowed so they cannot affect the
original job.

### Opting out

Reflections are enabled by default. Set `reflect: false` in a routine,
reminder, or webhook's YAML frontmatter to skip the meta-fork for that
job:

```yaml title="routines/low-signal-check.md" theme={null}
---
id: low-signal-check
cron: "*/15 * * * *"
background: true
isolated: true
reflect: false
description: High-frequency check — no trace needed
---
Quick status check.
```

There is no global toggle — opt out per job.

## Configuration

All background fork fields available in routine and reminder YAML
frontmatter:

| Field                 | Type           | Default                                | Description                                                     |
| --------------------- | -------------- | -------------------------------------- | --------------------------------------------------------------- |
| `background`          | `bool`         | `false` (routines), `true` (reminders) | Run as a background fork                                        |
| `isolated`            | `bool`         | `false`                                | Fresh session, no history                                       |
| `model`               | `string`       | —                                      | Model override                                                  |
| `thinking`            | `bool`         | `true`                                 | Extended thinking override                                      |
| `update-main-session` | `string`       | `on_ping`                              | `always`, `on_ping`, `freely`, `blocked`                        |
| `allow-ping`          | `bool`         | `true`                                 | Allow pings. `critical` does not bypass.                        |
| `allowed-tools`       | `list[string]` | —                                      | Additional tools merged with [default tools](#default-tools)    |
| `skills`              | `list[string]` | —                                      | [Skills](/extending/skills) to load at fire time                |
| `reflect`             | `bool`         | `true`                                 | Write a [reflection trace](#reflections) when the fork finishes |

## Examples

A fully configured background routine with all options:

```yaml title="routines/accountability-checkin.md" theme={null}
---
id: accountability-checkin
cron: "30 14 * * 1-5"
background: true
isolated: true
model: haiku
thinking: false
update-main-session: always
allow-ping: true
description: Afternoon accountability check-in
---
Check my Google Tasks for items due today.
If anything is overdue or due within 2 hours, ping me.
Always report what you found.
```

A silent background routine that only reports to the main session:

```yaml title="routines/quiet-sync.md" theme={null}
---
id: quiet-sync
cron: "0 7 * * *"
background: true
allow-ping: false
update-main-session: always
description: Silent morning context sync
---
Check my calendar and tasks for today.
Report a summary to the main session — do not ping.
```

A background reminder with chain follow-ups:

```yaml title="reminders/take-medication.md" theme={null}
---
id: take-medication
run-at: "2026-02-24T09:00:00-08:00"
max-chain: 3
update-main-session: freely
description: Medication reminder
---
Remind me to take my medication.
If I haven't acknowledged, schedule a follow-up in 30 minutes.
```

## Next steps

<Columns cols={2}>
  <Card title="Ping budget" icon="coins" href="/scheduling/ping-budget">
    How the refill-on-read budget controls background fork pinging.
  </Card>

  <Card title="Routines" icon="clock" href="/scheduling/routines">
    Recurring cron-scheduled routines that trigger background forks.
  </Card>

  <Card title="Reminders" icon="bell" href="/scheduling/reminders">
    One-shot and chainable reminders with follow-up chains.
  </Card>

  <Card title="Real-world examples" icon="lightbulb" href="/scheduling/examples">
    Background routine examples with conditional silence,
    model overrides, and no-ping patterns.
  </Card>
</Columns>
