gadget/docs/gadget-tasks.md
Rob Colbert 4d8f403d78 docs: add gadget-tasks documentation, update README and agent-toolbox
- README.md: Added gadget-tasks and @gadget/ai-toolbox to projects table,
  added Scheduled Tasks architecture section with diagram, updated
  monorepo structure, added gadget-tasks to dev server instructions,
  added doc link

- docs/agent-toolbox.md: Updated to reflect @gadget/ai-toolbox extraction
  from @gadget/ai. Changed IAiEnvironment → GadgetToolboxEnvironment,
  AiTool → GadgetTool, updated import paths, config examples, and added
  Tool Categories section. Clarified gadget-tasks is NOT a consumer.

- docs/gadget-tasks.md: New documentation covering architecture, per-task
  execution flow, configuration, startup/shutdown sequences, concurrency,
  work order tracking, heartbeat, error recovery, and getting started.
2026-05-17 01:30:35 -04:00

9.6 KiB
Raw Blame History

gadget-tasks Documentation

Overview

gadget-tasks is the Gadget Code scheduled task worker — a headless IDE client that automates the browser IDE flow on a cron schedule. It drives the gadget-code platform via REST API and Socket.IO, using the exact same protocol the browser IDE uses, but without any UI.

Key design principle: gadget-tasks contains zero duplicated code. No Mongoose models, no AI API calls, no prompt templates, no workspace management. All of that flows through the gadget-code platform, which delegates to drones for execution.

Architecture

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  gadget-tasks    │────▶│   gadget-code    │────▶│   gadget-drone   │
│  (Headless IDE)  │◀────│   (Platform)     │◀────│   (AWL Worker)   │
└──────────────────┘     └──────────────────┘     └──────────────────┘
       │                        │                        │
       │ REST API               │ MongoDB                │ Files
       │ Socket.IO              │ Redis                  │ Git
       │ JWT Auth               │ Socket.IO relay        │ AI API

gadget-tasks acts as a programmatic IDE user:

  • Authenticates with the platform (same as logging into the browser IDE)
  • Selects a drone (same as clicking a drone in the UI)
  • Creates chat sessions, locks drones, submits prompts (same as typing in the chat)
  • Waits for work order completion (same as watching the streaming response)
  • Updates task records when done

Per-Task Execution Flow

When a CronJob fires for a scheduled task:

  1. Create ChatSessionPOST /api/v1/chat-sessions
  2. Lock drone — Socket.IO requestSessionLock(drone, project, session)
  3. Set workspace mode — Socket.IO requestWorkspaceMode(drone, project, session, "agent")
  4. Submit prompt — Socket.IO submitPrompt(task.content) → creates ChatTurn with canonical system prompt → routes work order to drone
  5. Wait for completion — Socket.IO receives workOrderComplete(turnId, success, message)
  6. Release drone lock — Socket.IO releaseSessionLock(drone, project, session)
  7. Update task.lastRunPATCH /api/v1/projects/:id/tasks/:taskId/lastRun

Steps 26 use the same Socket.IO protocol the browser IDE uses. gadget-code builds the system prompt from its canonical templates, routes the work order to the drone, and persists all results as ChatTurn records — identical to interactive use.

Source Structure

gadget-tasks/
├── gadget-tasks.yaml          # YAML configuration
├── package.json
├── tsconfig.json
└── src/
    ├── gadget-tasks.ts        # Main entry — startup, shutdown, drone selection
    ├── config/
    │   └── env.ts             # Config loader — platform.baseUrl, redis, concurrency
    ├── lib/
    │   ├── process.ts         # GadgetProcess base class
    │   └── service.ts         # GadgetService base class
    └── services/
        ├── platform.ts        # REST API + Socket.IO headless IDE client
        ├── scheduler.ts       # CronJob management, concurrency control
        └── lock.ts            # Redis singleton lock (prevents duplicate instances)

Key Services

Service File Purpose
PlatformService src/services/platform.ts REST API client (auth, projects, sessions, drones) + Socket.IO client (session lock, workspace mode, prompt submission, work order tracking)
SchedulerService src/services/scheduler.ts Creates CronJobs from task crontab expressions, enforces concurrency limit, delegates to PlatformService
TaskLockService src/services/lock.ts Redis-based singleton lock — prevents multiple gadget-tasks instances from running simultaneously

Configuration

gadget-tasks.yaml

timezone: America/New_York
platform:
  baseUrl: https://code-dev.g4dge7.com:5174
redis:
  host: localhost
  port: 6379
  # password: optional
  # keyPrefix: defaults to "gadget:"
concurrency: 1
logging:
  console:
    enabled: true
  file:
    enabled: true
    path: ~/logs/gadget-tasks
    # name: defaults to "gadget-tasks"
    # maxWritesPerFile: defaults to 10000
    # maxFiles: defaults to 10
Field Required Default Description
timezone No America/New_York Timezone for CronJob scheduling
platform.baseUrl Yes URL of the gadget-code platform
redis.host No localhost Redis host for singleton lock
redis.port No 6379 Redis port
redis.password No Redis password
redis.keyPrefix No gadget: Redis key prefix
concurrency No 1 Max concurrent tasks (sequential by default)
logging.console.enabled No false Enable console logging
logging.file.enabled No false Enable file logging
logging.file.path No ./logs Log file directory

Credentials

Email and password are not stored in the config file. They are provided at startup:

  • CLI args: --user=admin@example.com --password=secret
  • Interactive prompt: If no CLI args, you'll be prompted (same pattern as gadget-drone)

This avoids storing credentials in config files that may be checked into version control.

Startup Sequence

1. hookProcessSignals()        — SIGINT handler
2. Acquire Redis singleton lock — exits if another instance is running
3. Get user credentials        — CLI args or interactive prompt
4. Authenticate with platform  — POST /api/v1/auth/sign-in → JWT
5. Select a drone              — auto-select if only one, otherwise interactive list
6. Connect Socket.IO           — JWT auth, websocket transport
7. Start scheduler             — empty initially
8. Fetch projects              — GET /api/v1/projects
9. Schedule enabled tasks      — create CronJob for each task's crontab
10. Start heartbeat            — 19s interval, prevents drone timeout

Shutdown Sequence

1. Stop all CronJobs           — SchedulerService.stop()
2. Disconnect Socket.IO        — reject pending work orders
3. Release Redis lock          — allow another instance to start

Getting Started

Prerequisites

  • gadget-code platform running (backend + frontend)
  • At least one gadget-drone registered and online
  • Redis running on localhost:6379 (or as configured)
  • A user account on the platform with projects that have tasks defined

Running

# From the monorepo root
cd gadget-tasks

# Development mode (TypeScript, auto-restart)
pnpm dev -- --user=admin@example.com --password=secret

# Or with interactive credential prompt
pnpm dev

# Production (build first)
pnpm build
pnpm start -- --user=admin@example.com --password=secret

Global CLI

# Link globally from the monorepo root
pnpm link:global

# Now available as a system command
gadget-tasks --user=admin@example.com --password=secret

Task Execution Details

Concurrency

By default, concurrency: 1 means tasks execute sequentially. If a task fires while the drone is busy with another task, the new task waits for the drone to become available. This is the correct model — drones process one work order at a time.

If you increase concurrency, you need multiple drones available. Each task requires a drone lock, and a drone can only be locked to one session at a time.

Work Order Tracking

When submitPrompt is called, the callback provides a turnId. gadget-tasks stores a Promise resolver in a pendingWorkOrders map keyed by turnId. When the server emits workOrderComplete(turnId, success, message), the corresponding Promise is resolved, unblocking the task execution.

Session Heartbeat

gadget-tasks sends a sessionHeartbeat every 19 seconds (same interval as the browser IDE). This prevents the drone's 120-second heartbeat timeout from firing while a task is active.

Error Recovery

If gadget-tasks crashes while a task is being processed:

  • The drone continues processing the work order independently
  • The session and ChatTurn records are preserved in the database
  • On restart, gadget-tasks creates new sessions for tasks that fire
  • Old sessions are visible in the browser IDE via their ChatTurn records

No special crash recovery is needed in gadget-tasks (unlike gadget-drone, which has crash recovery for incomplete work orders).

What gadget-tasks Does NOT Do

Does NOT Instead
Connect to MongoDB Uses REST API to read/write data
Define Mongoose models Uses @gadget/api TypeScript interfaces
Call AI APIs Submits prompts through the platform → drone
Build system prompts gadget-code builds prompts from canonical templates
Execute tools The drone executes tools via @gadget/ai-toolbox
Manage workspaces The drone manages workspace directories
Store credentials in config Provides them at startup via CLI or prompt
  • README — Project overview and quick start
  • Agent Toolbox@gadget/ai-toolbox tool implementations (used by drones, not gadget-tasks)
  • Socket Protocol — Socket.IO protocol between IDE, platform, and drone
  • Drone Documentation — How drones execute work orders