291 lines
6.3 KiB
Markdown
291 lines
6.3 KiB
Markdown
# Tasks
|
|
|
|
Autonomous agent scheduling. A task is a cron-driven or one-shot prompt
|
|
execution in a headless service channel — no human participant in the loop.
|
|
|
|
## Configuration
|
|
|
|
Global config keys (set via `PUT /admin/settings/:key`):
|
|
|
|
| Key | Default | Description |
|
|
|-----|---------|-------------|
|
|
| `tasks.enabled` | `true` | Master kill switch for task scheduler |
|
|
| `tasks.allow_personal` | `true` | Allow non-admin users to create tasks |
|
|
| `tasks.max_concurrent` | `5` | Max tasks running simultaneously |
|
|
| `tasks.personal_require_byok` | `false` | Personal tasks must use BYOK provider |
|
|
|
|
Default budget ceilings (overridable per task):
|
|
|
|
| Key | Default |
|
|
|-----|---------|
|
|
| `tasks.default_max_tokens` | `4096` |
|
|
| `tasks.default_max_tool_calls` | `10` |
|
|
| `tasks.default_max_wall_clock` | `300` (seconds) |
|
|
|
|
## Personal Tasks
|
|
|
|
### List My Tasks
|
|
|
|
```
|
|
GET /tasks
|
|
```
|
|
|
|
Returns tasks owned by the current user.
|
|
|
|
**Auth:** Authenticated.
|
|
|
|
### Create Task
|
|
|
|
```
|
|
POST /tasks
|
|
```
|
|
|
|
```json
|
|
{
|
|
"name": "Morning News Digest",
|
|
"description": "Summarize top tech news",
|
|
"task_type": "prompt",
|
|
"persona_id": "uuid|null",
|
|
"model_id": "claude-sonnet-4-20250514",
|
|
"system_prompt": "You are a news summarizer.",
|
|
"user_prompt": "Summarize the top 5 tech news stories from today.",
|
|
"schedule": "@daily",
|
|
"timezone": "America/New_York",
|
|
"max_tokens": 4096,
|
|
"max_tool_calls": 10,
|
|
"max_wall_clock": 300,
|
|
"output_mode": "channel",
|
|
"webhook_url": "https://...",
|
|
"provider_config_id": "uuid|null",
|
|
"notify_on_complete": false,
|
|
"notify_on_failure": true,
|
|
"tool_grants": ["web_search", "url_fetch"]
|
|
}
|
|
```
|
|
|
|
`task_type`: `prompt` (direct LLM execution) or `workflow` (instantiate
|
|
a workflow in the service channel).
|
|
|
|
`schedule`: cron expression or `once`. Supported patterns: `@hourly`,
|
|
`@daily`, `@weekly`, `*/N * * * *` (every N minutes), full 5-field cron
|
|
via `robfig/cron/v3`.
|
|
|
|
`output_mode`: `channel` (default — output stays in service channel),
|
|
`note` (creates a note from the output), `webhook` (POST result to URL).
|
|
|
|
**Auth:** `tasks.create` permission required.
|
|
|
|
### Get Task
|
|
|
|
```
|
|
GET /tasks/:id
|
|
```
|
|
|
|
**Auth:** Owner or admin.
|
|
|
|
### Update Task
|
|
|
|
```
|
|
PUT /tasks/:id
|
|
```
|
|
|
|
Partial update. All fields from create are accepted.
|
|
|
|
**Auth:** `tasks.create` permission, must be owner.
|
|
|
|
### Delete Task
|
|
|
|
```
|
|
DELETE /tasks/:id
|
|
```
|
|
|
|
**Auth:** `tasks.create` permission, must be owner.
|
|
|
|
### Run Now (Manual Trigger)
|
|
|
|
```
|
|
POST /tasks/:id/run
|
|
```
|
|
|
|
Immediately executes the task regardless of schedule. Creates a new
|
|
task run. Returns 409 if a run is already active.
|
|
|
|
**Auth:** `tasks.create` permission, must be owner.
|
|
|
|
### Kill Active Run
|
|
|
|
```
|
|
POST /tasks/:id/kill
|
|
```
|
|
|
|
Cancels the active run. Sets status to `cancelled`.
|
|
|
|
**Auth:** `tasks.create` permission, must be owner.
|
|
|
|
### List Runs
|
|
|
|
```
|
|
GET /tasks/:id/runs
|
|
```
|
|
|
|
Returns run history for the task, most recent first.
|
|
|
|
**Auth:** Owner or admin.
|
|
|
|
## Team Tasks
|
|
|
|
Team-scoped tasks visible to all team members, manageable by team admins.
|
|
|
|
### List Team Tasks (Member)
|
|
|
|
```
|
|
GET /teams/:teamId/tasks
|
|
```
|
|
|
|
Returns tasks scoped to the team. Read-only for members.
|
|
|
|
**Auth:** Team member.
|
|
|
|
### List Team Task Runs (Member)
|
|
|
|
```
|
|
GET /teams/:teamId/tasks/:id/runs
|
|
```
|
|
|
|
**Auth:** Team member.
|
|
|
|
### Create Team Task (Admin)
|
|
|
|
```
|
|
POST /teams/:teamId/tasks
|
|
```
|
|
|
|
Same shape as personal task creation. Team scope is injected automatically.
|
|
|
|
**Auth:** `tasks.create` permission, team admin.
|
|
|
|
### Update/Delete/Run/Kill Team Task
|
|
|
|
```
|
|
PUT /teams/:teamId/tasks/:id
|
|
DELETE /teams/:teamId/tasks/:id
|
|
POST /teams/:teamId/tasks/:id/run
|
|
POST /teams/:teamId/tasks/:id/kill
|
|
```
|
|
|
|
**Auth:** `tasks.create` permission, team admin.
|
|
|
|
## Admin Tasks
|
|
|
|
Platform-wide task management.
|
|
|
|
### List All Tasks
|
|
|
|
```
|
|
GET /admin/tasks
|
|
```
|
|
|
|
Returns all tasks across all users and teams.
|
|
|
|
**Auth:** Platform admin.
|
|
|
|
### Admin Run/Kill/Delete
|
|
|
|
```
|
|
POST /admin/tasks/:id/run
|
|
POST /admin/tasks/:id/kill
|
|
DELETE /admin/tasks/:id
|
|
```
|
|
|
|
**Auth:** Platform admin.
|
|
|
|
## Task Object
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"owner_id": "uuid",
|
|
"team_id": "uuid|null",
|
|
"name": "Morning News Digest",
|
|
"description": "...",
|
|
"scope": "personal|team|global",
|
|
"task_type": "prompt|workflow",
|
|
"persona_id": "uuid|null",
|
|
"model_id": "claude-sonnet-4-20250514",
|
|
"system_prompt": "...",
|
|
"user_prompt": "...",
|
|
"workflow_id": "uuid|null",
|
|
"tool_grants": ["web_search", "url_fetch"],
|
|
"schedule": "@daily",
|
|
"timezone": "America/New_York",
|
|
"is_active": true,
|
|
"max_tokens": 4096,
|
|
"max_tool_calls": 10,
|
|
"max_wall_clock": 300,
|
|
"output_mode": "channel|note|webhook",
|
|
"output_channel_id": "uuid|null",
|
|
"webhook_url": "https://...",
|
|
"webhook_secret": "...",
|
|
"provider_config_id": "uuid|null",
|
|
"notify_on_complete": false,
|
|
"notify_on_failure": true,
|
|
"last_run_at": "...|null",
|
|
"next_run_at": "...|null",
|
|
"run_count": 42,
|
|
"created_at": "...",
|
|
"updated_at": "..."
|
|
}
|
|
```
|
|
|
|
## Task Run Object
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"task_id": "uuid",
|
|
"channel_id": "uuid|null",
|
|
"status": "running|completed|failed|budget_exceeded|cancelled",
|
|
"started_at": "...",
|
|
"completed_at": "...|null",
|
|
"tokens_used": 1234,
|
|
"tool_calls": 3,
|
|
"wall_clock": 45,
|
|
"error": "...|null"
|
|
}
|
|
```
|
|
|
|
## Execution
|
|
|
|
The `TaskScheduler` is a background goroutine polling every 30 seconds.
|
|
|
|
1. Finds tasks where `next_run_at <= now` and `is_active = true`
|
|
2. Skips if an active run exists (`GetActiveRun` returns non-nil)
|
|
3. Creates or reuses a `service` channel (`output_channel_id`)
|
|
4. Persists the `user_prompt` as a message in the service channel
|
|
5. Runs `CoreToolLoop` (headless completion — same as streaming but no SSE)
|
|
6. Enforces budgets: `max_tokens`, `max_tool_calls`, `max_wall_clock`
|
|
7. On budget breach: status = `budget_exceeded`, owner notified
|
|
8. On completion: updates `last_run_at`, calculates `next_run_at`
|
|
|
|
**Provider resolution:** BYOK → team provider → global provider → routing
|
|
policy. If `tasks.personal_require_byok` is true, personal tasks that
|
|
don't have a BYOK provider fail at step 5.
|
|
|
|
## Webhooks
|
|
|
|
Tasks with `webhook_url` fire a POST on completion:
|
|
|
|
```json
|
|
{
|
|
"task_id": "uuid",
|
|
"run_id": "uuid",
|
|
"status": "completed|failed|budget_exceeded",
|
|
"output": "...",
|
|
"tokens_used": 1234,
|
|
"timestamp": "..."
|
|
}
|
|
```
|
|
|
|
Signed with HMAC-SHA256 using `webhook_secret` in the
|
|
`X-Switchboard-Signature` header. Retry: 3 attempts, exponential backoff.
|