From 3f2b6e258e3d4feec0f83eba3996a778c6ec2b77 Mon Sep 17 00:00:00 2001 From: viettranx Date: Mon, 16 Mar 2026 22:46:18 +0700 Subject: [PATCH] chore(teams): remove deprecated delegation tools Remove delegate_search, evaluate_loop, handoff from: - Seed data, system prompt, i18n keys/catalogs, channel events - Consumer handler (handleHandoffAnnounce), handoff route lookup - HandoffRouteData struct + PG implementation - Protocol events, MCP bridge comment - Web UI locale files (en/vi/zh) --- cmd/gateway_builtin_tools.go | 14 --- cmd/gateway_consumer.go | 3 - cmd/gateway_consumer_handlers.go | 125 ++++++++------------------ cmd/gateway_consumer_normal.go | 9 -- internal/agent/systemprompt.go | 3 - internal/channels/events.go | 3 - internal/hooks/context.go | 2 +- internal/i18n/catalog_en.go | 3 - internal/i18n/catalog_vi.go | 3 - internal/i18n/catalog_zh.go | 3 - internal/i18n/keys.go | 3 - internal/mcp/bridge_server.go | 3 +- internal/store/pg/teams.go | 57 ------------ pkg/protocol/events.go | 3 - ui/web/src/i18n/locales/en/tools.json | 3 - ui/web/src/i18n/locales/vi/tools.json | 3 - ui/web/src/i18n/locales/zh/tools.json | 3 - 17 files changed, 38 insertions(+), 205 deletions(-) diff --git a/cmd/gateway_builtin_tools.go b/cmd/gateway_builtin_tools.go index 3a4e3359..841c4149 100644 --- a/cmd/gateway_builtin_tools.go +++ b/cmd/gateway_builtin_tools.go @@ -106,20 +106,6 @@ func builtinToolSeedData() []store.BuiltinToolDef { {Name: "use_skill", DisplayName: "Use Skill", Description: "Activate a skill to use its specialized capabilities (tracing marker)", Category: "skills", Enabled: true}, {Name: "publish_skill", DisplayName: "Publish Skill", Description: "Register a skill directory (created via skill-creator) in the system database, making it discoverable and grantable to agents", Category: "skills", Enabled: true}, - // delegation (deprecated — team_tasks is the coordination mechanism now) - {Name: "delegate_search", DisplayName: "Delegate Search", Description: "Search for available delegation targets by keyword (deprecated)", Category: "delegation", Enabled: false, - Requires: []string{"managed_mode", "agent_links"}, - Metadata: json.RawMessage(`{"deprecated":true}`), - }, - {Name: "evaluate_loop", DisplayName: "Evaluate Loop", Description: "Run a generate→evaluate→revise loop between two agents (deprecated)", Category: "delegation", Enabled: false, - Requires: []string{"managed_mode", "agent_links"}, - Metadata: json.RawMessage(`{"deprecated":true}`), - }, - {Name: "handoff", DisplayName: "Handoff", Description: "Transfer the conversation to another agent (deprecated)", Category: "delegation", Enabled: false, - Requires: []string{"managed_mode", "agent_links"}, - Metadata: json.RawMessage(`{"deprecated":true}`), - }, - // teams {Name: "team_tasks", DisplayName: "Team Tasks", Description: "View, create, update, and complete tasks on the team task board", Category: "teams", Enabled: true, Requires: []string{"managed_mode", "teams"}, diff --git a/cmd/gateway_consumer.go b/cmd/gateway_consumer.go index b6426ceb..abbd32bd 100644 --- a/cmd/gateway_consumer.go +++ b/cmd/gateway_consumer.go @@ -101,9 +101,6 @@ func consumeInboundMessages(ctx context.Context, msgBus *bus.MessageBus, agents if handleDelegateAnnounce(ctx, msg, cfg, sched, channelMgr, msgBus, getAnnounceMu) { continue } - if handleHandoffAnnounce(ctx, msg, cfg, sched, channelMgr, msgBus) { - continue - } if handleTeammateMessage(ctx, msg, cfg, sched, channelMgr, teamStore, agentStore, msgBus, postTurn, &taskRunSessions) { continue } diff --git a/cmd/gateway_consumer_handlers.go b/cmd/gateway_consumer_handlers.go index 179e9f1e..409e46c3 100644 --- a/cmd/gateway_consumer_handlers.go +++ b/cmd/gateway_consumer_handlers.go @@ -309,88 +309,6 @@ func handleDelegateAnnounce( return true } -// handleHandoffAnnounce processes handoff announce messages: route initial message -// to target agent session using the "delegate" lane. -// Returns true if the message was handled (caller should continue). -func handleHandoffAnnounce( - ctx context.Context, - msg bus.InboundMessage, - cfg *config.Config, - sched *scheduler.Scheduler, - channelMgr *channels.Manager, - msgBus *bus.MessageBus, -) bool { - if !(msg.Channel == tools.ChannelSystem && strings.HasPrefix(msg.SenderID, "handoff:")) { - return false - } - - origChannel := msg.Metadata["origin_channel"] - origPeerKind := msg.Metadata["origin_peer_kind"] - origLocalKey := msg.Metadata["origin_local_key"] - origChannelType := resolveChannelType(channelMgr, origChannel) - targetAgent := msg.AgentID - if targetAgent == "" { - targetAgent = cfg.ResolveDefaultAgentID() - } - if origPeerKind == "" { - origPeerKind = string(sessions.PeerDirect) - } - - if origChannel == "" || msg.ChatID == "" { - slog.Warn("handoff announce: missing origin", "sender", msg.SenderID) - return true - } - - sessionKey := sessions.BuildScopedSessionKey(targetAgent, origChannel, sessions.PeerKind(origPeerKind), msg.ChatID, cfg.Sessions.Scope, cfg.Sessions.DmScope, cfg.Sessions.MainKey) - sessionKey = overrideSessionKeyFromLocalKey(sessionKey, origLocalKey, targetAgent, origChannel, msg.ChatID, origPeerKind) - - slog.Info("handoff announce → scheduler (delegate lane)", - "handoff", msg.SenderID, - "to", targetAgent, - "session", sessionKey, - ) - - announceUserID := msg.UserID - if origPeerKind == string(sessions.PeerGroup) && msg.ChatID != "" { - announceUserID = fmt.Sprintf("group:%s:%s", origChannel, msg.ChatID) - } - - outMeta := buildAnnounceOutMeta(origLocalKey) - - outCh := sched.Schedule(ctx, scheduler.LaneDelegate, agent.RunRequest{ - SessionKey: sessionKey, - Message: msg.Content, - Channel: origChannel, - ChannelType: origChannelType, - ChatID: msg.ChatID, - PeerKind: origPeerKind, - LocalKey: origLocalKey, - UserID: announceUserID, - RunID: fmt.Sprintf("handoff-%s", msg.Metadata["handoff_id"]), - Stream: false, - }) - - go func(origCh, chatID string, meta map[string]string) { - outcome := <-outCh - if outcome.Err != nil { - slog.Error("handoff announce: agent run failed", "error", outcome.Err) - return - } - if (outcome.Result.Content == "" && len(outcome.Result.Media) == 0) || agent.IsSilentReply(outcome.Result.Content) { - return - } - outMsg := bus.OutboundMessage{ - Channel: origCh, - ChatID: chatID, - Content: outcome.Result.Content, - Metadata: meta, - } - appendMediaToOutbound(&outMsg, outcome.Result.Media) - msgBus.PublishOutbound(outMsg) - }(origChannel, msg.ChatID, outMeta) - - return true -} // handleTeammateMessage processes teammate messages: bypass debounce, route to target // agent session using the "delegate" lane, then announce result back to lead. @@ -548,6 +466,21 @@ func handleTeammateMessage( if !alreadyTerminal { toAgent := inMeta["to_agent"] now := time.Now().UTC().Format("2006-01-02T15:04:05Z") + // Enrich event payload with task details for notifications. + taskSubject := "" + taskNumber := 0 + taskChannel := inMeta["origin_channel"] + taskChatID := inMeta["origin_chat_id"] + if currentTask != nil { + taskSubject = currentTask.Subject + taskNumber = currentTask.TaskNumber + if currentTask.Channel != "" { + taskChannel = currentTask.Channel + } + if currentTask.ChatID != "" { + taskChatID = currentTask.ChatID + } + } if outcome.Err != nil { if err := teamStore.FailTask(ctx, teamTaskID, teamID, outcome.Err.Error()); err != nil { slog.Warn("auto-complete: FailTask error", "task_id", teamTaskID, "error", err) @@ -555,12 +488,17 @@ func handleTeammateMessage( msgBus.Broadcast(bus.Event{ Name: protocol.EventTeamTaskFailed, Payload: protocol.TeamTaskEventPayload{ - TeamID: teamID.String(), - TaskID: teamTaskID.String(), - Status: store.TeamTaskStatusFailed, - Timestamp: now, - ActorType: "agent", - ActorID: toAgent, + TeamID: teamID.String(), + TaskID: teamTaskID.String(), + TaskNumber: taskNumber, + Subject: taskSubject, + Status: store.TeamTaskStatusFailed, + Reason: outcome.Err.Error(), + Channel: taskChannel, + ChatID: taskChatID, + Timestamp: now, + ActorType: "agent", + ActorID: toAgent, }, }) } @@ -580,8 +518,12 @@ func handleTeammateMessage( Payload: protocol.TeamTaskEventPayload{ TeamID: teamID.String(), TaskID: teamTaskID.String(), + TaskNumber: taskNumber, + Subject: taskSubject, Status: store.TeamTaskStatusCompleted, OwnerAgentKey: toAgent, + Channel: taskChannel, + ChatID: taskChatID, Timestamp: now, ActorType: "agent", ActorID: toAgent, @@ -865,7 +807,12 @@ func buildTaskBoardSnapshot(ctx context.Context, teamStore store.TeamStore, team if teamStore == nil || originTraceID == "" { return "" } - allTasks, err := teamStore.ListTasks(ctx, teamID, "", store.TeamTaskFilterAll, "", "", chatID, 0) + // Shared workspace: show all tasks across chats. + snapshotChatID := chatID + if team, err := teamStore.GetTeam(ctx, teamID); err == nil && tools.IsSharedWorkspace(team.Settings) { + snapshotChatID = "" + } + allTasks, err := teamStore.ListTasks(ctx, teamID, "", store.TeamTaskFilterAll, "", "", snapshotChatID, 0) if err != nil || len(allTasks) == 0 { return "" } diff --git a/cmd/gateway_consumer_normal.go b/cmd/gateway_consumer_normal.go index a8ee61ec..9d6536d0 100644 --- a/cmd/gateway_consumer_normal.go +++ b/cmd/gateway_consumer_normal.go @@ -43,15 +43,6 @@ func processNormalMessage( agentID = resolveAgentRoute(cfg, msg.Channel, msg.ChatID, msg.PeerKind) } - // Check handoff routing override - if teamStore != nil && msg.AgentID == "" { - if route, _ := teamStore.GetHandoffRoute(ctx, msg.Channel, msg.ChatID); route != nil { - agentID = route.ToAgentKey - slog.Info("inbound: handoff route active", - "channel", msg.Channel, "chat", msg.ChatID, "to", agentID) - } - } - agentLoop, err := agents.Get(agentID) if err != nil { slog.Warn("inbound: agent not found", "agent", agentID, "channel", msg.Channel) diff --git a/internal/agent/systemprompt.go b/internal/agent/systemprompt.go index 5d5ec2f9..abde4f3e 100644 --- a/internal/agent/systemprompt.go +++ b/internal/agent/systemprompt.go @@ -89,9 +89,6 @@ var coreToolSummaries = map[string]string{ "create_image": "Generate images from text descriptions using AI", "create_audio": "Generate music or sound effects from text descriptions using AI", "knowledge_graph_search": "Find people, projects, and their connections — use for relationship questions (who works with whom, project dependencies) that memory_search may miss", - "handoff": "Transfer conversation to another agent (ONLY when user explicitly asks to switch agents — NOT for task delegation)", - "evaluate_loop": "Run a generate→evaluate→revise loop between two agents for quality-critical tasks", - "delegate_search": "Search for agents by expertise to find the right delegation target", "team_tasks": "Team task board — track progress, manage dependencies (spawn auto-creates delegation tasks)", "team_message": "Send messages to teammates (progress updates, questions)", diff --git a/internal/channels/events.go b/internal/channels/events.go index 4daf7095..e96b722d 100644 --- a/internal/channels/events.go +++ b/internal/channels/events.go @@ -375,7 +375,6 @@ var toolStatusMap = map[string]string{ "browser": "🌐 Browsing...", // Delegation & teams "spawn": "👥 Delegating task...", - "handoff": "🔄 Handing off...", "team_tasks": "📋 Managing team tasks...", "team_message": "💬 Sending team message...", // Sessions @@ -388,8 +387,6 @@ var toolStatusMap = map[string]string{ "cron": "⏰ Managing schedule...", "skill_search": "🔍 Searching skills...", "use_skill": "🧩 Using skill...", - "delegate_search": "🔍 Searching delegates...", - "evaluate_loop": "🔄 Evaluating...", "mcp_tool_search": "🔌 Searching MCP tools...", } diff --git a/internal/hooks/context.go b/internal/hooks/context.go index f36a31a6..8282b4e0 100644 --- a/internal/hooks/context.go +++ b/internal/hooks/context.go @@ -7,7 +7,7 @@ type ctxKey string const skipHooksKey ctxKey = "skip_hooks" // WithSkipHooks returns a context that signals hook evaluation should be skipped. -// Used by evaluate_loop and agent evaluator to prevent recursive hook firing. +// Used by agent evaluator to prevent recursive hook firing. func WithSkipHooks(ctx context.Context, skip bool) context.Context { return context.WithValue(ctx, skipHooksKey, skip) } diff --git a/internal/i18n/catalog_en.go b/internal/i18n/catalog_en.go index 346a0321..7c1e4dd7 100644 --- a/internal/i18n/catalog_en.go +++ b/internal/i18n/catalog_en.go @@ -169,9 +169,6 @@ func init() { MsgToolSpawn: "Spawn a subagent for background work or delegate a task to a linked agent", MsgToolSkillSearch: "Search for available skills by keyword or description to find relevant capabilities", MsgToolUseSkill: "Activate a skill to use its specialized capabilities (tracing marker)", - MsgToolDelegateSearch: "Search for available delegation targets by keyword when there are too many linked agents to list", - MsgToolEvaluateLoop: "Run a generate→evaluate→revise loop between two agents for quality-critical output", - MsgToolHandoff: "Transfer the conversation to another agent — the user will talk directly to that agent", MsgToolTeamTasks: "View, create, update, and complete tasks on the team task board", MsgToolTeamMessage: "Send a direct message or broadcast to teammates in the agent team", }) diff --git a/internal/i18n/catalog_vi.go b/internal/i18n/catalog_vi.go index 76666b17..1322b121 100644 --- a/internal/i18n/catalog_vi.go +++ b/internal/i18n/catalog_vi.go @@ -169,9 +169,6 @@ func init() { MsgToolSpawn: "Tạo subagent chạy nền hoặc giao việc cho agent đã liên kết", MsgToolSkillSearch: "Tìm kiếm kỹ năng có sẵn theo từ khóa hoặc mô tả", MsgToolUseSkill: "Kích hoạt kỹ năng để sử dụng khả năng chuyên biệt (đánh dấu tracing)", - MsgToolDelegateSearch: "Tìm kiếm agent mục tiêu ủy quyền theo từ khóa khi có quá nhiều agent liên kết", - MsgToolEvaluateLoop: "Chạy vòng lặp tạo→đánh giá→sửa đổi giữa hai agent cho nội dung cần chất lượng cao", - MsgToolHandoff: "Chuyển cuộc hội thoại sang agent khác — người dùng sẽ nói chuyện trực tiếp với agent đó", MsgToolTeamTasks: "Xem, tạo, cập nhật và hoàn thành tác vụ trên bảng tác vụ nhóm", MsgToolTeamMessage: "Gửi tin nhắn trực tiếp hoặc broadcast đến đồng đội trong nhóm agent", }) diff --git a/internal/i18n/catalog_zh.go b/internal/i18n/catalog_zh.go index 524779cb..edfcdfef 100644 --- a/internal/i18n/catalog_zh.go +++ b/internal/i18n/catalog_zh.go @@ -169,9 +169,6 @@ func init() { MsgToolSpawn: "创建子代理执行后台工作或将任务委派给已链接的代理", MsgToolSkillSearch: "按关键字或描述搜索可用技能以查找相关功能", MsgToolUseSkill: "激活技能以使用其专门功能(追踪标记)", - MsgToolDelegateSearch: "当链接代理过多时,按关键字搜索可用的委派目标", - MsgToolEvaluateLoop: "在两个代理之间运行生成→评估→修改循环,用于高质量输出", - MsgToolHandoff: "将对话转移给另一个代理——用户将直接与该代理交谈", MsgToolTeamTasks: "查看、创建、更新和完成团队任务板上的任务", MsgToolTeamMessage: "向代理团队中的队友发送直接消息或广播", }) diff --git a/internal/i18n/keys.go b/internal/i18n/keys.go index dd32f34b..5ec3f0c7 100644 --- a/internal/i18n/keys.go +++ b/internal/i18n/keys.go @@ -170,9 +170,6 @@ const ( MsgToolSpawn = "core.tool.spawn" MsgToolSkillSearch = "core.tool.skill_search" MsgToolUseSkill = "core.tool.use_skill" - MsgToolDelegateSearch = "core.tool.delegate_search" - MsgToolEvaluateLoop = "core.tool.evaluate_loop" - MsgToolHandoff = "core.tool.handoff" MsgToolTeamTasks = "core.tool.team_tasks" MsgToolTeamMessage = "core.tool.team_message" ) diff --git a/internal/mcp/bridge_server.go b/internal/mcp/bridge_server.go index 334738d7..2c75706e 100644 --- a/internal/mcp/bridge_server.go +++ b/internal/mcp/bridge_server.go @@ -16,8 +16,7 @@ import ( ) // BridgeToolNames is the subset of GoClaw tools exposed via the MCP bridge. -// Excluded: spawn (agent loop), create_forum_topic (channels), -// handoff/delegate_search/evaluate_loop (require agent loop context). +// Excluded: spawn (agent loop), create_forum_topic (channels). var BridgeToolNames = map[string]bool{ // Filesystem "read_file": true, diff --git a/internal/store/pg/teams.go b/internal/store/pg/teams.go index cc4ea9d1..a0911b85 100644 --- a/internal/store/pg/teams.go +++ b/internal/store/pg/teams.go @@ -267,63 +267,6 @@ func (s *PGTeamStore) KnownUserIDs(ctx context.Context, teamID uuid.UUID, limit return users, rows.Err() } -// ============================================================ -// Handoff routing -// ============================================================ - -func (s *PGTeamStore) SetHandoffRoute(ctx context.Context, route *store.HandoffRouteData) error { - if route.ID == uuid.Nil { - route.ID = store.GenNewID() - } - route.CreatedAt = time.Now() - - var teamID *uuid.UUID - if route.TeamID != uuid.Nil { - teamID = &route.TeamID - } - - _, err := s.db.ExecContext(ctx, - `INSERT INTO handoff_routes (id, channel, chat_id, from_agent_key, to_agent_key, reason, created_by, created_at, team_id) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT (channel, chat_id) - DO UPDATE SET to_agent_key = EXCLUDED.to_agent_key, from_agent_key = EXCLUDED.from_agent_key, - reason = EXCLUDED.reason, created_by = EXCLUDED.created_by, created_at = EXCLUDED.created_at, - team_id = EXCLUDED.team_id`, - route.ID, route.Channel, route.ChatID, route.FromAgentKey, route.ToAgentKey, - route.Reason, route.CreatedBy, route.CreatedAt, teamID, - ) - return err -} - -func (s *PGTeamStore) GetHandoffRoute(ctx context.Context, channel, chatID string) (*store.HandoffRouteData, error) { - var d store.HandoffRouteData - var teamID *uuid.UUID - err := s.db.QueryRowContext(ctx, - `SELECT id, channel, chat_id, from_agent_key, to_agent_key, reason, created_by, created_at, team_id - FROM handoff_routes WHERE channel = $1 AND chat_id = $2`, - channel, chatID).Scan( - &d.ID, &d.Channel, &d.ChatID, &d.FromAgentKey, &d.ToAgentKey, - &d.Reason, &d.CreatedBy, &d.CreatedAt, &teamID, - ) - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - if err != nil { - return nil, err - } - if teamID != nil { - d.TeamID = *teamID - } - return &d, nil -} - -func (s *PGTeamStore) ClearHandoffRoute(ctx context.Context, channel, chatID string) error { - _, err := s.db.ExecContext(ctx, - `DELETE FROM handoff_routes WHERE channel = $1 AND chat_id = $2`, - channel, chatID) - return err -} - // ============================================================ // Scan helpers // ============================================================ diff --git a/pkg/protocol/events.go b/pkg/protocol/events.go index d47fd00a..5c6c40c0 100644 --- a/pkg/protocol/events.go +++ b/pkg/protocol/events.go @@ -22,9 +22,6 @@ const ( // Agent summoning events (predefined agent setup via LLM). EventAgentSummoning = "agent.summoning" - // Agent handoff event (payload: from_agent, to_agent, reason). - EventHandoff = "handoff" - // Team activity events (real-time team workflow visibility). EventTeamTaskCreated = "team.task.created" EventTeamTaskCompleted = "team.task.completed" diff --git a/ui/web/src/i18n/locales/en/tools.json b/ui/web/src/i18n/locales/en/tools.json index a168a1f7..80766217 100644 --- a/ui/web/src/i18n/locales/en/tools.json +++ b/ui/web/src/i18n/locales/en/tools.json @@ -183,9 +183,6 @@ "spawn": "Spawn a subagent to handle a task in the background", "skill_search": "Search for available skills by keyword or description to find relevant capabilities", "use_skill": "Activate a skill to use its specialized capabilities (tracing marker)", - "delegate_search": "Search for available delegation targets (deprecated)", - "evaluate_loop": "Run a generate→evaluate→revise loop between two agents (deprecated)", - "handoff": "Transfer the conversation to another agent (deprecated)", "team_tasks": "View, create, update, and complete tasks on the team task board", "team_message": "Send a direct message or broadcast to teammates in the agent team" } diff --git a/ui/web/src/i18n/locales/vi/tools.json b/ui/web/src/i18n/locales/vi/tools.json index 0b637869..62b7025b 100644 --- a/ui/web/src/i18n/locales/vi/tools.json +++ b/ui/web/src/i18n/locales/vi/tools.json @@ -183,9 +183,6 @@ "spawn": "Tạo subagent cho công việc nền", "skill_search": "Tìm kiếm skill khả dụng theo từ khóa hoặc mô tả để tìm khả năng phù hợp", "use_skill": "Kích hoạt skill để sử dụng khả năng chuyên biệt (đánh dấu tracing)", - "delegate_search": "Tìm kiếm mục tiêu ủy quyền (ngừng sử dụng)", - "evaluate_loop": "Chạy vòng lặp tạo→đánh giá→chỉnh sửa giữa hai agent (ngừng sử dụng)", - "handoff": "Chuyển cuộc hội thoại sang agent khác (ngừng sử dụng)", "team_tasks": "Xem, tạo, cập nhật và hoàn thành tác vụ trên bảng tác vụ nhóm", "team_message": "Gửi tin nhắn trực tiếp hoặc broadcast đến đồng đội trong nhóm agent" } diff --git a/ui/web/src/i18n/locales/zh/tools.json b/ui/web/src/i18n/locales/zh/tools.json index 7aa7098f..3d2e5b81 100644 --- a/ui/web/src/i18n/locales/zh/tools.json +++ b/ui/web/src/i18n/locales/zh/tools.json @@ -119,9 +119,6 @@ "spawn": "生成子Agent进行后台工作", "skill_search": "按关键字或描述搜索可用Skill以找到相关能力", "use_skill": "激活Skill以使用其专业能力(追踪标记)", - "delegate_search": "按关键字搜索可用委托目标(已弃用)", - "evaluate_loop": "在两个Agent之间运行生成→评估→修订循环(已弃用)", - "handoff": "将对话转移给另一个Agent(已弃用)", "team_tasks": "查看、创建、更新和完成团队任务板上的任务", "team_message": "向Agent团队中的队友发送直接消息或广播" }