Introduces a comprehensive agent management system and a new research interface within the cockpit. This includes backend API endpoints for CRUD operations on agents, a new agent lifecycle manager, and frontend components for managing agents, skills, and research workflows. - feat(backend): add REST API for agent lifecycle management (list, create, update, delete, import) - feat(backend): implement `pkg/agent/manager` for agent lifecycle control - feat(gateway): register agent API routes in the gateway - feat(frontend): add cockpit tabs for Agents, Skills, and Research - feat(frontend): implement AgentsPage and ResearchPage components - feat(frontend): add `use-agents` and `use-cockpit-skills` hooks - docs: add Agent Management API documentation and update project map - refactor(tools): update ToolSkill metadata handling and regex parsing
9.7 KiB
PicoClaw Integration Fix Design
Date: 2026-05-07
Scope
Fix 4 integration gaps identified in the CLI/BE/FE audit:
- Agent delete response mismatch — frontend expects
{message}but backend sends{status} - Refactor agent manager — eliminate code duplication between
pkg/agent/managerandweb/backend/api/agents.go - Add Cron API — expose CRUD for cron jobs via backend REST API
- Add MCP API — expose CRUD for MCP servers via backend REST API
- Wire tool registry to API — make
GET /api/toolsquerypkg/tools/registry.goinstead of static catalog - Add MCP + Cron UI pages — create frontend pages for MCP and Cron management
Non-Goals
- Health UI (deferred)
- Web search config UI (already exists per subagent findings — web search tab exists)
- Agent CRUD CLI (CLI only provides direct chat, not CRUD — acceptable)
- Skills system redesign (already well-integrated)
1. Agent Delete Response Mismatch
Problem
web/frontend/src/api/agents.ts:70 expects Promise<{ message: string }> but web/backend/api/agents.go:460 sends { status: "ok" }.
Fix
Update the frontend to match the backend response:
// web/frontend/src/api/agents.ts:70
export async function deleteAgent(slug: string): Promise<{ status: string }> {
// ... no other changes needed
}
The frontend agents-page.tsx uses deleteAgent() but only logs success via toast — it doesn't read the response body. So this is a low-risk fix.
2. Refactor Agent Manager
Problem
pkg/agent/manager/manager.go and web/backend/api/agents.go both implement identical agent CRUD logic with duplicated types, regex, and file I/O. They diverge over time and cause maintenance burden.
Approach: Use pkg/agent/manager from API layer
The backend API handler (web/backend/api/agents.go) should delegate to manager.NewManager(). Both use the same workspace path ~/.picoclaw/workspace/agents.
Changes
-
In
web/backend/api/agents.go:- Remove the duplicated
agentManagerstruct,expandAgentPath,slugRegex, and all CRUD methods (List, Get, Create, Update, Delete, Import) - Instead, import
github.com/sipeed/picoclaw/pkg/agent/managerand usemanager.NewManager("") - Keep the HTTP handler wrappers and request/response types
agentManagerbecomes a thin wrapper:&manager.Manager{workspacePath: mgr.workspacePath}— or just usemanager.NewManagerdirectly
- Remove the duplicated
-
The
agentstruct type inagents.goandpkg/agent/manager/types.goneed to be compared — one may have more fields. Check if they can be unified. -
Remove
agentCreateRequest,agentUpdateRequest,agentImportRequestifmanager.AgentCreateRequest/AgentUpdateRequestexist and are compatible. Ifpkg/agent/manager/types.gouses different struct names, either alias or adapt.
Decision: Check pkg/agent/manager/types.go to see the actual request/response types before deciding on aliasing vs adaptation.
Fallback if pkg/agent/manager lacks types
If pkg/agent/manager/types.go doesn't export request/response types, keep the HTTP-layer structs in agents.go but have the handler methods delegate to manager.Manager for actual file I/O. This avoids breaking the API contract while eliminating duplication.
3. Add Cron API
CLI Reference
cmd/picoclaw/internal/cron/ has: list, add, remove, enable, disable. Jobs stored at workspace/cron/jobs.json.
New Backend Endpoints (web/backend/api/cron.go)
| Method | Path | Handler | Purpose |
|---|---|---|---|
| GET | /api/cron/jobs |
handleListCronJobs | List all jobs |
| POST | /api/cron/jobs |
handleAddCronJob | Add a new job |
| DELETE | /api/cron/jobs/{id} |
handleDeleteCronJob | Delete a job |
| POST | /api/cron/jobs/{id}/enable |
handleEnableCronJob | Enable a job |
| POST | /api/cron/jobs/{id}/disable |
handleDisableCronJob | Disable a job |
Implementation
pkg/cron/service.gohasNewCronService(path, nil)and methods:ListJobs(),AddJob(),DeleteJob(),EnableJob(),DisableJob()- Cron service reads/writes
workspace/cron/jobs.json - Workspace path comes from config:
cfg.WorkspacePath()→ append/cron/jobs.json - Register routes in
router.go→registerCronRoutes(mux)
Request/Response Shapes
// GET /api/cron/jobs
{ "jobs": [{ "id": "uuid", "name": "string", "schedule": { "kind": "every"|"cron", "every_ms": 3600000, "expr": "0 9 * * *" }, "message": "string", "channel": "", "to": "", "enabled": true, "last_run": 1234567890, "next_run": 1234567890 }] }
// POST /api/cron/jobs
{ "name": "string", "every": 0, "cron": "0 9 * * *", "message": "string", "channel": "", "to": "" }
// Response: { "job": {...} }
// DELETE /api/cron/jobs/{id} → { "status": "ok" }
// POST /api/cron/jobs/{id}/enable → { "status": "ok" }
// POST /api/cron/jobs/{id}/disable → { "status": "ok" }
4. Add MCP API
CLI Reference
cmd/picoclaw/internal/mcp/ has: add, remove, list, edit, test, show. MCP servers stored in config.json under tools.mcp.servers.
New Backend Endpoints (web/backend/api/mcp.go)
| Method | Path | Handler | Purpose |
|---|---|---|---|
| GET | /api/mcp/servers |
handleListMCPServers | List all MCP servers |
| GET | /api/mcp/servers/{name} |
handleGetMCPServer | Get server config + test |
| POST | /api/mcp/servers |
handleAddMCPServer | Add new MCP server |
| PUT | /api/mcp/servers/{name} |
handleUpdateMCPServer | Update server |
| DELETE | /api/mcp/servers/{name} |
handleDeleteMCPServer | Remove server |
| POST | /api/mcp/servers/{name}/test |
handleTestMCPServer | Probe server health |
Implementation
- Read/write
config.jsonviapkg/config - MCP server config structure:
name,command,args[],env{},enabled - Workspace path from config for MCP server working dir
- For
test: use same probe logic fromcmd/picoclaw/internal/mcp/probe.go(import if available, or replicate inline — check if probe.go exists)
Request/Response Shapes
// GET /api/mcp/servers
{ "servers": [{ "name": "string", "command": "string", "args": [], "env": {}, "enabled": true, "status": "ok|error|disabled" }] }
// POST /api/mcp/servers
{ "name": "string", "command": "string", "args": [], "env": {}, "enabled": true }
// PUT /api/mcp/servers/{name}
// DELETE /api/mcp/servers/{name} → { "status": "ok" }
// POST /api/mcp/servers/{name}/test → { "status": "ok", "tool_count": 12 }
5. Wire Tool Registry to API
Problem
web/backend/api/tools.go has a hardcoded static toolCatalog (lines 75-202) instead of querying pkg/tools/registry.go at runtime.
Fix
Replace the static catalog with a dynamic query:
// In handleListTools, replace static toolCatalog with registry.GetAll()
registry := h.getToolRegistry() // inject or use global singleton
tools := registry.GetAll()
// Map to API response format
The challenge: the API response format may differ from registry.GetAll() output. Need to map:
registry.ToolhasName(),Description(),Parameters(),Execute()- API response needs
name,description,category,parameters(JSON schema)
Approach
Create an adapter function that maps Tool interface to ToolSupportItem API response struct. Run it at request time so the API always reflects live registry.
Fallback
If the registry doesn't expose enough metadata (e.g., category), enrich the registry with a Category() method on the Tool interface. Or use a static map for category lookup if adding method to interface is too intrusive.
6. Add MCP + Cron UI Pages
Frontend Routes
/agent/mcp— MCP server management page/agent/cron— Cron job management page
Route Registration
Add to web/frontend/src/routes/agent.tsx (or wherever nested agent routes are defined):
// agent/mcp route
// agent/cron route
Components to Create
web/frontend/src/components/agent/mcp/mcp-page.tsx— MCP server list with add/edit/deleteweb/frontend/src/components/agent/mcp/mcp-form-sheet.tsx— Add/edit MCP server formweb/frontend/src/components/agent/cron/cron-page.tsx— Cron job list with add/delete/enable/disableweb/frontend/src/components/agent/cron/cron-form-dialog.tsx— Add cron job form
API Clients to Add
web/frontend/src/api/cron.ts— CRUD hooks for cron APIweb/frontend/src/api/mcp.ts— CRUD hooks for MCP API
Design
Follow existing patterns (skills-page.tsx, models-page.tsx). Use sheets/dialogs for forms, list view for main display.
Implementation Order
- Fix agent delete response mismatch (frontend) — trivial, no risk
- Add Cron backend API — self-contained, no dependencies
- Add MCP backend API — self-contained, no dependencies
- Wire tool registry to API — moderate, touches core pkg
- Refactor agent manager (use pkg/agent/manager from API) — highest risk, do last
- Add MCP UI — depends on MCP backend API
- Add Cron UI — depends on Cron backend API
Steps 2, 3, 6, 7 are independent and can run in parallel via subagents.
Failure Modes
- Agent manager refactor breaks API: If
pkg/agent/managertypes don't match API contract, fallback to keeping handler methods but calling into manager for file I/O only. - Tool registry mapping loses fields: If
Toolinterface lacks category, add a static category map inapi/tools.gokeyed by tool name, maintained manually until registry is enriched. - Cron/MCP UI diverges from design system: Follow existing page patterns (skills-page.tsx, models-page.tsx) exactly — same component library, same layout patterns.
- MCP server probe fails in API: The CLI probe uses context with timeout. API handler should also use context with timeout. Fail gracefully with status="error" rather than crashing.