diff --git a/internal/http/agents_instances.go b/internal/http/agents_instances.go index 77a96899..659893dd 100644 --- a/internal/http/agents_instances.go +++ b/internal/http/agents_instances.go @@ -120,6 +120,12 @@ func (h *AgentsHandler) handleSetInstanceFile(w http.ResponseWriter, r *http.Req return } + // Ensure user profile exists (creates row if needed, e.g. admin adds contact manually). + if err := h.agents.EnsureUserProfile(r.Context(), id, instanceUserID); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + if err := h.agents.SetUserContextFile(r.Context(), id, instanceUserID, fileName, payload.Content); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return diff --git a/internal/store/agent_store.go b/internal/store/agent_store.go index 2cca6714..8a53c0c2 100644 --- a/internal/store/agent_store.go +++ b/internal/store/agent_store.go @@ -213,6 +213,7 @@ type AgentStore interface { // User-agent profiles + instances GetOrCreateUserProfile(ctx context.Context, agentID uuid.UUID, userID, workspace, channel string) (isNew bool, effectiveWorkspace string, err error) + EnsureUserProfile(ctx context.Context, agentID uuid.UUID, userID string) error ListUserInstances(ctx context.Context, agentID uuid.UUID) ([]UserInstanceData, error) UpdateUserProfileMetadata(ctx context.Context, agentID uuid.UUID, userID string, metadata map[string]string) error diff --git a/internal/store/pg/agents_context.go b/internal/store/pg/agents_context.go index 9baaa3c0..abafc779 100644 --- a/internal/store/pg/agents_context.go +++ b/internal/store/pg/agents_context.go @@ -111,6 +111,17 @@ func (s *PGAgentStore) GetOrCreateUserProfile(ctx context.Context, agentID uuid. return isInserted, ws, nil } +// EnsureUserProfile creates a minimal user_agent_profiles row if not exists. +// Used when admin manually adds a contact as an agent instance via the UI. +func (s *PGAgentStore) EnsureUserProfile(ctx context.Context, agentID uuid.UUID, userID string) error { + _, err := s.db.ExecContext(ctx, ` + INSERT INTO user_agent_profiles (agent_id, user_id, first_seen_at, last_seen_at) + VALUES ($1, $2, NOW(), NOW()) + ON CONFLICT (agent_id, user_id) DO NOTHING + `, agentID, userID) + return err +} + // --- User Instances --- func (s *PGAgentStore) ListUserInstances(ctx context.Context, agentID uuid.UUID) ([]store.UserInstanceData, error) { diff --git a/ui/web/src/pages/agents/agent-detail/agent-instances-tab.tsx b/ui/web/src/pages/agents/agent-detail/agent-instances-tab.tsx index e6637e8b..0253cd52 100644 --- a/ui/web/src/pages/agents/agent-detail/agent-instances-tab.tsx +++ b/ui/web/src/pages/agents/agent-detail/agent-instances-tab.tsx @@ -1,11 +1,12 @@ -import { useState, useEffect, useMemo } from "react"; -import { Save, Check, AlertCircle, Users, FileText } from "lucide-react"; +import { useState, useEffect, useMemo, useRef } from "react"; +import { Save, Check, AlertCircle, Users, FileText, Search, UserPlus } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Textarea } from "@/components/ui/textarea"; import { useContactResolver } from "@/hooks/use-contact-resolver"; import { useAgentInstances, type UserInstance } from "../hooks/use-agent-instances"; +import { useContactSearch } from "../hooks/use-contact-search"; interface AgentInstancesTabProps { agentId: string; @@ -60,27 +61,36 @@ export function AgentInstancesTab({ agentId }: AgentInstancesTabProps) { const instanceUserIDs = useMemo(() => instances.map((i) => i.user_id), [instances]); const { resolve } = useContactResolver(instanceUserIDs); + // Existing instance user_ids for deduplication + const existingIDs = useMemo(() => new Set(instances.map((i) => i.user_id)), [instances]); + + const handleContactSelect = (senderID: string) => { + setSelected(senderID); + }; + if (loading) { return
{t("instances.noInstances")}
-{t("instances.noInstancesDesc")}
-{t("instances.noInstances")}
+