# Handling Event-Triggered Automations in Agent-Native: A Technical Deep Dive

> Master event-triggered automations in Agent-Native. Learn how this technical deep dive explains incremental fetching, AI rule evaluation, and state synchronization for efficient Gmail automation.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: deep-dive
- Published: 2026-06-27

---

**Agent-Native implements event-triggered automations through a debounced execution pipeline that fetches Gmail messages incrementally, evaluates them against AI-powered rules, and executes actions like labeling or archiving while maintaining synchronization state via watermarks and processed-ID caches.**

The `BuilderIO/agent-native` repository provides a robust framework for building AI-native applications, including a sophisticated automation engine for email management within the Mail template. This system handles **event-triggered automations** by combining debounced UI triggers with cron-based scheduling, ensuring efficient processing of Gmail inbox events without duplicate handling.

## Automation Pipeline Architecture

The automation engine operates as a self-contained pipeline with multiple entry points and strict isolation of concerns.

### Trigger Mechanisms

The system supports two distinct invocation patterns:

- **Scheduled Execution** – A periodic job calls `processAutomations` at the end of the engine file, iterating over all Google-OAuth connected accounts to process pending messages.
- **Event-Triggered Execution** – UI components invoke `triggerAutomationsDebounced(ownerEmail)` (lines 30-38 of [`automation-engine.ts`](https://github.com/BuilderIO/agent-native/blob/main/automation-engine.ts)), a debounced wrapper that prevents rapid re-invocations within a short time window.

Both entry points execute within `runWithRequestContext`, ensuring per-user settings and LLM credentials remain isolated throughout the request lifecycle.

### Token Management and Gmail API Context

Before touching the Gmail API, the engine ensures valid authentication:

- **`getAccessToken`** (lines 83-122) checks token expiry and refreshes Google OAuth tokens when they are near expiration, persisting the refreshed credentials via `saveOAuthTokens`.
- All subsequent Gmail calls use this validated `accessToken`, preventing mid-stream authentication failures during batch operations.

## Message Fetching and Deduplication Strategy

The engine implements a robust two-layer deduplication system to handle incremental sync correctly.

### Watermark-Based Incremental Sync

Agent-Native uses Gmail's `historyId` mechanism to fetch only new messages since the last run:

- **`getWatermark` / `setWatermark`** (lines 27-38) retrieve and store the last seen `historyId` and timestamp from user settings.
- **`fetchNewInboxMessages`** first attempts a Gmail History request (lines 6-33). On failure, it falls back to `gmailListMessages` and updates the watermark with the latest `historyId` from the user profile (lines 41-57).

### Processed-ID Caching

To prevent action duplication within the current run window:

- **`getProcessedIds` / `saveProcessedIds`** (lines 40-55) maintain a short-lived cache of already-processed message IDs, automatically pruning entries older than seven days.
- The system limits batch size to `MAX_EMAILS_PER_RUN` (50 messages), returning only IDs absent from the processed cache.

## AI-Powered Rule Evaluation

Rule matching leverages LLM capabilities rather than simple pattern matching, enabling complex natural language conditions.

### Model Configuration and Validation

- **`getAutomationModelSettings`** (lines 53-63) reads the selected engine and model from user settings.
- **`canUseAutomationModel`** (lines 30-65) validates that the configured LLM provider is reachable before attempting evaluation, preventing wasted API calls on misconfigured accounts.

### Batch Evaluation Strategy

- **`loadActiveRules`** (lines 71-81) queries the `automationRules` table for enabled rules specific to the owner and domain.
- **`evaluateRules`** (lines 78-119) constructs a concise prompt containing all active rules and a batch of up to 10 emails, then invokes `callModel` to perform the evaluation.
- The model returns a JSON array mapping each email to its matched rule IDs, which the engine parses to determine which actions to execute.

## Action Execution Flow

Once rules match, the system delegates to specialized action handlers.

### Supported Automation Actions

For every matched rule, the engine parses the stored `actions` JSON into an array of `AutomationAction` objects. **`executeActions`** iterates over these, delegating each to **`executeAction`** (defined in [`automation-actions.ts`](https://github.com/BuilderIO/agent-native/blob/main/automation-actions.ts)). Supported actions include:

- **`label`** – Applies a Gmail label, creating it if necessary via `buildLabelCache` (lines 21-36)
- **`archive`** – Removes the message from the inbox
- **`mark_read`** – Marks the message as read
- **`star`** – Stars the message
- **`trash`** – Moves the message to trash

### Result Aggregation and Housekeeping

After action execution:
- The watermark updates to the latest `historyId`
- Processed IDs are persisted to the cache
- **`processAutomations`** aggregates results across all accounts, returning a `ProcessResult` containing total messages processed and actions executed (lines 73-89)

All usage is recorded under the "automation" label for downstream cost-tracking and observability.

## Practical Code Examples

### Defining an Automation Rule

Store this JSON schema in the `automationRules` table to create a new automation:

```json
{
  "ownerEmail": "alice@example.com",
  "domain": "mail",
  "name": "Label newsletters",
  "condition": "subject contains \"Newsletter\" OR from contains \"news@\"",
  "actions": [
    { "type": "label", "labelName": "Newsletter" },
    { "type": "archive" }
  ],
  "enabled": 1
}

```

### Triggering Automations from the UI

Use the `triggerAutomationsDebounced` hook to allow users to manually run automations:

```typescript
import { triggerAutomationsDebounced } from "@/templates/mail/actions/trigger-automations";

function useTrigger() {
  const owner = useUserEmail(); // assumes a hook returning the logged-in email
  const trigger = async () => {
    const { triggered, reason } = await triggerAutomationsDebounced(owner);
    if (!triggered) console.log("Automation run debounced:", reason);
  };
  return trigger;
}

```

### Extending Automation Actions

Add new action types by extending the `AutomationAction` union type:

```typescript
type AutomationAction =
  | { type: "label"; labelName: string }
  | { type: "archive" }
  | { type: "mark_read" }
  | { type: "star" }
  | { type: "trash" }
  // future: { type: "forward"; address: string };

```

## Summary

- **Dual trigger system** supports both cron schedules and debounced UI events via `processAutomations` and `triggerAutomationsDebounced`.
- **State management** uses Gmail `historyId` watermarks and 7-day processed-ID caches to ensure incremental sync without duplicates.
- **AI evaluation** batches up to 10 emails against active rules using configurable LLM providers, returning structured JSON match results.
- **Action execution** handles Gmail mutations (label, archive, star, trash) with automatic label creation and caches the results.
- **Request isolation** ensures per-user OAuth tokens and model settings apply correctly through `runWithRequestContext`.

## Frequently Asked Questions

### How does Agent-Native prevent duplicate automation runs?

The system implements **debouncing** through `triggerAutomationsDebounced` (lines 30-38 of [`automation-engine.ts`](https://github.com/BuilderIO/agent-native/blob/main/automation-engine.ts)), which rejects rapid successive triggers within a short window. Additionally, the **`getProcessedIds`** cache tracks message IDs already handled in recent runs, automatically pruning entries older than seven days to prevent indefinite storage growth while blocking re-processing.

### What happens when Gmail OAuth tokens expire during automation execution?

The **`getAccessToken`** function (lines 83-122) proactively checks token expiry before any Gmail API calls. If a token is near expiration, it refreshes the credentials using Google's OAuth endpoint and persists the updated tokens via `saveOAuthTokens`, ensuring the automation pipeline completes without authentication interruptions.

### How many emails can the automation engine process in a single run?

The engine respects a hard limit of **`MAX_EMAILS_PER_RUN` (50 messages)** per invocation. This batching prevents API rate limit exhaustion and ensures the AI evaluation prompt stays within context window constraints when processing up to 10 emails per batch in `evaluateRules`.

### Can I create custom automation actions beyond the built-in types?

Yes. The `AutomationAction` type in [`automation-actions.ts`](https://github.com/BuilderIO/agent-native/blob/main/automation-actions.ts) uses a discriminated union pattern that you can extend with new action variants. You must add the type definition and implement the corresponding logic in **`executeAction`** to handle the new action's Gmail API interactions, following the existing pattern for label caching and error handling.