- 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.
221 lines
9.6 KiB
Markdown
221 lines
9.6 KiB
Markdown
# 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
|