# 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 ChatSession** — `POST /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.lastRun** — `PATCH /api/v1/projects/:id/tasks/:taskId/lastRun` Steps 2–6 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 ```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 ```bash # 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 ```bash # 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 | ## Related Documentation - [README](../README.md) — Project overview and quick start - [Agent Toolbox](./agent-toolbox.md) — `@gadget/ai-toolbox` tool implementations (used by drones, not gadget-tasks) - [Socket Protocol](./socket-protocol.md) — Socket.IO protocol between IDE, platform, and drone - [Drone Documentation](../gadget-drone/docs/gadget-drone.md) — How drones execute work orders