# How AionUi's CronService Schedules and Executes AI Tasks Securely with CronBusyGuard

> Learn how AionUi's CronService securely schedules and executes AI tasks using CronBusyGuard for idle conversation targeting, preventing race conditions and duplicate responses. Optimize your AI workflows with robust task manage...

- Repository: [OfficeAI/AionUi](https://github.com/iofficeai/aionui)
- Tags: how-to-guide
- Published: 2026-02-19

---

**AionUi's CronService uses a combination of SQLite-backed job persistence, multiple timer strategies, and the CronBusyGuard state tracker to ensure AI tasks execute only when target conversations are idle, preventing race conditions and duplicate responses.**

AionUi is an open-source AI desktop application that automates agent interactions using scheduled tasks. The `CronService` in [`src/process/services/cron/CronService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronService.ts) provides the core scheduling engine, while `CronBusyGuard` in [`src/process/services/cron/CronBusyGuard.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronBusyGuard.ts) acts as a safety mechanism to block execution during active conversations. Together, they enable reliable, non-intrusive automation of Claude, Gemini, and Codex agents.

## CronService Architecture and Lifecycle

The `CronService` initializes by loading enabled jobs from the SQLite-backed `CronStore` and activating their timers. During shutdown or job deletion, it cancels these timers and persists the final state.

In `CronService.init()`, the service queries all enabled jobs and invokes `startTimer(job)` for each entry. This method, located at lines 49-62 in [`src/process/services/cron/CronService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronService.ts), ensures that scheduled tasks resume correctly after application restarts.

## Scheduling Jobs and Timer Types

Jobs are created via `cronService.addJob(params)`, defined at lines 73-106. This method enforces a **one-job-per-conversation rule** (lines 74-80) to prevent scheduling conflicts, computes the first `nextRunAtMs` using `updateNextRunTime(job)`, persists the job to `CronStore`, and starts the appropriate timer.

The `startTimer(job)` method (lines 121-127) supports three scheduling strategies:

### Cron Expressions

For `kind: 'cron'`, the service uses the **croner** library to parse expressions like `0 9 * * *`. The implementation at lines 200-216 stores the next run time via `timer.nextRun()` and updates `state.nextRunAtMs` at lines 178-181.

### Fixed Intervals

For `kind: 'every'`, the service uses `setInterval` (lines 226-232), calculating the next run as `Date.now() + everyMs`.

### One-Off Delays

For `kind: 'at'`, the service uses `setTimeout` with the delay computed from `schedule.atMs` (lines 240-259).

## Preventing Race Conditions with CronBusyGuard

The **CronBusyGuard** ensures that scheduled AI tasks do not interrupt ongoing conversations. Located in [`src/process/services/cron/CronBusyGuard.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronBusyGuard.ts), it maintains a `Map<string, ConversationState>` where each state tracks `isProcessing` and the last activity timestamp (lines 10-13).

### Tracking Conversation State

When a conversation starts processing a message, the `MessageMiddleware` or renderer process calls `cronBusyGuard.setProcessing(conversationId, true)`. Upon completion, it sets the flag to `false`. This real-time tracking prevents the cron service from injecting automated messages while a user is actively interacting with the agent.

### Execution Guard and Retry Logic

Before executing a job, `CronService.executeJob(job)` checks `cronBusyGuard.isProcessing(conversationId)` (lines 95-98). If the conversation is busy:

1. The job increments its `retryCount`.
2. If `retryCount` exceeds `maxRetries` (default 3), the job is **skipped**, logs a `skipped` status, and schedules the next run (lines 99-108).
3. Otherwise, the service registers a 30-second back-off timer using `setTimeout` (lines 111-117).

This mechanism ensures that automated tasks yield to user-initiated interactions while maintaining schedule integrity through intelligent retry policies.

## Executing AI Tasks

When the conversation is idle, `executeJob` proceeds through the following steps (lines 88-101):

1. **Task Retrieval**: Obtains the AI agent task via `WorkerManage`.
2. **Yolo Mode**: Forces `yoloMode: true` to bypass interactive approval dialogs (lines 30-33).
3. **File Handling**: Calls `copyFilesToDirectory` with an empty array for cron jobs (lines 83-86).
4. **Message Dispatch**: Invokes `task.sendMessage` with the appropriate payload (`content` for Codex/ACP, `input` for Gemini) (lines 88-92).
5. **State Update**: Sets `lastStatus = 'ok'`, resets `retryCount`, and updates the conversation's `modifyTime` to bubble it to the top of the UI (lines 94-101).

Errors during execution set `lastStatus = 'error'` and store the error message before proceeding to the next scheduled run.

## System Resilience and Power Management

### Preventing OS Suspension

While any job is enabled, `CronService` activates Electron's `powerSaveBlocker` in `prevent-app-suspension` mode (lines 332-341). This prevents the operating system from suspending the application while tasks are pending, without keeping the display awake. When no jobs remain enabled, the blocker is stopped to conserve battery.

### Handling System Resume

When the system wakes from sleep, `handleSystemResume()` (lines 559-587) iterates over all enabled jobs:

1. Stops stale timers.
2. Detects missed executions where `nextRunAtMs <= now`.
3. Logs warnings and inserts a UI **tips** message via `addMessage`, emitting `ipcBridge.conversation.responseStream` to notify the user instantly.
4. Restarts timers for future executions.

This ensures users are informed of any missed AI tasks due to system sleep, maintaining transparency in automated workflows.

## Practical Code Examples

### Adding a Daily Cron Job

```typescript
import { cronService } from '@process/services/cron/CronService';
import type { CronSchedule } from '@process/services/cron/CronStore';

const dailyCron: CronSchedule = {
  kind: 'cron',
  expr: '0 9 * * *',      // 09:00 every day
  tz: 'Asia/Shanghai',
};

await cronService.addJob({
  name: 'Daily Summary',
  schedule: dailyCron,
  message: 'Please generate the daily summary.',
  conversationId: 'conv_12345',
  agentType: 'codex',
  createdBy: 'user',
});

```

### Manually Guarding a Conversation

```typescript
import { cronBusyGuard } from '@process/services/cron/CronBusyGuard';

function onConversationStart(id: string) {
  cronBusyGuard.setProcessing(id, true);
}

function onConversationEnd(id: string) {
  cronBusyGuard.setProcessing(id, false);
}

```

### Listening for Missed Job Notifications

```typescript
ipcBridge.conversation.responseStream.on('data', (msg) => {
  if (msg.type === 'tips' && msg.data.type === 'warning') {
    // Show a toast or banner with msg.data.content
  }
});

```

## Summary

- **CronService** in [`src/process/services/cron/CronService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronService.ts) provides the core scheduling engine for AionUi, supporting cron expressions, fixed intervals, and one-off delays via SQLite persistence.
- **CronBusyGuard** in [`src/process/services/cron/CronBusyGuard.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronBusyGuard.ts) prevents race conditions by tracking per-conversation processing states, ensuring scheduled tasks only run when conversations are idle.
- The service implements a **retry policy** with a 30-second back-off and maximum 3 attempts before skipping a run, preventing infinite loops on busy conversations.
- **Power management** features use Electron's `powerSaveBlocker` to prevent OS suspension during scheduled tasks, while `handleSystemResume` detects and notifies users of missed executions after system sleep.
- Jobs are created via `addJob()`, which enforces one-job-per-conversation rules and automatically starts the appropriate timer using `croner`, `setInterval`, or `setTimeout`.

## Frequently Asked Questions

### How does CronBusyGuard prevent duplicate AI responses?

CronBusyGuard maintains a `Map` of conversation states in [`src/process/services/cron/CronBusyGuard.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronBusyGuard.ts). When a conversation starts processing any message, `setProcessing(conversationId, true)` marks it as busy. Before executing a scheduled job, `CronService.executeJob()` calls `isProcessing()` to check this state. If busy, the job defers execution using a 30-second retry timer, preventing the cron task from interrupting active conversations or causing duplicate responses.

### What happens to scheduled tasks when my computer goes to sleep?

When the system resumes, `CronService.handleSystemResume()` (lines 559-587) iterates through all enabled jobs, stops stale timers, and compares `nextRunAtMs` against the current time. If a job was missed during sleep, it logs a warning and inserts a UI tip message via `addMessage`, emitting `ipcBridge.conversation.responseStream` to notify the user immediately. The service then restarts timers for future executions, ensuring no silent failures occur due to system sleep.

### Can I schedule multiple cron jobs for the same conversation?

No. The `addJob()` method in [`src/process/services/cron/CronService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronService.ts) enforces a one-job-per-conversation rule at lines 74-80. If you attempt to create a job for a `conversationId` that already has an active scheduled task, the service throws an error. This design prevents scheduling conflicts and ensures that `CronBusyGuard` can accurately track conversation states without ambiguity from multiple concurrent automated workflows.

### What is "yolo mode" and why do cron jobs use it?

**Yolo mode** is an execution flag that bypasses interactive approval dialogs for AI agents. In [`src/process/services/cron/CronService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/process/services/cron/CronService.ts) (lines 30-33), cron jobs automatically set `yoloMode: true` when retrieving tasks via `WorkerManage`. This ensures that scheduled AI tasks execute without requiring user interaction, which is essential for unattended automation. The flag allows the cron service to send messages to Claude, Gemini, or Codex agents automatically while maintaining security through the `CronBusyGuard` rather than manual approvals.