diff --git a/apps/web/features/workspace/store.ts b/apps/web/features/workspace/store.ts index 591cc7b98c..ea18ed07d1 100644 --- a/apps/web/features/workspace/store.ts +++ b/apps/web/features/workspace/store.ts @@ -82,7 +82,7 @@ export const useWorkspaceStore = create((set, get) => ({ toast.error("Failed to load members"); return [] as MemberWithUser[]; }), - api.listAgents({ workspace_id: nextWorkspace.id }).catch((e) => { + api.listAgents({ workspace_id: nextWorkspace.id, include_archived: true }).catch((e) => { logger.error("failed to load agents", e); toast.error("Failed to load agents"); return [] as Agent[]; @@ -154,7 +154,7 @@ export const useWorkspaceStore = create((set, get) => ({ const { workspace } = get(); if (!workspace) return; try { - const agents = await api.listAgents({ workspace_id: workspace.id }); + const agents = await api.listAgents({ workspace_id: workspace.id, include_archived: true }); set({ agents }); } catch (e) { logger.error("failed to refresh agents", e); diff --git a/apps/web/shared/api/client.ts b/apps/web/shared/api/client.ts index cf9e863e4d..4721ccf30c 100644 --- a/apps/web/shared/api/client.ts +++ b/apps/web/shared/api/client.ts @@ -291,10 +291,11 @@ export class ApiClient { } // Agents - async listAgents(params?: { workspace_id?: string }): Promise { + async listAgents(params?: { workspace_id?: string; include_archived?: boolean }): Promise { const search = new URLSearchParams(); const wsId = params?.workspace_id ?? this.workspaceId; if (wsId) search.set("workspace_id", wsId); + if (params?.include_archived) search.set("include_archived", "true"); return this.fetch(`/api/agents?${search}`); } diff --git a/server/internal/handler/agent.go b/server/internal/handler/agent.go index 93069fb458..bcfb3a6de3 100644 --- a/server/internal/handler/agent.go +++ b/server/internal/handler/agent.go @@ -434,6 +434,10 @@ func (h *Handler) ArchiveAgent(w http.ResponseWriter, r *http.Request) { if !h.canManageAgent(w, r, agent) { return } + if agent.ArchivedAt.Valid { + writeError(w, http.StatusConflict, "agent is already archived") + return + } userID := requestUserID(r) archived, err := h.Queries.ArchiveAgent(r.Context(), db.ArchiveAgentParams{ @@ -447,7 +451,9 @@ func (h *Handler) ArchiveAgent(w http.ResponseWriter, r *http.Request) { } // Cancel all pending/active tasks for this agent. - h.Queries.CancelAgentTasksByAgent(r.Context(), parseUUID(id)) + if err := h.Queries.CancelAgentTasksByAgent(r.Context(), parseUUID(id)); err != nil { + slog.Warn("cancel agent tasks on archive failed", append(logger.RequestAttrs(r), "error", err, "agent_id", id)...) + } wsID := uuidToString(archived.WorkspaceID) slog.Info("agent archived", append(logger.RequestAttrs(r), "agent_id", id, "workspace_id", wsID)...) @@ -466,6 +472,10 @@ func (h *Handler) RestoreAgent(w http.ResponseWriter, r *http.Request) { if !h.canManageAgent(w, r, agent) { return } + if !agent.ArchivedAt.Valid { + writeError(w, http.StatusConflict, "agent is not archived") + return + } restored, err := h.Queries.RestoreAgent(r.Context(), parseUUID(id)) if err != nil { diff --git a/server/internal/service/task.go b/server/internal/service/task.go index 633609ba0b..92d602cfe2 100644 --- a/server/internal/service/task.go +++ b/server/internal/service/task.go @@ -314,12 +314,7 @@ func (s *TaskService) ReportProgress(ctx context.Context, taskID string, workspa } // ReconcileAgentStatus checks running task count and sets agent status accordingly. -// Skips archived agents — their status is irrelevant. func (s *TaskService) ReconcileAgentStatus(ctx context.Context, agentID pgtype.UUID) { - agent, err := s.Queries.GetAgent(ctx, agentID) - if err != nil || agent.ArchivedAt.Valid { - return - } running, err := s.Queries.CountRunningTasks(ctx, agentID) if err != nil { return