Files
goclaw/internal/tools/team_tasks_tool.go
T
viettranx 97cacfe68b feat(teams): member task progress reminder + fix broken progress notifications
- Fix progress event payload missing TaskNumber, Subject, OwnerAgentKey,
  ProgressPercent, ProgressStep — notifications were rendering empty
- Fix progress notification format to include task name (consistent with
  dispatched/failed) and guard empty ProgressStep
- Change percent tool schema from number to integer for clarity
- Add pre-run member task reminder injecting task context before LLM loop
- Add mid-loop progress nudge every 10 iterations with suggested percent
  based on iteration ratio (handles maxIter=0 unlimited case)
- Enhance leader cross-session reminder to show progress % when available
- Strengthen TEAM.md member guidance: focus, result quality, progress rules
- Add progress bar to task list table view (matches kanban card pattern)
2026-03-17 12:43:09 +07:00

157 lines
5.5 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 (for create or update)",
},
"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",
},
"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))
}
}