# Event-Driven Triggers

> Running background tasks that react to external events (e.g., timers via every()) and push asynchronous messages into the agent session.

- Repository: google-antigravity/antigravity-sdk-python
- GitHub: https://github.com/google-antigravity/antigravity-sdk-python
- Human wiki: https://grok-wiki.com/public/wiki/google-antigravity-antigravity-sdk-python-2abd361a7867
- Complete Markdown: https://grok-wiki.com/public/wiki/google-antigravity-antigravity-sdk-python-2abd361a7867/llms-full.txt

## Source Files

- `google/antigravity/triggers/triggers.py`
- `google/antigravity/triggers/trigger_runner.py`
- `examples/getting_started/triggers.py`
- `google/antigravity/triggers/README.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [google/antigravity/triggers/triggers.py](google/antigravity/triggers/triggers.py)
- [google/antigravity/triggers/trigger_runner.py](google/antigravity/triggers/trigger_runner.py)
- [google/antigravity/triggers/helpers.py](google/antigravity/triggers/helpers.py)
- [google/antigravity/triggers/__init__.py](google/antigravity/triggers/__init__.py)
- [google/antigravity/connections/connection.py](google/antigravity/connections/connection.py)
- [google/antigravity/connections/local/local_connection.py](google/antigravity/connections/local/local_connection.py)
- [examples/getting_started/triggers.py](examples/getting_started/triggers.py)
- [google/antigravity/triggers/README.md](google/antigravity/triggers/README.md)
</details>

# Event-Driven Triggers

Event-Driven Triggers are long-lived, asynchronous background tasks that run alongside an agent session. Unlike **Hooks**, which are tied to specific agent lifecycle dispatch points (like `pre_turn` or `post_tool_call`), Triggers react to external stimuli—such as timers, file changes, or webhooks—and push asynchronous notifications into the active agent session.

These notifications are injected into the conversation history, allowing the agent to react to background events during the next user turn or even mid-session depending on the connection implementation. This architecture is essential for building agents that monitor systems, wait for long-running processes, or respond to real-time data feeds.

## Architecture and Control Flow

Triggers operate independently of the main agent loop. They communicate with the agent via a `TriggerContext` which holds a reference to the active `Connection`.

```mermaid
flowchart TD
    subgraph "Agent Session"
        Runner[TriggerRunner]
        AgentLoop[Agent Main Loop]
    end

    subgraph "External Events"
        Timer((Timer))
        FS[File System]
        WH[Webhook]
    end

    Timer --> T1[Trigger 1]
    FS --> T2[Trigger 2]
    WH --> T3[Trigger 3]

    T1 & T2 & T3 -.-> |async ctx.send| Runner
    Runner --> |automated_trigger| Conn[Connection]
    Conn --> AgentLoop

    style Runner fill:#f9f,stroke:#333,stroke-width:2px
    style AgentLoop fill:#bbf,stroke:#333,stroke-width:2px
```
Sources: [google/antigravity/triggers/README.md:465-475](google/antigravity/triggers/README.md#L465-L475), [google/antigravity/triggers/trigger_runner.py:142-150](google/antigravity/triggers/trigger_runner.py#L142-L150)

## Defining Triggers

A Trigger is any `async` function that accepts a `TriggerContext` as its sole argument. For better validation and discovery, developers should use the `@triggers.trigger` decorator.

### Using the @trigger Decorator
The decorator validates the function signature and marks it for the SDK's internal discovery mechanisms.

```python
from google.antigravity import triggers

@triggers.trigger
async def my_custom_trigger(ctx: triggers.TriggerContext) -> None:
    """A trigger that sends a message every 10 seconds."""
    while True:
        await asyncio.sleep(10)
        await ctx.send("Background check: System status is nominal.")
```
Sources: [google/antigravity/triggers/triggers.py:53-81](google/antigravity/triggers/triggers.py#L53-L81)

### Using Helper Factories
The SDK provides several pre-built factories for common patterns in `google.antigravity.triggers.helpers`.

| Helper | Description | Usage |
| :--- | :--- | :--- |
| `every()` | Runs a callback on a fixed interval. | `every(300, my_callback)` |
| `on_file_change()` | Monitors a path for file system events (requires `watchfiles`). | `on_file_change("./logs", my_callback)` |

Sources: [google/antigravity/triggers/helpers.py:256-340](google/antigravity/triggers/helpers.py#L256-L340)

## Lifecycle Management: TriggerRunner

The `TriggerRunner` is responsible for starting triggers as concurrent `asyncio.Task` instances and ensuring they are cleanly cancelled when the session ends.

- **Isolation**: Each trigger runs in its own task. An unhandled exception in one trigger will be logged but will not affect the agent session or other triggers.
- **Cleanup**: Triggers are expected to handle `asyncio.CancelledError` if they need to perform specific cleanup (e.g., closing sockets or file handles).
- **Session Lifecycle**: Triggers are typically started when the `Agent` session enters an `async with` block and stopped upon exit.

```python
# Internal lifecycle in TriggerRunner
async def start(self) -> None:
    for trigger in self._triggers:
        ctx = TriggerContext(connection=self._connection)
        task = asyncio.create_task(self._run_trigger(trigger, ctx))
        self._tasks.append(task)
```
Sources: [google/antigravity/triggers/trigger_runner.py:130-150](google/antigravity/triggers/trigger_runner.py#L130-L150), [google/antigravity/triggers/trigger_runner.py:209-217](google/antigravity/triggers/trigger_runner.py#L209-L217)

## Pushing Messages to the Session

The `TriggerContext.send(content)` method is the primary way triggers communicate. Under the hood, this calls `connection.send_trigger_notification(content)`.

In the **Local Connection** implementation, these notifications are sent as `automated_trigger` fields within an `InputEvent` proto. This allows the backend to treat the notification as a special system-initiated message that the agent can "see" without requiring a direct user prompt.

```python
# google/antigravity/connections/local/local_connection.py
async def send_trigger_notification(self, content: str) -> None:
    """Sends a trigger message to the agent."""
    event = localharness_pb2.InputEvent(automated_trigger=content)
    await self._ws.send(json_format.MessageToJson(event))
```
Sources: [google/antigravity/triggers/triggers.py:46-50](google/antigravity/triggers/triggers.py#L46-L50), [google/antigravity/connections/local/local_connection.py:1280-1283](google/antigravity/connections/local/local_connection.py#L1280-L1283)

## Example: Monitoring a Pipeline

In this example, a custom trigger simulates a webhook listener for a CI/CD pipeline.

```python
from google.antigravity import Agent, LocalAgentConfig, triggers

@triggers.trigger
async def webhook_listener(ctx: triggers.TriggerContext):
    # Simulate waiting for a webhook event
    await asyncio.sleep(5) 
    await ctx.send("[WEBHOOK ALERT] Pipeline 'Build-123' failed on main.")

config = LocalAgentConfig(
    system_instructions="You monitor CI/CD pipelines.",
    triggers=[webhook_listener]
)

async with Agent(config) as agent:
    # Triggers start here
    await agent.chat("Let me know if any builds fail.")
    # ... later ...
    response = await agent.chat("Any updates?")
    print(await response.text()) # Agent will report the failure found in history
```
Sources: [examples/getting_started/triggers.py:144-174](examples/getting_started/triggers.py#L144-L174)

Triggers provide a robust way to bridge the gap between external real-time events and the agent's conversational session, ensuring the agent remains informed of background changes without blocking user interaction.
Sources: [google/antigravity/triggers/README.md:461-463](google/antigravity/triggers/README.md#L461-L463)
