Some checks failed
CI / build-and-test (push) Failing after -31m18s
- Created tables for agent_personas, agent_parts, agent_traits, and character_arcs. - Established relationships between personas, parts, skills, guardrails, and traits. - Added arc stages and their corresponding parts, along with a persona_arc table to track current stages. - Implemented cascading delete rules for referential integrity.
874 lines
31 KiB
Go
874 lines
31 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
|
|
"git.warky.dev/wdevs/amcs/internal/store"
|
|
ext "git.warky.dev/wdevs/amcs/internal/types"
|
|
)
|
|
|
|
type AgentPersonasTool struct {
|
|
store *store.DB
|
|
}
|
|
|
|
func NewAgentPersonasTool(db *store.DB) *AgentPersonasTool {
|
|
return &AgentPersonasTool{store: db}
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Personas
|
|
// ──────────────────────────────────────────────
|
|
|
|
// create_agent_persona
|
|
|
|
type CreatePersonaInput struct {
|
|
Name string `json:"name" jsonschema:"unique persona name — used as the load key"`
|
|
Description string `json:"description,omitempty" jsonschema:"short description of the persona"`
|
|
Summary string `json:"summary" jsonschema:"concise behaviour summary, returned by default on load"`
|
|
Detail string `json:"detail,omitempty" jsonschema:"full behaviour detail, returned only when detail=true"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"optional tags for grouping or filtering"`
|
|
}
|
|
|
|
type CreatePersonaOutput struct {
|
|
Persona ext.Persona `json:"persona"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) CreatePersona(ctx context.Context, _ *mcp.CallToolRequest, in CreatePersonaInput) (*mcp.CallToolResult, CreatePersonaOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, CreatePersonaOutput{}, errRequiredField("name")
|
|
}
|
|
if strings.TrimSpace(in.Summary) == "" {
|
|
return nil, CreatePersonaOutput{}, errRequiredField("summary")
|
|
}
|
|
if in.Tags == nil {
|
|
in.Tags = []string{}
|
|
}
|
|
persona, err := t.store.CreatePersona(ctx, ext.Persona{
|
|
Name: strings.TrimSpace(in.Name),
|
|
Description: strings.TrimSpace(in.Description),
|
|
Summary: strings.TrimSpace(in.Summary),
|
|
Detail: strings.TrimSpace(in.Detail),
|
|
Tags: in.Tags,
|
|
})
|
|
if err != nil {
|
|
return nil, CreatePersonaOutput{}, err
|
|
}
|
|
return nil, CreatePersonaOutput{Persona: persona}, nil
|
|
}
|
|
|
|
// update_agent_persona
|
|
|
|
type UpdatePersonaInput struct {
|
|
Name string `json:"name" jsonschema:"name of the persona to update"`
|
|
NewName string `json:"new_name,omitempty" jsonschema:"rename the persona"`
|
|
Description *string `json:"description,omitempty" jsonschema:"update description"`
|
|
Summary *string `json:"summary,omitempty" jsonschema:"update summary"`
|
|
Detail *string `json:"detail,omitempty" jsonschema:"update detail"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"replace tags"`
|
|
}
|
|
|
|
type UpdatePersonaOutput struct {
|
|
Persona ext.Persona `json:"persona"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) UpdatePersona(ctx context.Context, _ *mcp.CallToolRequest, in UpdatePersonaInput) (*mcp.CallToolResult, UpdatePersonaOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, UpdatePersonaOutput{}, errRequiredField("name")
|
|
}
|
|
updates := map[string]any{}
|
|
if n := strings.TrimSpace(in.NewName); n != "" {
|
|
updates["name"] = n
|
|
}
|
|
if in.Description != nil {
|
|
updates["description"] = strings.TrimSpace(*in.Description)
|
|
}
|
|
if in.Summary != nil {
|
|
updates["summary"] = strings.TrimSpace(*in.Summary)
|
|
}
|
|
if in.Detail != nil {
|
|
updates["detail"] = strings.TrimSpace(*in.Detail)
|
|
}
|
|
if in.Tags != nil {
|
|
updates["tags"] = in.Tags
|
|
}
|
|
persona, err := t.store.UpdatePersona(ctx, strings.TrimSpace(in.Name), updates)
|
|
if err != nil {
|
|
return nil, UpdatePersonaOutput{}, err
|
|
}
|
|
return nil, UpdatePersonaOutput{Persona: persona}, nil
|
|
}
|
|
|
|
// delete_agent_persona
|
|
|
|
type DeletePersonaInput struct {
|
|
Name string `json:"name" jsonschema:"name of the persona to delete"`
|
|
}
|
|
|
|
type DeletePersonaOutput struct {
|
|
Deleted bool `json:"deleted"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) DeletePersona(ctx context.Context, _ *mcp.CallToolRequest, in DeletePersonaInput) (*mcp.CallToolResult, DeletePersonaOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, DeletePersonaOutput{}, errRequiredField("name")
|
|
}
|
|
if err := t.store.DeletePersona(ctx, strings.TrimSpace(in.Name)); err != nil {
|
|
return nil, DeletePersonaOutput{}, err
|
|
}
|
|
return nil, DeletePersonaOutput{Deleted: true}, nil
|
|
}
|
|
|
|
// list_agent_personas
|
|
|
|
type ListPersonasInput struct {
|
|
Tag string `json:"tag,omitempty" jsonschema:"filter by tag"`
|
|
}
|
|
|
|
type ListPersonasOutput struct {
|
|
Personas []ext.Persona `json:"personas"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) ListPersonas(ctx context.Context, _ *mcp.CallToolRequest, in ListPersonasInput) (*mcp.CallToolResult, ListPersonasOutput, error) {
|
|
personas, err := t.store.ListPersonas(ctx, in.Tag)
|
|
if err != nil {
|
|
return nil, ListPersonasOutput{}, err
|
|
}
|
|
if personas == nil {
|
|
personas = []ext.Persona{}
|
|
}
|
|
return nil, ListPersonasOutput{Personas: personas}, nil
|
|
}
|
|
|
|
// get_agent_persona
|
|
|
|
type GetPersonaInput struct {
|
|
Name string `json:"name" jsonschema:"persona name to load"`
|
|
Detail bool `json:"detail,omitempty" jsonschema:"when true, return full part content and persona detail instead of summaries"`
|
|
Overrides map[string]string `json:"overrides,omitempty" jsonschema:"runtime part substitutions: {part_type: part_name}. Replaces all parts of that type without modifying the persona."`
|
|
}
|
|
|
|
type GetPersonaOutput struct {
|
|
Persona ext.PersonaFull `json:"persona"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) GetPersona(ctx context.Context, _ *mcp.CallToolRequest, in GetPersonaInput) (*mcp.CallToolResult, GetPersonaOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, GetPersonaOutput{}, errRequiredField("name")
|
|
}
|
|
persona, err := t.store.GetPersona(ctx, strings.TrimSpace(in.Name), in.Detail, in.Overrides)
|
|
if err != nil {
|
|
return nil, GetPersonaOutput{}, err
|
|
}
|
|
return nil, GetPersonaOutput{Persona: persona}, nil
|
|
}
|
|
|
|
// get_persona_manifest
|
|
|
|
type GetPersonaManifestInput struct {
|
|
Name string `json:"name" jsonschema:"persona name"`
|
|
}
|
|
|
|
type GetPersonaManifestOutput struct {
|
|
Manifest ext.PersonaManifest `json:"manifest"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) GetPersonaManifest(ctx context.Context, _ *mcp.CallToolRequest, in GetPersonaManifestInput) (*mcp.CallToolResult, GetPersonaManifestOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, GetPersonaManifestOutput{}, errRequiredField("name")
|
|
}
|
|
manifest, err := t.store.GetPersonaManifest(ctx, strings.TrimSpace(in.Name))
|
|
if err != nil {
|
|
return nil, GetPersonaManifestOutput{}, err
|
|
}
|
|
return nil, GetPersonaManifestOutput{Manifest: manifest}, nil
|
|
}
|
|
|
|
// compile_persona
|
|
|
|
type CompilePersonaInput struct {
|
|
Name string `json:"name" jsonschema:"persona name to compile"`
|
|
}
|
|
|
|
type CompilePersonaOutput struct {
|
|
Persona ext.Persona `json:"persona"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) CompilePersona(ctx context.Context, _ *mcp.CallToolRequest, in CompilePersonaInput) (*mcp.CallToolResult, CompilePersonaOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, CompilePersonaOutput{}, errRequiredField("name")
|
|
}
|
|
persona, err := t.store.CompilePersona(ctx, strings.TrimSpace(in.Name))
|
|
if err != nil {
|
|
return nil, CompilePersonaOutput{}, err
|
|
}
|
|
return nil, CompilePersonaOutput{Persona: persona}, nil
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Parts
|
|
// ──────────────────────────────────────────────
|
|
|
|
// create_agent_part
|
|
|
|
type CreatePartInput struct {
|
|
Name string `json:"name" jsonschema:"globally unique part name — used as the override key"`
|
|
PartType string `json:"part_type" jsonschema:"one of: system, agent, soul, identity, skill, specialization, tone, goal, context, protocol, backstory, motivation, voice, archetype, flaw, relationship"`
|
|
Description string `json:"description,omitempty" jsonschema:"short description of what this part does"`
|
|
Summary string `json:"summary" jsonschema:"concise version, used in summary mode"`
|
|
Content string `json:"content,omitempty" jsonschema:"full content, used in detail mode"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"optional tags"`
|
|
}
|
|
|
|
type CreatePartOutput struct {
|
|
Part ext.Part `json:"part"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) CreatePart(ctx context.Context, _ *mcp.CallToolRequest, in CreatePartInput) (*mcp.CallToolResult, CreatePartOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, CreatePartOutput{}, errRequiredField("name")
|
|
}
|
|
if strings.TrimSpace(in.PartType) == "" {
|
|
return nil, CreatePartOutput{}, errRequiredField("part_type")
|
|
}
|
|
if strings.TrimSpace(in.Summary) == "" {
|
|
return nil, CreatePartOutput{}, errRequiredField("summary")
|
|
}
|
|
if in.Tags == nil {
|
|
in.Tags = []string{}
|
|
}
|
|
part, err := t.store.CreatePart(ctx, ext.Part{
|
|
Name: strings.TrimSpace(in.Name),
|
|
PartType: strings.TrimSpace(in.PartType),
|
|
Description: strings.TrimSpace(in.Description),
|
|
Summary: strings.TrimSpace(in.Summary),
|
|
Content: strings.TrimSpace(in.Content),
|
|
Tags: in.Tags,
|
|
})
|
|
if err != nil {
|
|
return nil, CreatePartOutput{}, err
|
|
}
|
|
return nil, CreatePartOutput{Part: part}, nil
|
|
}
|
|
|
|
// update_agent_part
|
|
|
|
type UpdatePartInput struct {
|
|
Name string `json:"name" jsonschema:"name of the part to update"`
|
|
NewName string `json:"new_name,omitempty" jsonschema:"rename the part"`
|
|
PartType *string `json:"part_type,omitempty" jsonschema:"update part type"`
|
|
Description *string `json:"description,omitempty" jsonschema:"update description"`
|
|
Summary *string `json:"summary,omitempty" jsonschema:"update summary"`
|
|
Content *string `json:"content,omitempty" jsonschema:"update content"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"replace tags"`
|
|
}
|
|
|
|
type UpdatePartOutput struct {
|
|
Part ext.Part `json:"part"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) UpdatePart(ctx context.Context, _ *mcp.CallToolRequest, in UpdatePartInput) (*mcp.CallToolResult, UpdatePartOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, UpdatePartOutput{}, errRequiredField("name")
|
|
}
|
|
updates := map[string]any{}
|
|
if n := strings.TrimSpace(in.NewName); n != "" {
|
|
updates["name"] = n
|
|
}
|
|
if in.PartType != nil {
|
|
updates["part_type"] = strings.TrimSpace(*in.PartType)
|
|
}
|
|
if in.Description != nil {
|
|
updates["description"] = strings.TrimSpace(*in.Description)
|
|
}
|
|
if in.Summary != nil {
|
|
updates["summary"] = strings.TrimSpace(*in.Summary)
|
|
}
|
|
if in.Content != nil {
|
|
updates["content"] = strings.TrimSpace(*in.Content)
|
|
}
|
|
if in.Tags != nil {
|
|
updates["tags"] = in.Tags
|
|
}
|
|
part, err := t.store.UpdatePart(ctx, strings.TrimSpace(in.Name), updates)
|
|
if err != nil {
|
|
return nil, UpdatePartOutput{}, err
|
|
}
|
|
return nil, UpdatePartOutput{Part: part}, nil
|
|
}
|
|
|
|
// delete_agent_part
|
|
|
|
type DeletePartInput struct {
|
|
Name string `json:"name" jsonschema:"name of the part to delete"`
|
|
}
|
|
|
|
type DeletePartOutput struct {
|
|
Deleted bool `json:"deleted"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) DeletePart(ctx context.Context, _ *mcp.CallToolRequest, in DeletePartInput) (*mcp.CallToolResult, DeletePartOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, DeletePartOutput{}, errRequiredField("name")
|
|
}
|
|
if err := t.store.DeletePart(ctx, strings.TrimSpace(in.Name)); err != nil {
|
|
return nil, DeletePartOutput{}, err
|
|
}
|
|
return nil, DeletePartOutput{Deleted: true}, nil
|
|
}
|
|
|
|
// list_agent_parts
|
|
|
|
type ListPartsInput struct {
|
|
PartType string `json:"part_type,omitempty" jsonschema:"filter by part type"`
|
|
Tag string `json:"tag,omitempty" jsonschema:"filter by tag"`
|
|
}
|
|
|
|
type ListPartsOutput struct {
|
|
Parts []ext.Part `json:"parts"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) ListParts(ctx context.Context, _ *mcp.CallToolRequest, in ListPartsInput) (*mcp.CallToolResult, ListPartsOutput, error) {
|
|
parts, err := t.store.ListParts(ctx, in.PartType, in.Tag)
|
|
if err != nil {
|
|
return nil, ListPartsOutput{}, err
|
|
}
|
|
if parts == nil {
|
|
parts = []ext.Part{}
|
|
}
|
|
return nil, ListPartsOutput{Parts: parts}, nil
|
|
}
|
|
|
|
// get_agent_part
|
|
|
|
type GetPartInput struct {
|
|
Name string `json:"name" jsonschema:"part name"`
|
|
}
|
|
|
|
type GetPartOutput struct {
|
|
Part ext.Part `json:"part"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) GetPart(ctx context.Context, _ *mcp.CallToolRequest, in GetPartInput) (*mcp.CallToolResult, GetPartOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, GetPartOutput{}, errRequiredField("name")
|
|
}
|
|
part, err := t.store.GetPartByName(ctx, strings.TrimSpace(in.Name))
|
|
if err != nil {
|
|
return nil, GetPartOutput{}, err
|
|
}
|
|
return nil, GetPartOutput{Part: part}, nil
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Persona-Part / Skill / Guardrail links
|
|
// ──────────────────────────────────────────────
|
|
|
|
// add_persona_part
|
|
|
|
type AddPersonaPartInput struct {
|
|
Persona string `json:"persona" jsonschema:"persona name"`
|
|
Part string `json:"part" jsonschema:"part name to link"`
|
|
Order int `json:"order,omitempty" jsonschema:"assembly order (lower first, default 0)"`
|
|
Priority int `json:"priority,omitempty" jsonschema:"context-budget priority (higher loads first when trimming)"`
|
|
}
|
|
|
|
type AddPersonaPartOutput struct {
|
|
Persona string `json:"persona"`
|
|
Part string `json:"part"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddPersonaPart(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaPartInput) (*mcp.CallToolResult, AddPersonaPartOutput, error) {
|
|
if strings.TrimSpace(in.Persona) == "" {
|
|
return nil, AddPersonaPartOutput{}, errRequiredField("persona")
|
|
}
|
|
if strings.TrimSpace(in.Part) == "" {
|
|
return nil, AddPersonaPartOutput{}, errRequiredField("part")
|
|
}
|
|
if err := t.store.AddPersonaPart(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Part), in.Order, in.Priority); err != nil {
|
|
return nil, AddPersonaPartOutput{}, err
|
|
}
|
|
return nil, AddPersonaPartOutput{Persona: in.Persona, Part: in.Part}, nil
|
|
}
|
|
|
|
// remove_persona_part
|
|
|
|
type RemovePersonaPartInput struct {
|
|
Persona string `json:"persona" jsonschema:"persona name"`
|
|
Part string `json:"part" jsonschema:"part name to unlink"`
|
|
}
|
|
|
|
type RemovePersonaPartOutput struct {
|
|
Removed bool `json:"removed"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) RemovePersonaPart(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaPartInput) (*mcp.CallToolResult, RemovePersonaPartOutput, error) {
|
|
if strings.TrimSpace(in.Persona) == "" {
|
|
return nil, RemovePersonaPartOutput{}, errRequiredField("persona")
|
|
}
|
|
if strings.TrimSpace(in.Part) == "" {
|
|
return nil, RemovePersonaPartOutput{}, errRequiredField("part")
|
|
}
|
|
if err := t.store.RemovePersonaPart(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Part)); err != nil {
|
|
return nil, RemovePersonaPartOutput{}, err
|
|
}
|
|
return nil, RemovePersonaPartOutput{Removed: true}, nil
|
|
}
|
|
|
|
// add_persona_skill
|
|
|
|
type AddPersonaSkillInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
SkillID int64 `json:"skill_id" jsonschema:"agent skill id to link"`
|
|
}
|
|
|
|
type AddPersonaSkillOutput struct {
|
|
PersonaID int64 `json:"persona_id"`
|
|
SkillID int64 `json:"skill_id"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddPersonaSkill(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaSkillInput) (*mcp.CallToolResult, AddPersonaSkillOutput, error) {
|
|
if err := t.store.AddPersonaSkill(ctx, in.PersonaID, in.SkillID); err != nil {
|
|
return nil, AddPersonaSkillOutput{}, err
|
|
}
|
|
return nil, AddPersonaSkillOutput{PersonaID: in.PersonaID, SkillID: in.SkillID}, nil
|
|
}
|
|
|
|
// remove_persona_skill
|
|
|
|
type RemovePersonaSkillInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
SkillID int64 `json:"skill_id" jsonschema:"agent skill id to unlink"`
|
|
}
|
|
|
|
type RemovePersonaSkillOutput struct {
|
|
Removed bool `json:"removed"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) RemovePersonaSkill(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaSkillInput) (*mcp.CallToolResult, RemovePersonaSkillOutput, error) {
|
|
if err := t.store.RemovePersonaSkill(ctx, in.PersonaID, in.SkillID); err != nil {
|
|
return nil, RemovePersonaSkillOutput{}, err
|
|
}
|
|
return nil, RemovePersonaSkillOutput{Removed: true}, nil
|
|
}
|
|
|
|
// add_persona_guardrail
|
|
|
|
type AddPersonaGuardrailInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
GuardrailID int64 `json:"guardrail_id" jsonschema:"agent guardrail id to link"`
|
|
}
|
|
|
|
type AddPersonaGuardrailOutput struct {
|
|
PersonaID int64 `json:"persona_id"`
|
|
GuardrailID int64 `json:"guardrail_id"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddPersonaGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaGuardrailInput) (*mcp.CallToolResult, AddPersonaGuardrailOutput, error) {
|
|
if err := t.store.AddPersonaGuardrail(ctx, in.PersonaID, in.GuardrailID); err != nil {
|
|
return nil, AddPersonaGuardrailOutput{}, err
|
|
}
|
|
return nil, AddPersonaGuardrailOutput{PersonaID: in.PersonaID, GuardrailID: in.GuardrailID}, nil
|
|
}
|
|
|
|
// remove_persona_guardrail
|
|
|
|
type RemovePersonaGuardrailInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
GuardrailID int64 `json:"guardrail_id" jsonschema:"agent guardrail id to unlink"`
|
|
}
|
|
|
|
type RemovePersonaGuardrailOutput struct {
|
|
Removed bool `json:"removed"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) RemovePersonaGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaGuardrailInput) (*mcp.CallToolResult, RemovePersonaGuardrailOutput, error) {
|
|
if err := t.store.RemovePersonaGuardrail(ctx, in.PersonaID, in.GuardrailID); err != nil {
|
|
return nil, RemovePersonaGuardrailOutput{}, err
|
|
}
|
|
return nil, RemovePersonaGuardrailOutput{Removed: true}, nil
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Traits
|
|
// ──────────────────────────────────────────────
|
|
|
|
// create_agent_trait
|
|
|
|
type CreateTraitInput struct {
|
|
Name string `json:"name" jsonschema:"globally unique trait name"`
|
|
TraitType string `json:"trait_type" jsonschema:"one of: personality, cognitive, emotional, social, behavioral"`
|
|
Description string `json:"description,omitempty" jsonschema:"short description of this trait"`
|
|
Instruction string `json:"instruction,omitempty" jsonschema:"how to apply this trait in practice"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"optional tags"`
|
|
}
|
|
|
|
type CreateTraitOutput struct {
|
|
Trait ext.Trait `json:"trait"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) CreateTrait(ctx context.Context, _ *mcp.CallToolRequest, in CreateTraitInput) (*mcp.CallToolResult, CreateTraitOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, CreateTraitOutput{}, errRequiredField("name")
|
|
}
|
|
if strings.TrimSpace(in.TraitType) == "" {
|
|
return nil, CreateTraitOutput{}, errRequiredField("trait_type")
|
|
}
|
|
if in.Tags == nil {
|
|
in.Tags = []string{}
|
|
}
|
|
trait, err := t.store.CreateTrait(ctx, ext.Trait{
|
|
Name: strings.TrimSpace(in.Name),
|
|
TraitType: strings.TrimSpace(in.TraitType),
|
|
Description: strings.TrimSpace(in.Description),
|
|
Instruction: strings.TrimSpace(in.Instruction),
|
|
Tags: in.Tags,
|
|
})
|
|
if err != nil {
|
|
return nil, CreateTraitOutput{}, err
|
|
}
|
|
return nil, CreateTraitOutput{Trait: trait}, nil
|
|
}
|
|
|
|
// update_agent_trait
|
|
|
|
type UpdateTraitInput struct {
|
|
Name string `json:"name" jsonschema:"name of the trait to update"`
|
|
NewName string `json:"new_name,omitempty" jsonschema:"rename the trait"`
|
|
TraitType *string `json:"trait_type,omitempty" jsonschema:"update trait type"`
|
|
Description *string `json:"description,omitempty" jsonschema:"update description"`
|
|
Instruction *string `json:"instruction,omitempty" jsonschema:"update instruction"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"replace tags"`
|
|
}
|
|
|
|
type UpdateTraitOutput struct {
|
|
Trait ext.Trait `json:"trait"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) UpdateTrait(ctx context.Context, _ *mcp.CallToolRequest, in UpdateTraitInput) (*mcp.CallToolResult, UpdateTraitOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, UpdateTraitOutput{}, errRequiredField("name")
|
|
}
|
|
updates := map[string]any{}
|
|
if n := strings.TrimSpace(in.NewName); n != "" {
|
|
updates["name"] = n
|
|
}
|
|
if in.TraitType != nil {
|
|
updates["trait_type"] = strings.TrimSpace(*in.TraitType)
|
|
}
|
|
if in.Description != nil {
|
|
updates["description"] = strings.TrimSpace(*in.Description)
|
|
}
|
|
if in.Instruction != nil {
|
|
updates["instruction"] = strings.TrimSpace(*in.Instruction)
|
|
}
|
|
if in.Tags != nil {
|
|
updates["tags"] = in.Tags
|
|
}
|
|
trait, err := t.store.UpdateTrait(ctx, strings.TrimSpace(in.Name), updates)
|
|
if err != nil {
|
|
return nil, UpdateTraitOutput{}, err
|
|
}
|
|
return nil, UpdateTraitOutput{Trait: trait}, nil
|
|
}
|
|
|
|
// delete_agent_trait
|
|
|
|
type DeleteTraitInput struct {
|
|
Name string `json:"name" jsonschema:"name of the trait to delete"`
|
|
}
|
|
|
|
type DeleteTraitOutput struct {
|
|
Deleted bool `json:"deleted"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) DeleteTrait(ctx context.Context, _ *mcp.CallToolRequest, in DeleteTraitInput) (*mcp.CallToolResult, DeleteTraitOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, DeleteTraitOutput{}, errRequiredField("name")
|
|
}
|
|
if err := t.store.DeleteTrait(ctx, strings.TrimSpace(in.Name)); err != nil {
|
|
return nil, DeleteTraitOutput{}, err
|
|
}
|
|
return nil, DeleteTraitOutput{Deleted: true}, nil
|
|
}
|
|
|
|
// list_agent_traits
|
|
|
|
type ListTraitsInput struct {
|
|
TraitType string `json:"trait_type,omitempty" jsonschema:"filter by trait type"`
|
|
Tag string `json:"tag,omitempty" jsonschema:"filter by tag"`
|
|
}
|
|
|
|
type ListTraitsOutput struct {
|
|
Traits []ext.Trait `json:"traits"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) ListTraits(ctx context.Context, _ *mcp.CallToolRequest, in ListTraitsInput) (*mcp.CallToolResult, ListTraitsOutput, error) {
|
|
traits, err := t.store.ListTraits(ctx, in.TraitType, in.Tag)
|
|
if err != nil {
|
|
return nil, ListTraitsOutput{}, err
|
|
}
|
|
if traits == nil {
|
|
traits = []ext.Trait{}
|
|
}
|
|
return nil, ListTraitsOutput{Traits: traits}, nil
|
|
}
|
|
|
|
// get_agent_trait
|
|
|
|
type GetTraitInput struct {
|
|
Name string `json:"name" jsonschema:"trait name"`
|
|
}
|
|
|
|
type GetTraitOutput struct {
|
|
Trait ext.Trait `json:"trait"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) GetTrait(ctx context.Context, _ *mcp.CallToolRequest, in GetTraitInput) (*mcp.CallToolResult, GetTraitOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, GetTraitOutput{}, errRequiredField("name")
|
|
}
|
|
trait, err := t.store.GetTraitByName(ctx, strings.TrimSpace(in.Name))
|
|
if err != nil {
|
|
return nil, GetTraitOutput{}, err
|
|
}
|
|
return nil, GetTraitOutput{Trait: trait}, nil
|
|
}
|
|
|
|
// add_persona_trait
|
|
|
|
type AddPersonaTraitInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
TraitID int64 `json:"trait_id" jsonschema:"agent trait id to link"`
|
|
}
|
|
|
|
type AddPersonaTraitOutput struct {
|
|
PersonaID int64 `json:"persona_id"`
|
|
TraitID int64 `json:"trait_id"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddPersonaTrait(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaTraitInput) (*mcp.CallToolResult, AddPersonaTraitOutput, error) {
|
|
if err := t.store.AddPersonaTrait(ctx, in.PersonaID, in.TraitID); err != nil {
|
|
return nil, AddPersonaTraitOutput{}, err
|
|
}
|
|
return nil, AddPersonaTraitOutput{PersonaID: in.PersonaID, TraitID: in.TraitID}, nil
|
|
}
|
|
|
|
// remove_persona_trait
|
|
|
|
type RemovePersonaTraitInput struct {
|
|
PersonaID int64 `json:"persona_id" jsonschema:"persona id"`
|
|
TraitID int64 `json:"trait_id" jsonschema:"agent trait id to unlink"`
|
|
}
|
|
|
|
type RemovePersonaTraitOutput struct {
|
|
Removed bool `json:"removed"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) RemovePersonaTrait(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaTraitInput) (*mcp.CallToolResult, RemovePersonaTraitOutput, error) {
|
|
if err := t.store.RemovePersonaTrait(ctx, in.PersonaID, in.TraitID); err != nil {
|
|
return nil, RemovePersonaTraitOutput{}, err
|
|
}
|
|
return nil, RemovePersonaTraitOutput{Removed: true}, nil
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Character Arcs
|
|
// ──────────────────────────────────────────────
|
|
|
|
// create_character_arc
|
|
|
|
type CreateCharacterArcInput struct {
|
|
Name string `json:"name" jsonschema:"unique arc name"`
|
|
Description string `json:"description,omitempty" jsonschema:"description of the arc"`
|
|
Summary string `json:"summary,omitempty" jsonschema:"brief arc summary"`
|
|
}
|
|
|
|
type CreateCharacterArcOutput struct {
|
|
Arc ext.CharacterArc `json:"arc"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) CreateCharacterArc(ctx context.Context, _ *mcp.CallToolRequest, in CreateCharacterArcInput) (*mcp.CallToolResult, CreateCharacterArcOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, CreateCharacterArcOutput{}, errRequiredField("name")
|
|
}
|
|
arc, err := t.store.CreateCharacterArc(ctx, ext.CharacterArc{
|
|
Name: strings.TrimSpace(in.Name),
|
|
Description: strings.TrimSpace(in.Description),
|
|
Summary: strings.TrimSpace(in.Summary),
|
|
})
|
|
if err != nil {
|
|
return nil, CreateCharacterArcOutput{}, err
|
|
}
|
|
return nil, CreateCharacterArcOutput{Arc: arc}, nil
|
|
}
|
|
|
|
// list_character_arcs
|
|
|
|
type ListCharacterArcsInput struct{}
|
|
|
|
type ListCharacterArcsOutput struct {
|
|
Arcs []ext.CharacterArc `json:"arcs"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) ListCharacterArcs(ctx context.Context, _ *mcp.CallToolRequest, _ ListCharacterArcsInput) (*mcp.CallToolResult, ListCharacterArcsOutput, error) {
|
|
arcs, err := t.store.ListCharacterArcs(ctx)
|
|
if err != nil {
|
|
return nil, ListCharacterArcsOutput{}, err
|
|
}
|
|
if arcs == nil {
|
|
arcs = []ext.CharacterArc{}
|
|
}
|
|
return nil, ListCharacterArcsOutput{Arcs: arcs}, nil
|
|
}
|
|
|
|
// add_arc_stage
|
|
|
|
type AddArcStageInput struct {
|
|
Arc string `json:"arc" jsonschema:"arc name"`
|
|
Name string `json:"name" jsonschema:"stage name"`
|
|
StageOrder int `json:"stage_order,omitempty" jsonschema:"position in arc sequence (lower first)"`
|
|
Description string `json:"description,omitempty" jsonschema:"what happens at this stage"`
|
|
Condition string `json:"condition,omitempty" jsonschema:"trigger condition description (evaluated externally)"`
|
|
}
|
|
|
|
type AddArcStageOutput struct {
|
|
Stage ext.ArcStage `json:"stage"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddArcStage(ctx context.Context, _ *mcp.CallToolRequest, in AddArcStageInput) (*mcp.CallToolResult, AddArcStageOutput, error) {
|
|
if strings.TrimSpace(in.Arc) == "" {
|
|
return nil, AddArcStageOutput{}, errRequiredField("arc")
|
|
}
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, AddArcStageOutput{}, errRequiredField("name")
|
|
}
|
|
stage, err := t.store.AddArcStage(ctx, strings.TrimSpace(in.Arc), ext.ArcStage{
|
|
Name: strings.TrimSpace(in.Name),
|
|
StageOrder: in.StageOrder,
|
|
Description: strings.TrimSpace(in.Description),
|
|
Condition: strings.TrimSpace(in.Condition),
|
|
})
|
|
if err != nil {
|
|
return nil, AddArcStageOutput{}, err
|
|
}
|
|
return nil, AddArcStageOutput{Stage: stage}, nil
|
|
}
|
|
|
|
// add_stage_part
|
|
|
|
type AddStagePartInput struct {
|
|
StageID int64 `json:"stage_id" jsonschema:"arc stage id"`
|
|
PartName string `json:"part_name" jsonschema:"part name to link"`
|
|
}
|
|
|
|
type AddStagePartOutput struct {
|
|
StageID int64 `json:"stage_id"`
|
|
PartName string `json:"part_name"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AddStagePart(ctx context.Context, _ *mcp.CallToolRequest, in AddStagePartInput) (*mcp.CallToolResult, AddStagePartOutput, error) {
|
|
if strings.TrimSpace(in.PartName) == "" {
|
|
return nil, AddStagePartOutput{}, errRequiredField("part_name")
|
|
}
|
|
if err := t.store.AddStagePart(ctx, in.StageID, strings.TrimSpace(in.PartName)); err != nil {
|
|
return nil, AddStagePartOutput{}, err
|
|
}
|
|
return nil, AddStagePartOutput{StageID: in.StageID, PartName: in.PartName}, nil
|
|
}
|
|
|
|
// remove_stage_part
|
|
|
|
type RemoveStagePartInput struct {
|
|
StageID int64 `json:"stage_id" jsonschema:"arc stage id"`
|
|
PartName string `json:"part_name" jsonschema:"part name to unlink"`
|
|
}
|
|
|
|
type RemoveStagePartOutput struct {
|
|
Removed bool `json:"removed"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) RemoveStagePart(ctx context.Context, _ *mcp.CallToolRequest, in RemoveStagePartInput) (*mcp.CallToolResult, RemoveStagePartOutput, error) {
|
|
if strings.TrimSpace(in.PartName) == "" {
|
|
return nil, RemoveStagePartOutput{}, errRequiredField("part_name")
|
|
}
|
|
if err := t.store.RemoveStagePart(ctx, in.StageID, strings.TrimSpace(in.PartName)); err != nil {
|
|
return nil, RemoveStagePartOutput{}, err
|
|
}
|
|
return nil, RemoveStagePartOutput{Removed: true}, nil
|
|
}
|
|
|
|
// assign_persona_arc
|
|
|
|
type AssignPersonaArcInput struct {
|
|
Persona string `json:"persona" jsonschema:"persona name"`
|
|
Arc string `json:"arc" jsonschema:"arc name to assign"`
|
|
StartStage string `json:"start_stage" jsonschema:"name of the starting stage"`
|
|
}
|
|
|
|
type AssignPersonaArcOutput struct {
|
|
Persona string `json:"persona"`
|
|
Arc string `json:"arc"`
|
|
Stage string `json:"stage"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AssignPersonaArc(ctx context.Context, _ *mcp.CallToolRequest, in AssignPersonaArcInput) (*mcp.CallToolResult, AssignPersonaArcOutput, error) {
|
|
if strings.TrimSpace(in.Persona) == "" {
|
|
return nil, AssignPersonaArcOutput{}, errRequiredField("persona")
|
|
}
|
|
if strings.TrimSpace(in.Arc) == "" {
|
|
return nil, AssignPersonaArcOutput{}, errRequiredField("arc")
|
|
}
|
|
if strings.TrimSpace(in.StartStage) == "" {
|
|
return nil, AssignPersonaArcOutput{}, errRequiredField("start_stage")
|
|
}
|
|
if err := t.store.AssignPersonaArc(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Arc), strings.TrimSpace(in.StartStage)); err != nil {
|
|
return nil, AssignPersonaArcOutput{}, err
|
|
}
|
|
return nil, AssignPersonaArcOutput{Persona: in.Persona, Arc: in.Arc, Stage: in.StartStage}, nil
|
|
}
|
|
|
|
// advance_persona_stage
|
|
|
|
type AdvancePersonaStageInput struct {
|
|
Persona string `json:"persona" jsonschema:"persona name"`
|
|
}
|
|
|
|
type AdvancePersonaStageOutput struct {
|
|
Stage ext.ArcStage `json:"stage"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) AdvancePersonaStage(ctx context.Context, _ *mcp.CallToolRequest, in AdvancePersonaStageInput) (*mcp.CallToolResult, AdvancePersonaStageOutput, error) {
|
|
if strings.TrimSpace(in.Persona) == "" {
|
|
return nil, AdvancePersonaStageOutput{}, errRequiredField("persona")
|
|
}
|
|
stage, err := t.store.AdvancePersonaStage(ctx, strings.TrimSpace(in.Persona))
|
|
if err != nil {
|
|
return nil, AdvancePersonaStageOutput{}, err
|
|
}
|
|
return nil, AdvancePersonaStageOutput{Stage: stage}, nil
|
|
}
|
|
|
|
// reset_persona_stage
|
|
|
|
type ResetPersonaStageInput struct {
|
|
Persona string `json:"persona" jsonschema:"persona name"`
|
|
}
|
|
|
|
type ResetPersonaStageOutput struct {
|
|
Stage ext.ArcStage `json:"stage"`
|
|
}
|
|
|
|
func (t *AgentPersonasTool) ResetPersonaStage(ctx context.Context, _ *mcp.CallToolRequest, in ResetPersonaStageInput) (*mcp.CallToolResult, ResetPersonaStageOutput, error) {
|
|
if strings.TrimSpace(in.Persona) == "" {
|
|
return nil, ResetPersonaStageOutput{}, errRequiredField("persona")
|
|
}
|
|
stage, err := t.store.ResetPersonaStage(ctx, strings.TrimSpace(in.Persona))
|
|
if err != nil {
|
|
return nil, ResetPersonaStageOutput{}, err
|
|
}
|
|
return nil, ResetPersonaStageOutput{Stage: stage}, nil
|
|
}
|