mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-29 14:21:29 +00:00
* feat(teams): refactor attachments, remove team_message, add task comments UI Major team system refactoring: - Drop team_workspace_files, team_workspace_file_versions, team_workspace_comments, team_messages tables; replace team_task_attachments with path-based schema - Add denormalized comment_count/attachment_count on team_tasks for dashboard perf - Auto-track file writes as task attachments via WorkspaceInterceptor - Remove team_message tool entirely (tool, store, i18n, builtin_tools, MCP bridge) - Members communicate via task comments; approve/reject use comments for audit trail - Add commented/new_task notification types to TeamNotifyConfig - Enrich task completion announce with member comments - User-created tasks stay pending (backlog) — no auto-assign to leader - Configurable member request tasks (member_requests.enabled in team settings) - Structured task description template in TEAM.md for v2 leads - HTTP attachment download endpoint with IDOR + path traversal protection - Web UI: count badges on task list, comments section with input, download button - Team settings UI: completed/commented/new_task toggles, member requests section * feat(teams): priority dispatch, compact prompting, realtime comments - Priority dispatch: DispatchUnblockedTasks dispatches only 1 task per owner per round (highest priority first). Fixes cancel bug where CancelSession killed innocent queued tasks. - Prompt rework: Replace verbose Task Decomposition (25 lines) with compact Task Planning (8 lines). Add explicit UUID warning and sequencing guidance for weak models (Qwen, MiniMax). - Recent comments in dispatch: buildRecentCommentsSummary appends 3 most recent comments to re-dispatched tasks (reject, retry, stale). - Enrich comment event payload with TaskNumber, Subject, CommentText (truncated 500 runes, UTF-8 safe). - UI: Board subscribes to TEAM_TASK_COMMENTED for realtime comment_count badge updates. Task detail dialog auto-refreshes comments on event. - Tool description hint: guide models to write self-contained task descriptions with clear objectives and context. * perf(teams): add ListRecentTaskComments with SQL LIMIT Dispatch only needs 3 most recent comments — avoid fetching all. New ListRecentTaskComments(ctx, taskID, limit) uses ORDER BY DESC LIMIT N then reverses to chronological order. * feat(teams): add subject embedding for semantic task search + improve prompting - Add vector(1536) embedding column to team_tasks with HNSW index - Implement hybrid search: FTS (0.3) + cosine similarity (0.7) with graceful fallback - Auto-generate embeddings on task create/update, backfill existing tasks on startup - Wire embedding provider into PGTeamStore via gateway_setup - Change FTS from OR to AND with prefix matching for precise keyword search - Reduce search page size from 30 to 5 to save tokens - Rename migration 000023 → 000024, bump RequiredSchemaVersion to 24 - Update TEAM.md hints: prefer search over list, batch task creation with blocked_by - Add anti-pattern examples to prevent sequential task creation
157 lines
5.7 KiB
Go
157 lines
5.7 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// TeamTasksTool exposes the shared team task list to agents.
|
|
// Actions: list, get, create, claim, complete, cancel, search, review, comment, progress, attach, update.
|
|
type TeamTasksTool struct {
|
|
manager *TeamToolManager
|
|
}
|
|
|
|
func NewTeamTasksTool(manager *TeamToolManager) *TeamTasksTool {
|
|
return &TeamTasksTool{manager: manager}
|
|
}
|
|
|
|
func (t *TeamTasksTool) Name() string { return "team_tasks" }
|
|
|
|
func (t *TeamTasksTool) Description() string {
|
|
return "Manage the shared team task list (create, claim, complete, track progress). See TEAM.md for available actions and team context."
|
|
}
|
|
|
|
func (t *TeamTasksTool) Parameters() map[string]any {
|
|
return map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"action": map[string]any{
|
|
"type": "string",
|
|
"description": "'list', 'get', 'create', 'claim', 'complete', 'cancel', 'approve', 'reject', 'search', 'review', 'comment', 'progress', 'attach', 'update', 'ask_user', or 'clear_ask_user'. " +
|
|
"ask_user: set a periodic reminder that is sent to the USER (not the team) when you need the user's input/decision to continue (e.g. 'Which design do you prefer?'). ONLY use when you have a question for the user. Do NOT use for status updates, waiting for teammates, or notifications — use 'progress' instead. " +
|
|
"clear_ask_user: cancel a previously set ask_user reminder. " +
|
|
"retry: re-dispatch a stale or failed task.",
|
|
},
|
|
"task_id": map[string]any{
|
|
"type": "string",
|
|
"description": "Task UUID (required for most actions except list, create, search). When working on a dispatched task, this is auto-resolved from context — you can omit it for complete/progress/comment.",
|
|
},
|
|
"subject": map[string]any{
|
|
"type": "string",
|
|
"description": "Task subject (required for create, optional for update)",
|
|
},
|
|
"description": map[string]any{
|
|
"type": "string",
|
|
"description": "Task description with clear objectives and requirements. Include all context the assignee needs to complete the task independently.",
|
|
},
|
|
"result": map[string]any{
|
|
"type": "string",
|
|
"description": "Result summary (required for complete)",
|
|
},
|
|
"text": map[string]any{
|
|
"type": "string",
|
|
"description": "Text content: comment text, cancel/reject reason, progress update, or ask_user reminder question (must be a question asking the user for input/decision)",
|
|
},
|
|
"status": map[string]any{
|
|
"type": "string",
|
|
"description": "Filter for list: '' (all, default), 'active', 'completed', 'in_review'",
|
|
},
|
|
"query": map[string]any{
|
|
"type": "string",
|
|
"description": "Search query for action=search (supports keyword AND semantic matching). Use search before create to check for duplicates.",
|
|
},
|
|
"priority": map[string]any{
|
|
"type": "number",
|
|
"description": "Priority, higher = more important (for create, default 0)",
|
|
},
|
|
"blocked_by": map[string]any{
|
|
"type": "array",
|
|
"items": map[string]any{"type": "string"},
|
|
"description": "Task IDs that must complete first (for create/update)",
|
|
},
|
|
"require_approval": map[string]any{
|
|
"type": "boolean",
|
|
"description": "Require user approval before claim (for create, default false)",
|
|
},
|
|
"percent": map[string]any{
|
|
"type": "integer",
|
|
"description": "Progress percentage 0-100 (for progress action)",
|
|
},
|
|
"file_id": map[string]any{
|
|
"type": "string",
|
|
"description": "Workspace file ID (for attach)",
|
|
},
|
|
"assignee": map[string]any{
|
|
"type": "string",
|
|
"description": "Agent key to assign task to (REQUIRED for create). Auto-dispatches to that team member.",
|
|
},
|
|
"page": map[string]any{
|
|
"type": "number",
|
|
"description": "Page number for list/search (default 1, 30 per page)",
|
|
},
|
|
},
|
|
"required": []string{"action"},
|
|
}
|
|
}
|
|
|
|
// v2Actions lists team_tasks actions that require team version >= 2.
|
|
var v2Actions = map[string]bool{
|
|
"approve": true, "reject": true, "review": true, "comment": true,
|
|
"progress": true, "attach": true, "update": true,
|
|
"ask_user": true, "clear_ask_user": true, "retry": true,
|
|
}
|
|
|
|
func (t *TeamTasksTool) Execute(ctx context.Context, args map[string]any) *Result {
|
|
action, _ := args["action"].(string)
|
|
|
|
// Gate v2-only actions: resolve team once and check version.
|
|
if v2Actions[action] {
|
|
team, _, err := t.manager.resolveTeam(ctx)
|
|
if err != nil {
|
|
return ErrorResult(err.Error())
|
|
}
|
|
if !IsTeamV2(team) {
|
|
return ErrorResult(fmt.Sprintf("action '%s' requires team version 2 — upgrade in team settings", action))
|
|
}
|
|
}
|
|
|
|
switch action {
|
|
case "list":
|
|
return t.executeList(ctx, args)
|
|
case "get":
|
|
return t.executeGet(ctx, args)
|
|
case "create":
|
|
return t.executeCreate(ctx, args)
|
|
case "claim":
|
|
return t.executeClaim(ctx, args)
|
|
case "complete":
|
|
return t.executeComplete(ctx, args)
|
|
case "cancel":
|
|
return t.executeCancel(ctx, args)
|
|
case "approve":
|
|
return t.executeApprove(ctx, args)
|
|
case "reject":
|
|
return t.executeReject(ctx, args)
|
|
case "search":
|
|
return t.executeSearch(ctx, args)
|
|
case "review":
|
|
return t.executeReview(ctx, args)
|
|
case "comment":
|
|
return t.executeComment(ctx, args)
|
|
case "progress":
|
|
return t.executeProgress(ctx, args)
|
|
case "attach":
|
|
return t.executeAttach(ctx, args)
|
|
case "update":
|
|
return t.executeUpdate(ctx, args)
|
|
case "ask_user":
|
|
return t.executeAskUser(ctx, args)
|
|
case "clear_ask_user":
|
|
return t.executeClearAskUser(ctx, args)
|
|
case "retry":
|
|
return t.executeRetry(ctx, args)
|
|
default:
|
|
return ErrorResult(fmt.Sprintf("unknown action: %s (use list, get, create, claim, complete, cancel, search, review, comment, progress, attach, update, ask_user, clear_ask_user, or retry)", action))
|
|
}
|
|
}
|