docs: redesign agent integration around bot users as first-class citizens

Reworked the exploration plan so that assigning an agent feels like
assigning a coworker. Bot users appear in the regular assignee picker,
progress shows as normal task comments, and completion is just the bot
marking the task done. No special agent panels or new UI paradigms.

https://claude.ai/code/session_01CL89RAEskas7Hz1TSr7eKR
This commit is contained in:
Claude 2026-03-22 11:08:50 +00:00
parent 55f0322808
commit ce7956e842
No known key found for this signature in database
1 changed files with 258 additions and 185 deletions

View File

@ -10,216 +10,283 @@ Both frameworks can connect to external services, execute code, and manage tasks
---
## Design Principle
**Assigning an agent to a task should feel exactly like assigning a coworker.**
- Bot users appear in the same assignee picker as human users
- You assign them the same way — search, click, done
- Progress shows up as regular comments in the task thread
- The task gets marked done when the bot finishes
- The only visual difference is a small bot badge next to the name
No separate "agent panels," no special buttons. The existing assignee UX *is* the interface.
---
## Integration Concept
The core idea: **assign an AI agent (via OpenClaw or NanoClaw) to a Vikunja task, and the agent autonomously works to complete it.**
This requires two directions of communication:
1. **Vikunja → Agent**: "Here's a task, go do it" (task assignment triggers agent work)
2. **Agent → Vikunja**: "Here's my progress/result" (agent updates task status, adds comments, attaches files)
1. **Vikunja → Agent**: Assigning a bot user to a task triggers a dispatch to the agent's endpoint
2. **Agent → Vikunja**: The agent calls back via the standard Vikunja API to post comments, update the task, attach files, and mark it done
---
## What Already Exists in Vikunja That Helps
### Webhooks (outbound notifications)
- Project-level and user-level webhooks already exist
- Events like `task.created`, `task.updated`, `task.assignee.created` can trigger HTTP calls
- HMAC-SHA256 signing, Basic Auth support
- **This handles Vikunja → Agent notifications**
### Task Assignees (the primary UX surface)
- Tasks can have multiple assignees (users)
- Assignment dispatches `TaskAssigneeCreatedEvent`
- Existing assignee picker UI with search
- **This becomes the integration trigger** — assigning a bot user dispatches work to the agent
### REST API + API Tokens (inbound control)
- Full CRUD API for tasks, comments, attachments, assignees
- Scoped API tokens (`tk_` prefix) with per-route permissions
- **This handles Agent → Vikunja updates**
- **This handles Agent → Vikunja updates** — the bot user gets its own API token
### Event System (Watermill-based)
- Async event dispatch with retry logic
- Existing task lifecycle events (created, updated, deleted, assignee changes)
- Listener registration pattern for extending behavior
- **New listener on `TaskAssigneeCreatedEvent`** checks if assignee is a bot and dispatches
### Cron System
- `robfig/cron/v3` for scheduled background work
- Used for reminders, overdue checks, cleanup jobs
- **Can monitor running agent tasks** for timeouts/failures
### Task Assignees
- Tasks can have multiple assignees (users)
- Assignment dispatches `TaskAssigneeCreatedEvent`
### Webhooks (outbound notifications)
- Project-level and user-level webhooks already exist
- HMAC-SHA256 signing, Basic Auth support
- Could supplement the integration but not the primary mechanism
---
## What Would Need to Change
### Option A: Lightweight / Webhook-Based Integration (Recommended Starting Point)
### 1. Bot User Identity (`pkg/user/user.go`)
This approach uses existing Vikunja primitives and puts the orchestration logic in an external bridge service.
Add `IsBot` flag to the User struct:
```go
IsBot bool `xorm:"bool default false" json:"is_bot"`
```
#### New Components
Bot users are real users with a special flag. They:
- Appear in the assignee picker alongside regular users
- Show a bot badge in the UI (avatar, comments, assignee lists)
- Cannot log in via the web UI — API token only
- Don't receive email notifications (reminders, overdue, etc.)
- Don't need email confirmation, password, or TOTP
- Have their own audit trail — every action is attributed to the bot
1. **Agent Configuration Model** (`pkg/models/agents.go`)
- New database table `agents` storing agent connections:
```go
type Agent struct {
ID int64
Name string // "My OpenClaw Agent"
Type string // "openclaw" or "nanoclaw"
EndpointURL string // Agent API base URL
APIKey string // Auth credential for the agent
ProjectID int64 // Scoped to a project
CreatedByID int64
Created time.Time
Updated time.Time
}
```
- CRUD + Permissions (project-level admin only)
- Migration file for the new table
**Database migration:** Add `is_bot` column to `users` table.
2. **Agent Assignment on Tasks** (`pkg/models/task_agent.go`)
- New table `task_agents` linking tasks to agents:
```go
type TaskAgent struct {
ID int64
TaskID int64
AgentID int64
Status string // "pending", "running", "completed", "failed"
Created time.Time
Updated time.Time
}
```
- When an agent is assigned to a task, Vikunja sends the task details to the agent's endpoint
- Agent status tracked and displayed on the task
### 2. Agent Connection Config on Bot Users (`pkg/user/user.go` or new model)
3. **Agent Dispatch Listener** (`pkg/models/listeners.go`)
- New event listener for `TaskAgentAssignedEvent`
- Sends HTTP POST to the agent's endpoint with task details:
```json
{
"task_id": 123,
"title": "Fix the login bug",
"description": "Users report...",
"callback_url": "https://vikunja.example.com/api/v1",
"callback_token": "tk_..."
}
```
- The callback URL and token let the agent call back into Vikunja's API
Bot users need agent connection details. Two approaches:
4. **Agent Callback API Token Generation**
- When dispatching to an agent, auto-generate a scoped API token
- Permissions limited to: update this task, add comments, add attachments
- Token expires when task is marked done or after a configurable TTL
**Option A: Fields on the User model** (simpler)
```go
AgentEndpointURL string `xorm:"text null" json:"agent_endpoint_url,omitempty"`
AgentAPIKey string `xorm:"text null" json:"-"` // never exposed in API responses
AgentType string `xorm:"varchar(50) null" json:"agent_type,omitempty"` // "openclaw", "nanoclaw"
```
5. **API Routes** (`pkg/routes/api/v1/`)
- `GET /projects/{id}/agents` — list configured agents for a project
- `PUT /projects/{id}/agents` — add an agent configuration
- `POST /projects/{id}/agents/{agentID}` — update agent config
- `DELETE /projects/{id}/agents/{agentID}` — remove agent
- `PUT /tasks/{id}/agents` — assign agent to task
- `DELETE /tasks/{id}/agents/{agentID}` — unassign agent
- `GET /tasks/{id}/agents` — list agents on a task with status
- `POST /tasks/{id}/agents/{agentID}/status` — agent reports status back (webhook receiver)
**Option B: Separate `bot_configs` table** (cleaner separation)
```go
type BotConfig struct {
ID int64
UserID int64 // FK to users, unique (one config per bot user)
EndpointURL string
APIKey string // encrypted at rest
Type string // "openclaw", "nanoclaw"
Created time.Time
Updated time.Time
}
```
6. **Frontend Changes**
- **Agent config UI** in project settings (`frontend/src/views/project/settings/`)
- Form to add/edit agent connections (name, type, URL, API key)
- **Task detail agent panel** (`frontend/src/components/tasks/`)
- Show assigned agents and their status
- Button to assign an available agent to the task
- Live status indicator (pending/running/completed/failed)
- **New Pinia store** (`frontend/src/stores/agents.ts`)
- **New service** (`frontend/src/services/agent.ts`)
- **New model types** (`frontend/src/modelTypes/IAgent.ts`)
**Recommendation: Option B** — keeps the User model clean, allows the config to be managed independently, and makes it easier to encrypt the API key at rest.
#### Flow
### 3. Agent Dispatch Listener (`pkg/models/listeners.go`)
New listener on `TaskAssigneeCreatedEvent`:
```
User assigns agent to task in Vikunja UI
→ Vikunja creates TaskAgent record
→ Vikunja generates scoped API token
→ Vikunja POSTs task details + callback token to agent endpoint
→ Agent starts working autonomously
→ Agent calls back to Vikunja API:
- POST /tasks/{id}/comments → progress updates
- PUT /tasks/{id} → update status, mark done
- PUT /tasks/{id}/attachments → attach deliverables
→ Vikunja shows agent progress in task detail view
When a bot user is assigned to a task:
1. Load the bot's agent config (endpoint URL, API key, type)
2. Load full task context (title, description, comments, attachments, related tasks)
3. Generate a scoped API token for the bot user (if one doesn't exist)
4. POST to the agent endpoint with task details + callback token
5. The agent starts working autonomously
```
Corresponding listener on `TaskAssigneeDeletedEvent`:
```
When a bot user is unassigned from a task:
1. POST a cancellation request to the agent endpoint
2. Agent stops work on that task
```
### 4. Agent Adapter Interface (`pkg/modules/agents/`)
Generic interface with provider-specific implementations:
```go
type AgentProvider interface {
// Dispatch sends a task to the agent for processing
Dispatch(ctx context.Context, task *models.Task, callbackURL string, callbackToken string) error
// Cancel tells the agent to stop working on a task
Cancel(ctx context.Context, taskID int64) error
// Status checks if the agent is healthy
Status(ctx context.Context) (AgentStatus, error)
}
```
Implementations:
- `pkg/modules/agents/openclaw/` — OpenClaw HTTP API adapter
- `pkg/modules/agents/nanoclaw/` — NanoClaw adapter
### 5. Bot User Creation API
New endpoint or flag on existing user creation:
- `PUT /api/v1/bots` — create a bot user (admin or project admin only)
- Takes: name, username, agent type, endpoint URL, API key
- Creates user with `is_bot: true`
- Creates associated `BotConfig`
- Auto-generates an API token for the bot
- Returns the bot user + token (token shown only once)
- `GET /api/v1/bots` — list bot users accessible to current user
- `POST /api/v1/bots/{id}` — update bot config
- `DELETE /api/v1/bots/{id}` — deactivate bot user
### 6. Frontend Changes
**Minimal — that's the point.** The UX piggybacks on existing patterns:
#### Assignee Picker (modify existing)
- Bot users already appear in the user list (they're real users)
- Add a small bot icon/badge next to bot user names
- No other changes needed — assignment works identically
#### User/Avatar Display (`frontend/src/components/misc/User.vue`)
- Show bot badge (small robot icon) on avatar
- Applied everywhere users are displayed: assignee lists, comments, activity log
#### Task Comments
- Comments from bot users get a subtle "bot" indicator
- No separate rendering — same comment thread, same layout
- Users reply to bot comments naturally (the agent sees replies via API polling or webhooks)
#### Bot Management UI (new, minimal)
- Settings page for creating/managing bot users
- Form: name, username, agent type, endpoint URL, API key
- Shows the generated API token once on creation
- Could live under team/workspace settings
#### Model Types (`frontend/src/modelTypes/IUser.ts`)
- Add `isBot: boolean` to `IUser`
- Add `agentType?: string` to `IUser`
### 7. Guard Rails & Edge Cases
**Authentication:**
- Bot users cannot authenticate via username/password or OIDC
- Only API token auth is valid for bots
- Login endpoint rejects users with `is_bot: true`
**Notifications:**
- Skip email notifications for bot users (reminders, overdue, mentions)
- Bot users shouldn't trigger "user mentioned" notifications when they @-mention someone? Or should they? (configurable)
**Permissions:**
- Bot users need project access just like regular users (added to project/team)
- Bot actions are scoped by the same permission system
- Bot can only modify tasks in projects it has write access to
**Rate Limiting:**
- Consider rate limits on bot API calls to prevent runaway agents
- Configurable per bot or globally
**Failure Handling:**
- Cron job checks for stalled agent tasks (assigned to bot, no activity for X minutes)
- Notify the user who assigned the bot if the agent appears stuck
- Auto-unassign after configurable timeout
---
### Option B: Deep Integration (More Ambitious)
## The User Experience, End to End
Builds on Option A with additional capabilities:
### Setup (one-time)
1. Admin goes to Settings → Bots
2. Creates a bot: "CodeReview Bot", type: OpenClaw, endpoint: `https://openclaw.example.com/api/agent`
3. Vikunja creates a bot user, generates an API token, shows it once
4. Admin adds the bot user to the relevant project(s)/team(s)
7. **Agent Status Polling Cron**
- Background cron job checking agent health/status
- Polls agents with `status: "running"` to detect stalls
- Updates task status if agent becomes unreachable
- Auto-retry logic for transient failures
### Daily Use
1. User creates a task: "Review PR #42 for security issues"
2. User clicks the assignee picker, sees both coworkers and bots
3. User assigns "CodeReview Bot" — looks just like assigning a coworker
4. Behind the scenes: Vikunja dispatches the task to OpenClaw
5. Minutes later, comments start appearing on the task from the bot:
- "Started reviewing PR #42..."
- "Found 2 potential issues: SQL injection in `user_query.go:45`, missing auth check in `api/handler.go:112`"
- "Full report attached."
6. Bot attaches a detailed report file
7. Bot marks the task as done
8. User sees it all in the normal task detail view — no special UI needed
8. **Agent Chat Interface**
- Extend task comments to support "agent messages"
- Add a `source` field to `TaskComment` ("user" vs "agent")
- Frontend renders agent comments differently (distinct styling)
- Users can reply to agent comments to provide guidance
9. **Agent Templates / Skills**
- Pre-configured agent profiles for common task types
- Map Vikunja labels/tags to agent skills
- "Code Review" agent, "Research" agent, "Writing" agent, etc.
10. **Multi-Agent Orchestration**
- Multiple agents on a single task (one researches, another implements)
- Agent-to-agent handoff via task relations
- Parent task decomposition: agent creates subtasks and assigns sub-agents
### Collaboration
- User can reply to bot comments with clarifications
- User can unassign the bot to stop it
- User can assign a different bot or a human to take over
- Multiple bots can be assigned (one researches, another implements)
---
## Implementation Effort Estimate
## Implementation Phases
### Option A (Recommended MVP)
### Phase 1: Bot User Foundation
- Add `is_bot` field to User model + migration
- Add `BotConfig` model + migration
- Bot creation API endpoint
- Skip email/password/TOTP requirements for bots
- Block web login for bots
- Skip email notifications for bots
- Frontend: `isBot` on IUser, bot badge on User.vue
| Component | Files to Create/Modify | Scope |
|-----------|----------------------|-------|
| Agent model + migration | 3-4 new Go files | New DB table, CRUD, permissions |
| TaskAgent model + migration | 3-4 new Go files | New DB table, CRUD, permissions |
| Agent dispatch listener | Modify `listeners.go`, new dispatch logic | HTTP client to agent |
| API token generation | Modify `api_tokens.go` | Auto-scoped token creation |
| API routes | Modify `routes.go`, new handler files | 8-10 new endpoints |
| Frontend: model + service | 2-4 new TS files | Types, API service layer |
| Frontend: store | 1 new file | Pinia store for agents |
| Frontend: project settings UI | 1-2 new Vue components | Agent config form |
| Frontend: task detail panel | 1-2 new Vue components | Agent status display |
| Events | Modify `events.go` | New agent-related events |
| i18n | Modify `en.json` | Translation strings |
**~8-12 files changed**
**Roughly 15-25 files to create or modify.**
### Phase 2: Agent Dispatch
- Agent provider interface (`pkg/modules/agents/`)
- OpenClaw adapter
- NanoClaw adapter
- Listener on `TaskAssigneeCreatedEvent` to dispatch to agent
- Listener on `TaskAssigneeDeletedEvent` to cancel agent work
- Scoped API token auto-generation for bots
### Key Decisions to Make
**~6-10 new files**
1. **OpenClaw vs NanoClaw vs Both?**
- OpenClaw has a documented HTTP API (`/api/agent/*` endpoints)
- NanoClaw is simpler, built on Claude Agent SDK
- Could support both via an adapter pattern (common interface, provider-specific implementations)
- Recommendation: **Start with a generic interface, implement OpenClaw adapter first** since it has the richer API
### Phase 3: Monitoring & Resilience
- Cron job for stalled agent detection
- Notify assigning user on agent failure
- Agent health check endpoint
- Rate limiting for bot API calls
2. **Security model for agent callbacks**
- Scoped API tokens (recommended) vs. separate agent auth mechanism
- Should agents get their own "user" identity or act as the assigning user?
- Recommendation: **Agents act as a special "agent" user type** — auditable, distinct from human users
**~3-5 files**
3. **Task context sent to agent**
- Just the task? Task + comments? Task + related tasks? Task + project context?
- Recommendation: **Task + description + comments**, expandable later
### Phase 4: Polish
- Bot management UI in frontend settings
- Bot comment styling
- Configuration for agent dispatch behavior (what context to send, timeouts)
- Documentation
4. **Agent failure handling**
- What happens when an agent fails or goes silent?
- Recommendation: **Configurable timeout, cron-based health check, notify assigning user on failure**
5. **Where does the bridge service run?**
- Option A keeps it inside Vikunja (direct HTTP calls to agent endpoints)
- Could also be an external microservice that consumes Vikunja webhooks
- Recommendation: **Inside Vikunja** for simplicity (new `pkg/modules/agents/` package)
**~4-6 files**
---
@ -227,56 +294,62 @@ Builds on Option A with additional capabilities:
```
pkg/
├── user/
│ └── user.go # + IsBot field
├── models/
│ ├── agents.go # Agent configuration model
│ ├── agents_permissions.go # Agent permissions
│ ├── task_agents.go # Task-Agent assignment model
│ ├── task_agents_permissions.go # Task-Agent permissions
│ └── events.go # + new agent events
│ ├── bot_config.go # BotConfig model (endpoint, API key, type)
│ ├── bot_config_permissions.go # BotConfig permissions
│ ├── events.go # + BotAssignedToTaskEvent
│ └── listeners.go # + bot dispatch listener
├── modules/
│ └── agents/
│ ├── agent.go # Common agent interface
│ ├── agent.go # AgentProvider interface
│ ├── dispatch.go # Dispatch logic (called by listener)
│ ├── openclaw/
│ │ └── openclaw.go # OpenClaw adapter
│ ├── nanoclaw/
│ │ └── nanoclaw.go # NanoClaw adapter
│ └── dispatch.go # Task dispatch logic
│ │ └── openclaw.go # OpenClaw adapter
│ └── nanoclaw/
│ └── nanoclaw.go # NanoClaw adapter
├── routes/
│ └── api/v1/
│ └── agents.go # Agent API handlers
│ └── bots.go # Bot CRUD endpoints
frontend/src/
├── modelTypes/
│ └── IAgent.ts # Agent TypeScript interfaces
├── services/
│ └── agent.ts # Agent API service
├── stores/
│ └── agents.ts # Pinia store
│ └── IUser.ts # + isBot, agentType
├── models/
│ └── user.ts # + isBot default
├── components/
│ └── tasks/
│ └── partials/
│ └── agentPanel.vue # Agent status on task detail
│ └── misc/
│ └── User.vue # + bot badge
├── views/
│ └── project/
│ └── settings/
│ └── agents.vue # Agent configuration page
│ └── settings/
│ └── Bots.vue # Bot management page
```
---
## Key Design Decisions
| Decision | Recommendation | Rationale |
|----------|---------------|-----------|
| Bot identity | `is_bot` flag on User | Bots are first-class citizens, appear in all user contexts naturally |
| Agent config storage | Separate `BotConfig` table | Clean separation, easier to encrypt API keys |
| UX surface | Existing assignee picker | "Assign work to a coworker" feeling — no new concepts to learn |
| Dispatch trigger | `TaskAssigneeCreatedEvent` listener | Piggybacks on existing event system, zero new UI needed |
| Agent communication | Standard Vikunja REST API | Bot uses same API as any other client — comments, updates, attachments |
| Provider support | Adapter pattern with interface | Support OpenClaw and NanoClaw (and future providers) cleanly |
| Progress reporting | Task comments | Shows up naturally in the existing task detail view |
| Completion | Bot marks task done via API | Same as a human marking it done — no special mechanism |
---
## Summary
The integration is very feasible because Vikunja already has the key building blocks:
- **Webhooks** for outbound notifications
- **API tokens** for secure inbound callbacks
- **Event system** for reactive dispatch
- **Cron** for health monitoring
The integration is built on one core insight: **bot users are users.** By adding an `is_bot` flag and wiring up agent dispatch on assignment, the entire existing Vikunja UX — assignee picker, comments, task status, attachments — becomes the agent interface. No new UI paradigms needed.
The core new work is:
1. A new `Agent` model for storing agent configurations
2. A new `TaskAgent` model for task-agent assignments
3. HTTP dispatch logic to send tasks to agents
4. Auto-generated scoped API tokens for callbacks
5. Frontend UI for configuration and status display
The new code is focused on:
1. Bot user identity (`is_bot` flag + `BotConfig`)
2. Agent dispatch (listener + provider adapters)
3. Visual distinction (bot badge in frontend)
Start with Option A (webhook-based, ~15-25 files), validate the concept, then expand to Option B features as needed.
Everything else — permissions, API, events, comments, task lifecycle — already works.