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

# Automation & scheduled tasks

> Scheduled agent jobs: isolated sessions per run, catch-up on missed triggers, the unattended permission tier and its fallback behaviour, and webhook notifications.

Automations let the agent work on a schedule: a daily standup digest, a weekly cleanup, a
check script on the hour. Scheduling runs on [croner](https://github.com/Hexagon/croner)
with file locking and crash recovery.

<Note>
  The scheduler is vendored from `@amaster.ai/pi-task-scheduler` (Apache-2.0). A few
  things were changed locally, one of which affects behaviour: the scheduler lifecycle
  is owned by the sidecar itself, and the scheduler tools' scope was widened from
  "the current session" to **app level** — automations are an app-level asset, visible
  across sessions.
</Note>

## Execution shape

A few deliberate decisions, all serving "do not interrupt what I am doing":

<Columns cols={2}>
  <Card title="Each trigger gets its own session">
    Results stay in that session, reviewable later, never disturbing the conversation you
    are in. Materialised sessions appear in the sidebar automatically with a ⚡ badge.
  </Card>

  <Card title="Missed triggers catch up merged">
    When the app reopens, missed triggers **run once, merged** — not once per miss. A
    daily report skipped for three days does not run three times.
  </Card>

  <Card title="Notifications land in the session">
    Clicking a completion notification drops you directly into that run's session, with an
    automatic fallback to the JS plugin path if the native call fails.
  </Card>

  <Card title="Guardrails">
    At most 2 concurrent runs and 30 tasks; queued triggers show up in the run history.
  </Card>
</Columns>

### What "its own session" means concretely

Each run is a brand-new session identified as
`automation:<taskId>:<historyEntryId>`. Consequently:

* It takes the **create-session** branch rather than the resume branch (an explicit id
  would be validated as an existing stream session and fail with "session not found")
* The session is persisted with its transcript JSONL, so it is **fully reviewable later**
* The real session id is recorded on the task's history entry, used for notification
  deep links

<Note>
  **The prompt delivered is exactly the text you wrote — no prefix, no suffix.**
  Unattended constraints are enforced by the permission tier, not by wrapping the prompt.
  Wrapping would instead make the model work inside a frame it should not have to care
  about.
</Note>

## Three ways to create one

1. **The form** — sidebar "Automations" → a full management view: task cards, a creation
   dialog, run history;
2. **Ask the agent in conversation** — the sidecar registers scheduler tools, so the agent
   can create tasks for you;
3. **Task templates** — pick one from the template picker and adjust it.

The editor's "instruction" row is followed by three composer-style capsules — **working
directory, permission tier, model**. A new task's directory defaults to the current
workspace.

### Built-in templates

| Template | What it does |
| - | - |
| Daily briefing | Summarise today's schedule and to-do highlights each morning |
| Weekly report draft | Review the week and draft a report on Friday evening |
| Daily repo checkup | Run tests and a build, summarise failures (needs a working directory) |
| Periodic information scan | Search a topic for recent developments every 6 hours |
| Remind me later | A one-off task that runs a day from now |

## The unattended permission tier

When there is nobody to ask, "ask" is not an option — approval has to be a hard decision.

Scheduled tasks use their **own** permission axis (`toolPolicyProfile`), three tiers,
**defaulting to the tightest**:

| Tier | Behaviour |
| - | - |
| `read-only` (default) | Every side-effecting tool that would need approval (`bash` / `write` / `edit` / config tools) is **denied outright** |
| `workspace-write` | `write` / `edit` pass only **inside the workspace or the machine-local writable-root list** — anything beyond that is denied on the spot; `bash`, MCP and the config tools (subagent / skill / theme / plugin management) are denied |
| `full` | No restriction. Pick it only when the task genuinely needs it |

**An explicit grant is not overridden by the tier**: `allowCommands` (command word
prefixes) and `allowMcpTools` (exact tool names) are standing authorizations, evaluated
before the tiers — the same allow-list must not mean opposite things on the "I run it
myself" path and the "it runs on a schedule" path.

### What happens when a tool is denied

A denial does not deadlock the task or trigger endless retries. The scheduler tells the
model:

> Do not retry this tool; finish the task with allowed read-only operations.

So a `read-only` daily report that hits `bash` will assemble the report by reading
files instead, rather than retrying until it times out.

<Warning>
  This shares its vocabulary with "permission levels", **and the two now decide the same
  way**:

  * **"Auto within workspace"** (permission level) decides **by path** — it tells whether
    the target file is inside the workspace or the writable-root list, and asks when it
    cannot tell
  * **"Writable workspace"** (automation tier) **also decides by path** — `write`/`edit`
    pass only inside the workspace or the writable-root list; anything beyond that, plus
    `bash`, MCP and the config tools, is denied

  The only difference is that nobody can be asked, so "would ask" becomes "denies". Both
  use the same capsule styling (the automation editor deliberately reuses
  ModePicker's form), so judging by name will mislead you. See
  [Modes overview](/en/features/modes).
</Warning>

## Webhooks and notifications

Task events (fired, completed, failed) can be pushed to any service — the
`automation.*` entries in the event registry show up automatically in the webhook
settings dropdown.

Completion can also go out as a system notification; clicking it lands in that run's
session.

`maxConcurrentRuns` (default 2) and `maxTasks` (default 30) are locally added
guardrails so the scheduler cannot run away.

## Run history

The "Run history" tab aggregates across tasks, groups by day, and is searchable. Queued
triggers appear here too — so when a task "didn't run", the first place to look is the
run history, not the task card.

## Management UI

<CardGroup cols={3}>
  <Card title="Task cards">Header row: run now / edit / history; ⋯ holds only delete</Card>
  <Card title="Two tabs">"Tasks | Run history"; history aggregates across tasks, grouped by day, searchable</Card>
  <Card title="Bulk management">Card checkbox / select-all, bulk enable / pause / delete (two-step delete confirmation)</Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="Modes overview" icon="sliders" href="/en/features/modes">
    The difference between the two "tier" axes and what each means.
  </Card>

  <Card title="Remote access & mobile" icon="phone" href="/en/features/remote-mobile">
    Check on these jobs while you are away.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.