package profile import ( "fmt" "os" "strings" "gopkg.in/yaml.v3" ) type Profile struct { Version int `yaml:"version"` Agent struct { ID string `yaml:"id"` Identity struct { Name string `yaml:"name"` Aliases map[string]string `yaml:"aliases"` Role string `yaml:"role"` SelfAwareness string `yaml:"self_awareness"` } `yaml:"identity"` PublicProfile struct { Bio string `yaml:"bio"` Status string `yaml:"status"` } `yaml:"public_profile"` } `yaml:"agent"` Personality struct { Traits []string `yaml:"traits"` Style struct { Tone string `yaml:"tone"` Verbosity string `yaml:"verbosity"` Formality string `yaml:"formality"` } `yaml:"style"` Avoid []string `yaml:"avoid"` } `yaml:"personality"` Behavior struct { Language struct { MatchConversation bool `yaml:"match_conversation"` RussianDisplayName string `yaml:"russian_display_name"` EnglishDisplayName string `yaml:"english_display_name"` } `yaml:"language"` } `yaml:"behavior"` LLM struct { Response struct { Model string `yaml:"model"` } `yaml:"response"` } `yaml:"llm"` } func Load(path string) (Profile, error) { data, err := os.ReadFile(path) if err != nil { return Profile{}, err } var p Profile if err := yaml.Unmarshal(data, &p); err != nil { return Profile{}, err } if strings.TrimSpace(p.Agent.ID) == "" { return Profile{}, fmt.Errorf("profile %s: agent.id is required", path) } return p, nil } // SystemPrompt builds a compact persona prompt for Tasks board mention replies. func (p Profile) SystemPrompt() string { name := p.Agent.Identity.Name if name == "" { name = p.Agent.ID } ru := p.Behavior.Language.RussianDisplayName if ru == "" { ru = p.Agent.Identity.Aliases["ru"] } var b strings.Builder b.WriteString(fmt.Sprintf("You are %s", name)) if ru != "" && !strings.EqualFold(ru, name) { b.WriteString(fmt.Sprintf(" / %s", ru)) } b.WriteString(fmt.Sprintf(" (agent_id=%s), an AI agent on the l-ai Tasks board.\n", p.Agent.ID)) if bio := strings.TrimSpace(p.Agent.PublicProfile.Bio); bio != "" { b.WriteString(bio) b.WriteByte('\n') } if role := strings.TrimSpace(p.Agent.Identity.Role); role != "" { b.WriteString("Role: ") b.WriteString(role) b.WriteByte('\n') } if len(p.Personality.Traits) > 0 { b.WriteString("Traits: ") b.WriteString(strings.Join(p.Personality.Traits, ", ")) b.WriteByte('\n') } if s := p.Personality.Style; s.Tone != "" || s.Verbosity != "" || s.Formality != "" { b.WriteString(fmt.Sprintf("Style: tone=%s verbosity=%s formality=%s\n", s.Tone, s.Verbosity, s.Formality)) } if len(p.Personality.Avoid) > 0 { b.WriteString("Avoid: ") b.WriteString(strings.Join(p.Personality.Avoid, "; ")) b.WriteByte('\n') } b.WriteString("You reply only to task comment @mentions. Be helpful and concise. ") b.WriteString("Match the language of the triggering comment (Russian or English). ") b.WriteString("Do not invent task state that is not in the provided context. ") b.WriteString("Output only the comment body to post — no preamble or quotes.\n") return b.String() }