package store import ( "context" "encoding/json" "github.com/google/uuid" ) // Link direction constants. const ( LinkDirectionOutbound = "outbound" LinkDirectionInbound = "inbound" LinkDirectionBidirectional = "bidirectional" ) // Link status constants. const ( LinkStatusActive = "active" LinkStatusDisabled = "disabled" ) // AgentLinkData represents a directional link between two agents for delegation. type AgentLinkData struct { BaseModel SourceAgentID uuid.UUID `json:"source_agent_id" db:"source_agent_id"` TargetAgentID uuid.UUID `json:"target_agent_id" db:"target_agent_id"` Direction string `json:"direction" db:"direction"` // "outbound", "inbound", "bidirectional" TeamID *uuid.UUID `json:"team_id,omitempty" db:"team_id"` // non-nil = auto-created by team Description string `json:"description,omitempty" db:"description"` MaxConcurrent int `json:"max_concurrent" db:"max_concurrent"` Settings json.RawMessage `json:"settings,omitempty" db:"settings"` Status string `json:"status" db:"status"` // "active", "disabled" CreatedBy string `json:"created_by" db:"created_by"` // Joined fields (populated by queries that JOIN agents table) SourceAgentKey string `json:"source_agent_key,omitempty" db:"source_agent_key"` SourceDisplayName string `json:"source_display_name,omitempty" db:"source_display_name"` SourceEmoji string `json:"source_emoji,omitempty" db:"source_emoji"` TargetAgentKey string `json:"target_agent_key,omitempty" db:"target_agent_key"` TargetDisplayName string `json:"target_display_name,omitempty" db:"target_display_name"` TargetEmoji string `json:"target_emoji,omitempty" db:"target_emoji"` TargetDescription string `json:"target_description,omitempty" db:"target_description"` TeamName string `json:"team_name,omitempty" db:"team_name"` // from LEFT JOIN agent_teams (link's own team) TargetIsTeamLead bool `json:"target_is_team_lead,omitempty" db:"target_is_team_lead"` // true if target is lead of any active team TargetTeamName string `json:"target_team_name,omitempty" db:"target_team_name"` // name of team the target leads } // AgentLinkStore manages inter-agent delegation links. type AgentLinkStore interface { CreateLink(ctx context.Context, link *AgentLinkData) error DeleteLink(ctx context.Context, id uuid.UUID) error UpdateLink(ctx context.Context, id uuid.UUID, updates map[string]any) error GetLink(ctx context.Context, id uuid.UUID) (*AgentLinkData, error) // ListLinksFrom returns all links where agentID is the source. ListLinksFrom(ctx context.Context, agentID uuid.UUID) ([]AgentLinkData, error) // ListLinksTo returns all links where agentID is the target. ListLinksTo(ctx context.Context, agentID uuid.UUID) ([]AgentLinkData, error) // CanDelegate checks if fromAgent can delegate to toAgent considering direction. CanDelegate(ctx context.Context, fromAgentID, toAgentID uuid.UUID) (bool, error) // GetLinkBetween returns the active directional link allowing fromAgent to // delegate to toAgent. MaxConcurrent is retained for API compatibility but // is reserved and has no runtime admission effect. // Returns full link data including Settings for per-user permission checks. // Returns nil, nil if no matching link exists. GetLinkBetween(ctx context.Context, fromAgentID, toAgentID uuid.UUID) (*AgentLinkData, error) // DelegateTargets returns all agents that fromAgent can delegate to, // with joined agent_key and display_name for AGENTS.md generation. DelegateTargets(ctx context.Context, fromAgentID uuid.UUID) ([]AgentLinkData, error) // SearchDelegateTargets performs FTS search over delegation targets. SearchDelegateTargets(ctx context.Context, fromAgentID uuid.UUID, query string, limit int) ([]AgentLinkData, error) // SearchDelegateTargetsByEmbedding performs vector similarity search over delegation targets. SearchDelegateTargetsByEmbedding(ctx context.Context, fromAgentID uuid.UUID, embedding []float32, limit int) ([]AgentLinkData, error) // DeleteTeamLinksForAgent removes all team-specific links involving an agent. DeleteTeamLinksForAgent(ctx context.Context, teamID, agentID uuid.UUID) error }