Changeset 0.28.0.2 (#174)
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# 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.
|
||||
Autonomous agent scheduling. A task is a cron-driven, one-shot, or
|
||||
webhook-triggered execution in a headless service channel — no human
|
||||
participant in the loop.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -63,17 +64,20 @@ POST /tasks
|
||||
}
|
||||
```
|
||||
|
||||
`task_type`: `prompt` (direct LLM execution) or `workflow` (instantiate
|
||||
a workflow in the service channel).
|
||||
`task_type`: `prompt` (direct LLM execution), `workflow` (deferred —
|
||||
not yet implemented; creation is rejected at the API), or `action`
|
||||
(no LLM — relay/webhook only; requires `task.action` permission).
|
||||
|
||||
`schedule`: cron expression or `once`. Supported patterns: `@hourly`,
|
||||
`@daily`, `@weekly`, `*/N * * * *` (every N minutes), full 5-field cron
|
||||
via `robfig/cron/v3`.
|
||||
`schedule`: cron expression, `once`, or `webhook`. Supported cron
|
||||
patterns: `@hourly`, `@daily`, `@weekly`, `*/N * * * *` (every N
|
||||
minutes), full 5-field cron via `robfig/cron/v3`. When `schedule` is
|
||||
`webhook`, the task fires only on inbound POST to its trigger URL.
|
||||
|
||||
`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.
|
||||
**Auth:** `tasks.create` permission required. Additionally, `task.action`
|
||||
permission is required for `task_type: "action"`.
|
||||
|
||||
### Get Task
|
||||
|
||||
@@ -89,7 +93,11 @@ GET /tasks/:id
|
||||
PUT /tasks/:id
|
||||
```
|
||||
|
||||
Partial update. All fields from create are accepted.
|
||||
Partial update. Accepted fields: `name`, `description`, `persona_id`,
|
||||
`model_id`, `system_prompt`, `user_prompt`, `workflow_id`, `tool_grants`,
|
||||
`schedule`, `timezone`, `is_active`, `max_tokens`, `max_tool_calls`,
|
||||
`max_wall_clock`, `output_mode`, `output_channel_id`, `webhook_url`,
|
||||
`provider_config_id`, `notify_on_complete`, `notify_on_failure`.
|
||||
|
||||
**Auth:** `tasks.create` permission, must be owner.
|
||||
|
||||
@@ -108,7 +116,7 @@ POST /tasks/:id/run
|
||||
```
|
||||
|
||||
Immediately executes the task regardless of schedule. Creates a new
|
||||
task run. Returns 409 if a run is already active.
|
||||
task run. Returns 409 if a run is already active or queued.
|
||||
|
||||
**Auth:** `tasks.create` permission, must be owner.
|
||||
|
||||
@@ -132,6 +140,39 @@ Returns run history for the task, most recent first.
|
||||
|
||||
**Auth:** Owner or admin.
|
||||
|
||||
## Webhook Trigger (Inbound)
|
||||
|
||||
External systems fire a task by POSTing to its trigger URL.
|
||||
|
||||
```
|
||||
POST /api/v1/hooks/t/:token
|
||||
```
|
||||
|
||||
The `:token` is a per-task secret generated at creation time (returned
|
||||
in the `trigger_token` field). The request body is stored as
|
||||
`trigger_payload` on the run record and forwarded to the executor:
|
||||
|
||||
- **Prompt tasks:** payload is prepended to the user prompt as context.
|
||||
- **Action tasks:** payload is relayed to the outbound webhook as-is.
|
||||
- **Workflow tasks:** (deferred) payload becomes initial stage data.
|
||||
|
||||
Returns `202 Accepted`:
|
||||
```json
|
||||
{
|
||||
"triggered": true,
|
||||
"run_id": "uuid",
|
||||
"task_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
Returns `409 Conflict` if a run is already active or queued.
|
||||
Returns `410 Gone` if the task is inactive.
|
||||
|
||||
**Auth:** None (token-based). The trigger token *is* the credential.
|
||||
|
||||
**Rate limiting:** Governed by global request rate limits. No per-task
|
||||
rate limiting in v0.28 — defer to a future version if needed.
|
||||
|
||||
## Team Tasks
|
||||
|
||||
Team-scoped tasks visible to all team members, manageable by team admins.
|
||||
@@ -209,16 +250,17 @@ DELETE /admin/tasks/:id
|
||||
"name": "Morning News Digest",
|
||||
"description": "...",
|
||||
"scope": "personal|team|global",
|
||||
"task_type": "prompt|workflow",
|
||||
"task_type": "prompt|workflow|action",
|
||||
"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",
|
||||
"schedule": "@daily|once|webhook",
|
||||
"timezone": "America/New_York",
|
||||
"is_active": true,
|
||||
"trigger_token": "hex-string|null",
|
||||
"max_tokens": 4096,
|
||||
"max_tool_calls": 10,
|
||||
"max_wall_clock": 300,
|
||||
@@ -244,7 +286,8 @@ DELETE /admin/tasks/:id
|
||||
"id": "uuid",
|
||||
"task_id": "uuid",
|
||||
"channel_id": "uuid|null",
|
||||
"status": "running|completed|failed|budget_exceeded|cancelled",
|
||||
"status": "queued|running|completed|failed|budget_exceeded|cancelled",
|
||||
"trigger_payload": "...|null",
|
||||
"started_at": "...",
|
||||
"completed_at": "...|null",
|
||||
"tokens_used": 1234,
|
||||
@@ -260,18 +303,29 @@ 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`
|
||||
3. Adopts a queued run if one exists (from webhook trigger), otherwise
|
||||
creates a new run record
|
||||
4. Marks `last_run_at` (records when execution started, not when it finished)
|
||||
5. Creates or reuses a `service` channel (`output_channel_id`)
|
||||
6. Persists the `user_prompt` as a message in the service channel
|
||||
(with trigger payload prepended if present)
|
||||
7. For **prompt** tasks: runs `CoreToolLoop` (headless completion)
|
||||
8. For **action** tasks: skips LLM, relays trigger payload to webhook
|
||||
9. Enforces budgets: `max_tokens`, `max_tool_calls`, `max_wall_clock`
|
||||
10. On budget breach: status = `budget_exceeded`, owner notified
|
||||
11. On completion: calculates `next_run_at`
|
||||
|
||||
**Workflow tasks:** Not yet implemented. The API rejects
|
||||
`task_type: "workflow"` at creation time. Reserved for a future version.
|
||||
|
||||
**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.
|
||||
don't have a BYOK provider fail at step 7.
|
||||
|
||||
## Webhooks
|
||||
**Action tasks** skip steps 6–9. They create the service channel, fire
|
||||
the outbound webhook with the trigger payload, and complete.
|
||||
|
||||
## Webhooks (Outbound)
|
||||
|
||||
Tasks with `webhook_url` fire a POST on completion:
|
||||
|
||||
@@ -279,12 +333,28 @@ Tasks with `webhook_url` fire a POST on completion:
|
||||
{
|
||||
"task_id": "uuid",
|
||||
"run_id": "uuid",
|
||||
"task_name": "Morning News Digest",
|
||||
"channel_id": "uuid",
|
||||
"status": "completed|failed|budget_exceeded",
|
||||
"completed_at": "2025-03-11T12:00:00Z",
|
||||
"output": "...",
|
||||
"tokens_used": 1234,
|
||||
"timestamp": "..."
|
||||
"error": "...|null"
|
||||
}
|
||||
```
|
||||
|
||||
Signed with HMAC-SHA256 using `webhook_secret` in the
|
||||
`X-Switchboard-Signature` header. Retry: 3 attempts, exponential backoff.
|
||||
`X-Switchboard-Signature` header. Retry: 3 attempts, exponential
|
||||
backoff (1s, 5s, 25s).
|
||||
|
||||
## Task Chaining
|
||||
|
||||
Task A's outbound `webhook_url` can be Task B's trigger URL:
|
||||
|
||||
```
|
||||
Task A (webhook_url) → POST /api/v1/hooks/t/<task-B-token> → Task B runs
|
||||
```
|
||||
|
||||
This enables multi-step pipelines: CI failure → summarize with LLM →
|
||||
post to Slack. Each link is a separate task with its own type, budget,
|
||||
and notification preferences.
|
||||
|
||||
Reference in New Issue
Block a user