How the Craft Agents Automation System Triggers Actions Based on Events

The Craft Agents automation system uses a workspace-isolated event bus and declarative JSON configuration to match incoming events against regex or cron patterns, evaluate conditional logic, and dispatch actions through specialized handlers for prompts, webhooks, and logging.

The Craft Agents open-source repository implements a reactive automation pipeline that transforms workspace events into executable actions. At its core, the system combines a lightweight event bus with a JSON-driven configuration schema to enable event-driven workflows without global mutable state. The entire engine is contained in the @craft-agents/shared package and centers around the AutomationSystem class.

Event Bus Architecture and Workspace Isolation

Every AutomationSystem instance creates its own WorkspaceEventBus to ensure strict isolation between workspaces. This design prevents cross-workspace event leakage and eliminates global mutable state.

In packages/shared/src/automations/automation-system.ts (lines 65-68), the constructor initializes the bus:

this.eventBus = new WorkspaceEventBus(options.workspaceId);

Handlers subscribe to this bus using handler.subscribe(this.eventBus), receiving only events emitted within their specific workspace context.

Configuration Loading and Validation

The system reads automation rules from a workspace-specific automations.json file. During initialization, AutomationSystem validates the configuration, back-fills missing matcher IDs, and compacts the history file.

According to the source code in packages/shared/src/automations/automation-system.ts (lines 14-27), the constructor handles configuration loading:

const config = await loadOrCreateConfig(configPath);
this.configProvider = new ConfigProvider(config);

The JSON structure maps event names to arrays of matchers, each containing conditions and action definitions.

Event Matching Logic

When eventBus.emit(eventName, payload) fires, handlers retrieve matching rules via configProvider.getMatchersForEvent(event). The system evaluates matches through two primary functions:

  • matcherMatches – for app events
  • matcherMatchesSdk – for agent events

Both functions check the enabled flag, apply regex patterns (matcher.matcher) or cron expressions (matcher.cron), and evaluate optional conditions via evaluateConditions.

In packages/shared/src/automations/utils.ts (lines 64-82), the condition evaluator processes time-of-day restrictions, state changes, and logical combinations before allowing the action to proceed.

Handler Types and Action Execution

The system registers three core handlers during initialization:

  1. PromptHandler – Processes prompt actions by building sandboxed environments
  2. WebhookHandler – Executes HTTP calls for webhook actions
  3. EventLogHandler – Persists events to a history file

PromptHandler Implementation

When processing prompt actions, PromptHandler constructs a sandboxed environment using buildEnvFromPayload and expands ${VAR} placeholders within the prompt text. It parses @mentions via parsePromptReferences and assembles PendingPrompt objects.

As implemented in packages/shared/src/automations/handlers/prompt-handler.ts (lines 47-62), the handler delivers ready-to-run prompts through the onPromptsReady callback supplied during system initialization.

WebhookHandler and Security

WebhookHandler follows a similar flow but uses buildWebhookEnv to expose only CRAFT_WH_* secrets, ensuring sensitive credentials remain scoped to webhook contexts.

Scheduler Integration

For time-based automation, the SchedulerService periodically emits SchedulerTick events when enableScheduler is true. The system evaluates cron-based matchers against the current time using matchesCron.

In packages/shared/src/automations/utils.ts (lines 52-58), the cron matcher determines whether the current minute matches the configured schedule, enabling cron-based automation rules.

Lifecycle Management

The AutomationSystem provides explicit cleanup through its dispose() method. This stops the scheduler service, disposes all registered handlers, and clears the internal metadata cache.

According to the source in packages/shared/src/automations/automation-system.ts (lines 140-155), proper disposal ensures no memory leaks occur when workspace instances are destroyed.

Implementation Example

Creating an automation system requires configuring the workspace root and event callbacks:

import { AutomationSystem } from '@craft-agents/shared/automations/automation-system';

const system = new AutomationSystem({
  workspaceRootPath: '/my/workspace',
  workspaceId: 'ws-123',
  enableScheduler: true,
  onPromptsReady: (prompts) => {
    console.log('Ready prompts:', prompts);
  },
});

Configure automations via automations.json:

{
  "automations": {
    "LabelAdd": [
      {
        "id": "a1b2c3",
        "name": "Welcome new label",
        "matcher": "^welcome$",
        "actions": [
          {
            "type": "prompt",
            "prompt": "A new label \"${label}\" was added – should I create a session?",
            "labels": ["auto"],
            "permissionMode": "read"
          }
        ]
      }
    ]
  }
}

Emit events from UI components or services:

system.eventBus.emit('LabelAdd', {
  workspaceId: 'ws-123',
  sessionId: 'sess-456',
  label: 'welcome',
  timestamp: Date.now(),
});

Summary

  • The WorkspaceEventBus provides isolated event streaming per workspace, preventing cross-contamination between tenant contexts.
  • Matcher functions (matcherMatches, matcherMatchesSdk) evaluate regex patterns, cron schedules, and conditional logic before triggering actions.
  • Three specialized handlers process matched events: PromptHandler for AI prompts, WebhookHandler for HTTP callbacks, and EventLogHandler for audit trails.
  • The system uses sandboxed environments (buildEnvFromPayload) to safely expand variables and references within automated prompts.
  • Scheduler integration enables cron-based automation through SchedulerTick events evaluated against automations.json configurations.

Frequently Asked Questions

What file format does Craft Agents use for automation configuration?

The system uses a JSON file named automations.json located in the workspace root. This declarative configuration maps event names to arrays of matcher objects, each containing regex patterns or cron schedules, conditions, and action definitions. The AutomationSystem validates this file on startup and back-fills missing matcher IDs automatically.

How does the system prevent cross-workspace event leakage?

Each workspace receives its own WorkspaceEventBus instance created with a unique workspaceId. Handlers subscribe only to their specific bus instance, ensuring events emitted in one workspace never propagate to handlers in another. This architecture eliminates global mutable state and enforces strict tenant isolation.

What types of conditions can be evaluated before triggering an action?

The evaluateConditions function supports time-of-day restrictions, state change detection, and logical combinations of multiple criteria. These conditions execute after pattern matching (regex or cron) but before action dispatch, allowing fine-grained control over when automations fire.

How does the scheduler trigger automated actions?

When enableScheduler is true, the SchedulerService emits SchedulerTick events at regular intervals. The system evaluates these ticks against cron expressions in automations.json using matchesCron. When the current time matches a configured schedule, the system executes the associated actions through the standard handler pipeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →