From e043241deac15f4fcb5b9827100a7abf84eba29d Mon Sep 17 00:00:00 2001 From: viettranx Date: Sun, 12 Apr 2026 12:37:53 +0700 Subject: [PATCH] fix(tools): add missing tools to goclaw group and relax agent update ownership - Add 10 missing tools to the goclaw group in policy.go: skill_manage, publish_skill, use_skill, delegate, memory_expand, knowledge_graph_search, vault_search, create_audio, datetime, heartbeat. Fixes skill creation permission denied when agents use group:goclaw allow list. - Relax agent update ownership check: tenant admins can now update any agent in their tenant (adminMiddleware + tenant-scoped GetByID already ensures proper authorization). Previously only agent owner or system owner could update. - Improve agent update error logging: include user_id and tenant_id in structured log, return actual error message instead of generic "internal error" for better debugging. --- internal/http/agents.go | 14 +++++++------- internal/tools/policy.go | 12 ++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/http/agents.go b/internal/http/agents.go index 5741ad05..48bc3e67 100644 --- a/internal/http/agents.go +++ b/internal/http/agents.go @@ -347,16 +347,15 @@ func (h *AgentsHandler) handleUpdate(w http.ResponseWriter, r *http.Request) { return } - // Only owner can update + // Tenant admins can update any agent in their tenant (adminMiddleware already + // verified RoleAdmin). System owners can update any agent across tenants. + // GetByID respects tenant scoping from context, so if the agent is returned + // it belongs to the caller's tenant. ag, err := h.agents.GetByID(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, protocol.ErrNotFound, i18n.T(locale, i18n.MsgNotFound, "agent", id.String())) return } - if userID != "" && ag.OwnerID != userID && !h.isOwnerUser(userID) { - writeError(w, http.StatusForbidden, protocol.ErrUnauthorized, i18n.T(locale, i18n.MsgOwnerOnly, "update agent")) - return - } var updates map[string]any if !bindJSON(w, r, locale, &updates) { @@ -426,8 +425,9 @@ func (h *AgentsHandler) handleUpdate(w http.ResponseWriter, r *http.Request) { } if err := h.agents.Update(r.Context(), id, allowed); err != nil { - slog.Error("agents.update", "id", id, "error", err) - writeError(w, http.StatusInternalServerError, protocol.ErrInternal, i18n.T(locale, i18n.MsgFailedToUpdate, "agent", "internal error")) + slog.Error("agents.update", "id", id, "user_id", userID, + "tenant_id", store.TenantIDFromContext(r.Context()), "error", err) + writeError(w, http.StatusInternalServerError, protocol.ErrInternal, i18n.T(locale, i18n.MsgFailedToUpdate, "agent", err.Error())) return } diff --git a/internal/tools/policy.go b/internal/tools/policy.go index e799feba..df3cff0a 100644 --- a/internal/tools/policy.go +++ b/internal/tools/policy.go @@ -28,12 +28,16 @@ var toolGroups = map[string][]string{ "goclaw": { "read_file", "write_file", "list_files", "edit", "exec", "web_search", "web_fetch", "browser", - "memory_search", "memory_get", + "memory_search", "memory_get", "memory_expand", + "knowledge_graph_search", "vault_search", "sessions_list", "sessions_history", "sessions_send", "spawn", "session_status", - "cron", "message", "create_forum_topic", "list_group_members", + "delegate", + "cron", "datetime", "heartbeat", + "message", "create_forum_topic", "list_group_members", "read_image", "read_document", "read_audio", "read_video", - "create_image", "create_video", - "skill_search", "mcp_tool_search", "tts", + "create_image", "create_video", "create_audio", + "skill_search", "skill_manage", "publish_skill", "use_skill", + "mcp_tool_search", "tts", "team_tasks", }, }