Add schema for agent personas, parts, traits, and character arcs
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.
This commit is contained in:
2026-05-05 09:43:14 +02:00
parent 1ceb317f4b
commit e285a03639
20 changed files with 4671 additions and 30 deletions

View File

@@ -216,6 +216,7 @@ func routes(logger *slog.Logger, cfg *config.Config, info buildinfo.Info, db *st
RetryMetadata: tools.NewRetryEnrichmentTool(enrichmentRetryer), RetryMetadata: tools.NewRetryEnrichmentTool(enrichmentRetryer),
//Maintenance: tools.NewMaintenanceTool(db), //Maintenance: tools.NewMaintenanceTool(db),
Skills: tools.NewSkillsTool(db, activeProjects), Skills: tools.NewSkillsTool(db, activeProjects),
Personas: tools.NewAgentPersonasTool(db),
ChatHistory: tools.NewChatHistoryTool(db, activeProjects), ChatHistory: tools.NewChatHistoryTool(db, activeProjects),
Describe: tools.NewDescribeTool(db, mcpserver.BuildToolCatalog()), Describe: tools.NewDescribeTool(db, mcpserver.BuildToolCatalog()),
} }

View File

@@ -8,18 +8,19 @@ import (
) )
type ModelPublicAgentGuardrails struct { type ModelPublicAgentGuardrails struct {
bun.BaseModel `bun:"table:public.agent_guardrails,alias:agent_guardrails"` bun.BaseModel `bun:"table:public.agent_guardrails,alias:agent_guardrails"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"` ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
Content resolvespec_common.SqlString `bun:"content,type:text,notnull," json:"content"` Content resolvespec_common.SqlString `bun:"content,type:text,notnull," json:"content"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"` CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"` Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"` GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"` Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
Severity resolvespec_common.SqlString `bun:"severity,type:text,default:'medium',notnull," json:"severity"` Severity resolvespec_common.SqlString `bun:"severity,type:text,default:'medium',notnull," json:"severity"`
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"` Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"` UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelGuardrailIDPublicPlanGuardrails []*ModelPublicPlanGuardrails `bun:"rel:has-many,join:id=guardrail_id" json:"relguardrailidpublicplanguardrails,omitempty"` // Has many ModelPublicPlanGuardrails RelGuardrailIDPublicAgentPersonaGuardrails []*ModelPublicAgentPersonaGuardrails `bun:"rel:has-many,join:id=guardrail_id" json:"relguardrailidpublicagentpersonaguardrails,omitempty"` // Has many ModelPublicAgentPersonaGuardrails
RelGuardrailIDPublicProjectGuardrails []*ModelPublicProjectGuardrails `bun:"rel:has-many,join:id=guardrail_id" json:"relguardrailidpublicprojectguardrails,omitempty"` // Has many ModelPublicProjectGuardrails RelGuardrailIDPublicPlanGuardrails []*ModelPublicPlanGuardrails `bun:"rel:has-many,join:id=guardrail_id" json:"relguardrailidpublicplanguardrails,omitempty"` // Has many ModelPublicPlanGuardrails
RelGuardrailIDPublicProjectGuardrails []*ModelPublicProjectGuardrails `bun:"rel:has-many,join:id=guardrail_id" json:"relguardrailidpublicprojectguardrails,omitempty"` // Has many ModelPublicProjectGuardrails
} }
// TableName returns the table name for ModelPublicAgentGuardrails // TableName returns the table name for ModelPublicAgentGuardrails

View File

@@ -0,0 +1,69 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicAgentParts struct {
bun.BaseModel `bun:"table:public.agent_parts,alias:agent_parts"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
Content resolvespec_common.SqlString `bun:"content,type:text,default:'',notnull," json:"content"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
PartType resolvespec_common.SqlString `bun:"part_type,type:text,notnull," json:"part_type"`
Summary resolvespec_common.SqlString `bun:"summary,type:text,notnull," json:"summary"`
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelPartIDPublicAgentPersonaParts []*ModelPublicAgentPersonaParts `bun:"rel:has-many,join:id=part_id" json:"relpartidpublicagentpersonaparts,omitempty"` // Has many ModelPublicAgentPersonaParts
RelPartIDPublicArcStageParts []*ModelPublicArcStageParts `bun:"rel:has-many,join:id=part_id" json:"relpartidpublicarcstageparts,omitempty"` // Has many ModelPublicArcStageParts
}
// TableName returns the table name for ModelPublicAgentParts
func (m ModelPublicAgentParts) TableName() string {
return "public.agent_parts"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentParts
func (m ModelPublicAgentParts) TableNameOnly() string {
return "agent_parts"
}
// SchemaName returns the schema name for ModelPublicAgentParts
func (m ModelPublicAgentParts) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicAgentParts) GetID() int64 {
return m.ID.Int64()
}
// GetIDStr returns the primary key as a string
func (m ModelPublicAgentParts) GetIDStr() string {
return fmt.Sprintf("%v", m.ID)
}
// SetID sets the primary key value
func (m ModelPublicAgentParts) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicAgentParts) UpdateID(newid int64) {
m.ID.FromString(fmt.Sprintf("%d", newid))
}
// GetIDName returns the name of the primary key column
func (m ModelPublicAgentParts) GetIDName() string {
return "id"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentParts) GetPrefix() string {
return "APG"
}

View File

@@ -0,0 +1,34 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"github.com/uptrace/bun"
)
type ModelPublicAgentPersonaGuardrails struct {
bun.BaseModel `bun:"table:public.agent_persona_guardrails,alias:agent_persona_guardrails"`
GuardrailID int64 `bun:"guardrail_id,type:bigint,notnull," json:"guardrail_id"`
PersonaID int64 `bun:"persona_id,type:bigint,notnull," json:"persona_id"`
RelGuardrailID *ModelPublicAgentGuardrails `bun:"rel:has-one,join:guardrail_id=id" json:"relguardrailid,omitempty"` // Has one ModelPublicAgentGuardrails
RelPersonaID *ModelPublicAgentPersonas `bun:"rel:has-one,join:persona_id=id" json:"relpersonaid,omitempty"` // Has one ModelPublicAgentPersonas
}
// TableName returns the table name for ModelPublicAgentPersonaGuardrails
func (m ModelPublicAgentPersonaGuardrails) TableName() string {
return "public.agent_persona_guardrails"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentPersonaGuardrails
func (m ModelPublicAgentPersonaGuardrails) TableNameOnly() string {
return "agent_persona_guardrails"
}
// SchemaName returns the schema name for ModelPublicAgentPersonaGuardrails
func (m ModelPublicAgentPersonaGuardrails) SchemaName() string {
return "public"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentPersonaGuardrails) GetPrefix() string {
return "APG"
}

View File

@@ -0,0 +1,36 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"github.com/uptrace/bun"
)
type ModelPublicAgentPersonaParts struct {
bun.BaseModel `bun:"table:public.agent_persona_parts,alias:agent_persona_parts"`
PartID int64 `bun:"part_id,type:bigint,notnull," json:"part_id"`
PartOrder int32 `bun:"part_order,type:int,default:0,notnull," json:"part_order"`
PersonaID int64 `bun:"persona_id,type:bigint,notnull," json:"persona_id"`
Priority int32 `bun:"priority,type:int,default:0,notnull," json:"priority"`
RelPartID *ModelPublicAgentParts `bun:"rel:has-one,join:part_id=id" json:"relpartid,omitempty"` // Has one ModelPublicAgentParts
RelPersonaID *ModelPublicAgentPersonas `bun:"rel:has-one,join:persona_id=id" json:"relpersonaid,omitempty"` // Has one ModelPublicAgentPersonas
}
// TableName returns the table name for ModelPublicAgentPersonaParts
func (m ModelPublicAgentPersonaParts) TableName() string {
return "public.agent_persona_parts"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentPersonaParts
func (m ModelPublicAgentPersonaParts) TableNameOnly() string {
return "agent_persona_parts"
}
// SchemaName returns the schema name for ModelPublicAgentPersonaParts
func (m ModelPublicAgentPersonaParts) SchemaName() string {
return "public"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentPersonaParts) GetPrefix() string {
return "APP"
}

View File

@@ -0,0 +1,34 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"github.com/uptrace/bun"
)
type ModelPublicAgentPersonaSkills struct {
bun.BaseModel `bun:"table:public.agent_persona_skills,alias:agent_persona_skills"`
PersonaID int64 `bun:"persona_id,type:bigint,notnull," json:"persona_id"`
SkillID int64 `bun:"skill_id,type:bigint,notnull," json:"skill_id"`
RelPersonaID *ModelPublicAgentPersonas `bun:"rel:has-one,join:persona_id=id" json:"relpersonaid,omitempty"` // Has one ModelPublicAgentPersonas
RelSkillID *ModelPublicAgentSkills `bun:"rel:has-one,join:skill_id=id" json:"relskillid,omitempty"` // Has one ModelPublicAgentSkills
}
// TableName returns the table name for ModelPublicAgentPersonaSkills
func (m ModelPublicAgentPersonaSkills) TableName() string {
return "public.agent_persona_skills"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentPersonaSkills
func (m ModelPublicAgentPersonaSkills) TableNameOnly() string {
return "agent_persona_skills"
}
// SchemaName returns the schema name for ModelPublicAgentPersonaSkills
func (m ModelPublicAgentPersonaSkills) SchemaName() string {
return "public"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentPersonaSkills) GetPrefix() string {
return "APS"
}

View File

@@ -0,0 +1,34 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"github.com/uptrace/bun"
)
type ModelPublicAgentPersonaTraits struct {
bun.BaseModel `bun:"table:public.agent_persona_traits,alias:agent_persona_traits"`
PersonaID int64 `bun:"persona_id,type:bigint,notnull," json:"persona_id"`
TraitID int64 `bun:"trait_id,type:bigint,notnull," json:"trait_id"`
RelPersonaID *ModelPublicAgentPersonas `bun:"rel:has-one,join:persona_id=id" json:"relpersonaid,omitempty"` // Has one ModelPublicAgentPersonas
RelTraitID *ModelPublicAgentTraits `bun:"rel:has-one,join:trait_id=id" json:"reltraitid,omitempty"` // Has one ModelPublicAgentTraits
}
// TableName returns the table name for ModelPublicAgentPersonaTraits
func (m ModelPublicAgentPersonaTraits) TableName() string {
return "public.agent_persona_traits"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentPersonaTraits
func (m ModelPublicAgentPersonaTraits) TableNameOnly() string {
return "agent_persona_traits"
}
// SchemaName returns the schema name for ModelPublicAgentPersonaTraits
func (m ModelPublicAgentPersonaTraits) SchemaName() string {
return "public"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentPersonaTraits) GetPrefix() string {
return "APT"
}

View File

@@ -0,0 +1,74 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicAgentPersonas struct {
bun.BaseModel `bun:"table:public.agent_personas,alias:agent_personas"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
CompiledAt resolvespec_common.SqlTimeStamp `bun:"compiled_at,type:timestamptz,nullzero," json:"compiled_at"`
CompiledDetail resolvespec_common.SqlString `bun:"compiled_detail,type:text,default:'',notnull," json:"compiled_detail"`
CompiledSummary resolvespec_common.SqlString `bun:"compiled_summary,type:text,default:'',notnull," json:"compiled_summary"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
Detail resolvespec_common.SqlString `bun:"detail,type:text,default:'',notnull," json:"detail"`
GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
Summary resolvespec_common.SqlString `bun:"summary,type:text,notnull," json:"summary"`
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelPersonaIDPublicAgentPersonaParts []*ModelPublicAgentPersonaParts `bun:"rel:has-many,join:id=persona_id" json:"relpersonaidpublicagentpersonaparts,omitempty"` // Has many ModelPublicAgentPersonaParts
RelPersonaIDPublicAgentPersonaSkills []*ModelPublicAgentPersonaSkills `bun:"rel:has-many,join:id=persona_id" json:"relpersonaidpublicagentpersonaskills,omitempty"` // Has many ModelPublicAgentPersonaSkills
RelPersonaIDPublicAgentPersonaGuardrails []*ModelPublicAgentPersonaGuardrails `bun:"rel:has-many,join:id=persona_id" json:"relpersonaidpublicagentpersonaguardrails,omitempty"` // Has many ModelPublicAgentPersonaGuardrails
RelPersonaIDPublicAgentPersonaTraits []*ModelPublicAgentPersonaTraits `bun:"rel:has-many,join:id=persona_id" json:"relpersonaidpublicagentpersonatraits,omitempty"` // Has many ModelPublicAgentPersonaTraits
RelPersonaIDPublicPersonaArcs []*ModelPublicPersonaArc `bun:"rel:has-many,join:id=persona_id" json:"relpersonaidpublicpersonaarcs,omitempty"` // Has many ModelPublicPersonaArc
}
// TableName returns the table name for ModelPublicAgentPersonas
func (m ModelPublicAgentPersonas) TableName() string {
return "public.agent_personas"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentPersonas
func (m ModelPublicAgentPersonas) TableNameOnly() string {
return "agent_personas"
}
// SchemaName returns the schema name for ModelPublicAgentPersonas
func (m ModelPublicAgentPersonas) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicAgentPersonas) GetID() int64 {
return m.ID.Int64()
}
// GetIDStr returns the primary key as a string
func (m ModelPublicAgentPersonas) GetIDStr() string {
return fmt.Sprintf("%v", m.ID)
}
// SetID sets the primary key value
func (m ModelPublicAgentPersonas) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicAgentPersonas) UpdateID(newid int64) {
m.ID.FromString(fmt.Sprintf("%d", newid))
}
// GetIDName returns the name of the primary key column
func (m ModelPublicAgentPersonas) GetIDName() string {
return "id"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentPersonas) GetPrefix() string {
return "APG"
}

View File

@@ -8,22 +8,23 @@ import (
) )
type ModelPublicAgentSkills struct { type ModelPublicAgentSkills struct {
bun.BaseModel `bun:"table:public.agent_skills,alias:agent_skills"` bun.BaseModel `bun:"table:public.agent_skills,alias:agent_skills"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"` ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
Content resolvespec_common.SqlString `bun:"content,type:text,notnull," json:"content"` Content resolvespec_common.SqlString `bun:"content,type:text,notnull," json:"content"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"` CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"` Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
DomainTags resolvespec_common.SqlStringArray `bun:"domain_tags,type:text[],default:'{}',notnull," json:"domain_tags"` DomainTags resolvespec_common.SqlStringArray `bun:"domain_tags,type:text[],default:'{}',notnull," json:"domain_tags"`
FrameworkTags resolvespec_common.SqlStringArray `bun:"framework_tags,type:text[],default:'{}',notnull," json:"framework_tags"` FrameworkTags resolvespec_common.SqlStringArray `bun:"framework_tags,type:text[],default:'{}',notnull," json:"framework_tags"`
GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"` GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"`
LanguageTags resolvespec_common.SqlStringArray `bun:"language_tags,type:text[],default:'{}',notnull," json:"language_tags"` LanguageTags resolvespec_common.SqlStringArray `bun:"language_tags,type:text[],default:'{}',notnull," json:"language_tags"`
LibraryTags resolvespec_common.SqlStringArray `bun:"library_tags,type:text[],default:'{}',notnull," json:"library_tags"` LibraryTags resolvespec_common.SqlStringArray `bun:"library_tags,type:text[],default:'{}',notnull," json:"library_tags"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"` Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"` Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"` UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelRelatedSkillIDPublicLearnings []*ModelPublicLearnings `bun:"rel:has-many,join:id=related_skill_id" json:"relrelatedskillidpubliclearnings,omitempty"` // Has many ModelPublicLearnings RelSkillIDPublicAgentPersonaSkills []*ModelPublicAgentPersonaSkills `bun:"rel:has-many,join:id=skill_id" json:"relskillidpublicagentpersonaskills,omitempty"` // Has many ModelPublicAgentPersonaSkills
RelSkillIDPublicPlanSkills []*ModelPublicPlanSkills `bun:"rel:has-many,join:id=skill_id" json:"relskillidpublicplanskills,omitempty"` // Has many ModelPublicPlanSkills RelRelatedSkillIDPublicLearnings []*ModelPublicLearnings `bun:"rel:has-many,join:id=related_skill_id" json:"relrelatedskillidpubliclearnings,omitempty"` // Has many ModelPublicLearnings
RelSkillIDPublicProjectSkills []*ModelPublicProjectSkills `bun:"rel:has-many,join:id=skill_id" json:"relskillidpublicprojectskills,omitempty"` // Has many ModelPublicProjectSkills RelSkillIDPublicPlanSkills []*ModelPublicPlanSkills `bun:"rel:has-many,join:id=skill_id" json:"relskillidpublicplanskills,omitempty"` // Has many ModelPublicPlanSkills
RelSkillIDPublicProjectSkills []*ModelPublicProjectSkills `bun:"rel:has-many,join:id=skill_id" json:"relskillidpublicprojectskills,omitempty"` // Has many ModelPublicProjectSkills
} }
// TableName returns the table name for ModelPublicAgentSkills // TableName returns the table name for ModelPublicAgentSkills

View File

@@ -0,0 +1,67 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicAgentTraits struct {
bun.BaseModel `bun:"table:public.agent_traits,alias:agent_traits"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
GUID resolvespec_common.SqlUUID `bun:"guid,type:uuid,default:gen_random_uuid(),notnull," json:"guid"`
Instruction resolvespec_common.SqlString `bun:"instruction,type:text,default:'',notnull," json:"instruction"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
TraitType resolvespec_common.SqlString `bun:"trait_type,type:text,notnull," json:"trait_type"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelTraitIDPublicAgentPersonaTraits []*ModelPublicAgentPersonaTraits `bun:"rel:has-many,join:id=trait_id" json:"reltraitidpublicagentpersonatraits,omitempty"` // Has many ModelPublicAgentPersonaTraits
}
// TableName returns the table name for ModelPublicAgentTraits
func (m ModelPublicAgentTraits) TableName() string {
return "public.agent_traits"
}
// TableNameOnly returns the table name without schema for ModelPublicAgentTraits
func (m ModelPublicAgentTraits) TableNameOnly() string {
return "agent_traits"
}
// SchemaName returns the schema name for ModelPublicAgentTraits
func (m ModelPublicAgentTraits) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicAgentTraits) GetID() int64 {
return m.ID.Int64()
}
// GetIDStr returns the primary key as a string
func (m ModelPublicAgentTraits) GetIDStr() string {
return fmt.Sprintf("%v", m.ID)
}
// SetID sets the primary key value
func (m ModelPublicAgentTraits) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicAgentTraits) UpdateID(newid int64) {
m.ID.FromString(fmt.Sprintf("%d", newid))
}
// GetIDName returns the name of the primary key column
func (m ModelPublicAgentTraits) GetIDName() string {
return "id"
}
// GetPrefix returns the table prefix
func (m ModelPublicAgentTraits) GetPrefix() string {
return "ATG"
}

View File

@@ -0,0 +1,34 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"github.com/uptrace/bun"
)
type ModelPublicArcStageParts struct {
bun.BaseModel `bun:"table:public.arc_stage_parts,alias:arc_stage_parts"`
PartID int64 `bun:"part_id,type:bigint,notnull," json:"part_id"`
StageID int64 `bun:"stage_id,type:bigint,notnull," json:"stage_id"`
RelPartID *ModelPublicAgentParts `bun:"rel:has-one,join:part_id=id" json:"relpartid,omitempty"` // Has one ModelPublicAgentParts
RelStageID *ModelPublicArcStages `bun:"rel:has-one,join:stage_id=id" json:"relstageid,omitempty"` // Has one ModelPublicArcStages
}
// TableName returns the table name for ModelPublicArcStageParts
func (m ModelPublicArcStageParts) TableName() string {
return "public.arc_stage_parts"
}
// TableNameOnly returns the table name without schema for ModelPublicArcStageParts
func (m ModelPublicArcStageParts) TableNameOnly() string {
return "arc_stage_parts"
}
// SchemaName returns the schema name for ModelPublicArcStageParts
func (m ModelPublicArcStageParts) SchemaName() string {
return "public"
}
// GetPrefix returns the table prefix
func (m ModelPublicArcStageParts) GetPrefix() string {
return "ASP"
}

View File

@@ -0,0 +1,67 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicArcStages struct {
bun.BaseModel `bun:"table:public.arc_stages,alias:arc_stages"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
ArcID int64 `bun:"arc_id,type:bigint,notnull," json:"arc_id"`
Condition resolvespec_common.SqlString `bun:"condition,type:text,default:'',notnull," json:"condition"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
StageOrder int32 `bun:"stage_order,type:int,default:0,notnull," json:"stage_order"`
RelArcID *ModelPublicCharacterArcs `bun:"rel:has-one,join:arc_id=id" json:"relarcid,omitempty"` // Has one ModelPublicCharacterArcs
RelStageIDPublicArcStageParts []*ModelPublicArcStageParts `bun:"rel:has-many,join:id=stage_id" json:"relstageidpublicarcstageparts,omitempty"` // Has many ModelPublicArcStageParts
RelCurrentStageIDPublicPersonaArcs []*ModelPublicPersonaArc `bun:"rel:has-many,join:id=current_stage_id" json:"relcurrentstageidpublicpersonaarcs,omitempty"` // Has many ModelPublicPersonaArc
}
// TableName returns the table name for ModelPublicArcStages
func (m ModelPublicArcStages) TableName() string {
return "public.arc_stages"
}
// TableNameOnly returns the table name without schema for ModelPublicArcStages
func (m ModelPublicArcStages) TableNameOnly() string {
return "arc_stages"
}
// SchemaName returns the schema name for ModelPublicArcStages
func (m ModelPublicArcStages) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicArcStages) GetID() int64 {
return m.ID.Int64()
}
// GetIDStr returns the primary key as a string
func (m ModelPublicArcStages) GetIDStr() string {
return fmt.Sprintf("%v", m.ID)
}
// SetID sets the primary key value
func (m ModelPublicArcStages) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicArcStages) UpdateID(newid int64) {
m.ID.FromString(fmt.Sprintf("%d", newid))
}
// GetIDName returns the name of the primary key column
func (m ModelPublicArcStages) GetIDName() string {
return "id"
}
// GetPrefix returns the table prefix
func (m ModelPublicArcStages) GetPrefix() string {
return "ASR"
}

View File

@@ -0,0 +1,65 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicCharacterArcs struct {
bun.BaseModel `bun:"table:public.character_arcs,alias:character_arcs"`
ID resolvespec_common.SqlInt64 `bun:"id,type:bigserial,pk,autoincrement," json:"id"`
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
Description resolvespec_common.SqlString `bun:"description,type:text,default:'',notnull," json:"description"`
Name resolvespec_common.SqlString `bun:"name,type:text,notnull," json:"name"`
Summary resolvespec_common.SqlString `bun:"summary,type:text,default:'',notnull," json:"summary"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelArcIDPublicArcStages []*ModelPublicArcStages `bun:"rel:has-many,join:id=arc_id" json:"relarcidpublicarcstages,omitempty"` // Has many ModelPublicArcStages
RelArcIDPublicPersonaArcs []*ModelPublicPersonaArc `bun:"rel:has-many,join:id=arc_id" json:"relarcidpublicpersonaarcs,omitempty"` // Has many ModelPublicPersonaArc
}
// TableName returns the table name for ModelPublicCharacterArcs
func (m ModelPublicCharacterArcs) TableName() string {
return "public.character_arcs"
}
// TableNameOnly returns the table name without schema for ModelPublicCharacterArcs
func (m ModelPublicCharacterArcs) TableNameOnly() string {
return "character_arcs"
}
// SchemaName returns the schema name for ModelPublicCharacterArcs
func (m ModelPublicCharacterArcs) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicCharacterArcs) GetID() int64 {
return m.ID.Int64()
}
// GetIDStr returns the primary key as a string
func (m ModelPublicCharacterArcs) GetIDStr() string {
return fmt.Sprintf("%v", m.ID)
}
// SetID sets the primary key value
func (m ModelPublicCharacterArcs) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicCharacterArcs) UpdateID(newid int64) {
m.ID.FromString(fmt.Sprintf("%d", newid))
}
// GetIDName returns the name of the primary key column
func (m ModelPublicCharacterArcs) GetIDName() string {
return "id"
}
// GetPrefix returns the table prefix
func (m ModelPublicCharacterArcs) GetPrefix() string {
return "CAH"
}

View File

@@ -0,0 +1,64 @@
// Code generated by relspecgo. DO NOT EDIT.
package generatedmodels
import (
"fmt"
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
"github.com/uptrace/bun"
)
type ModelPublicPersonaArc struct {
bun.BaseModel `bun:"table:public.persona_arc,alias:persona_arc"`
PersonaID int64 `bun:"persona_id,type:bigint,pk," json:"persona_id"`
ArcID int64 `bun:"arc_id,type:bigint,notnull," json:"arc_id"`
CurrentStageID int64 `bun:"current_stage_id,type:bigint,notnull," json:"current_stage_id"`
UpdatedAt resolvespec_common.SqlTimeStamp `bun:"updated_at,type:timestamptz,default:now(),notnull," json:"updated_at"`
RelArcID *ModelPublicCharacterArcs `bun:"rel:has-one,join:arc_id=id" json:"relarcid,omitempty"` // Has one ModelPublicCharacterArcs
RelCurrentStageID *ModelPublicArcStages `bun:"rel:has-one,join:current_stage_id=id" json:"relcurrentstageid,omitempty"` // Has one ModelPublicArcStages
RelPersonaID *ModelPublicAgentPersonas `bun:"rel:has-one,join:persona_id=id" json:"relpersonaid,omitempty"` // Has one ModelPublicAgentPersonas
}
// TableName returns the table name for ModelPublicPersonaArc
func (m ModelPublicPersonaArc) TableName() string {
return "public.persona_arc"
}
// TableNameOnly returns the table name without schema for ModelPublicPersonaArc
func (m ModelPublicPersonaArc) TableNameOnly() string {
return "persona_arc"
}
// SchemaName returns the schema name for ModelPublicPersonaArc
func (m ModelPublicPersonaArc) SchemaName() string {
return "public"
}
// GetID returns the primary key value
func (m ModelPublicPersonaArc) GetID() int64 {
return int64(m.PersonaID)
}
// GetIDStr returns the primary key as a string
func (m ModelPublicPersonaArc) GetIDStr() string {
return fmt.Sprintf("%d", m.PersonaID)
}
// SetID sets the primary key value
func (m ModelPublicPersonaArc) SetID(newid int64) {
m.UpdateID(newid)
}
// UpdateID updates the primary key value
func (m *ModelPublicPersonaArc) UpdateID(newid int64) {
m.PersonaID = newid
}
// GetIDName returns the name of the primary key column
func (m ModelPublicPersonaArc) GetIDName() string {
return "persona_id"
}
// GetPrefix returns the table prefix
func (m ModelPublicPersonaArc) GetPrefix() string {
return "PAE"
}

View File

@@ -38,6 +38,7 @@ type ToolSet struct {
RetryMetadata *tools.RetryEnrichmentTool RetryMetadata *tools.RetryEnrichmentTool
//Maintenance *tools.MaintenanceTool //Maintenance *tools.MaintenanceTool
Skills *tools.SkillsTool Skills *tools.SkillsTool
Personas *tools.AgentPersonasTool
ChatHistory *tools.ChatHistoryTool ChatHistory *tools.ChatHistoryTool
Describe *tools.DescribeTool Describe *tools.DescribeTool
Learnings *tools.LearningsTool Learnings *tools.LearningsTool
@@ -90,6 +91,7 @@ func NewHandlers(cfg config.MCPConfig, logger *slog.Logger, toolSet ToolSet, onS
registerFileTools, registerFileTools,
registerMaintenanceTools, registerMaintenanceTools,
registerSkillTools, registerSkillTools,
registerPersonaTools,
registerChatHistoryTools, registerChatHistoryTools,
registerDescribeTools, registerDescribeTools,
} { } {
@@ -565,10 +567,114 @@ func registerChatHistoryTools(server *mcp.Server, logger *slog.Logger, toolSet T
return nil return nil
} }
func registerPersonaTools(server *mcp.Server, logger *slog.Logger, toolSet ToolSet) error {
p := toolSet.Personas
if err := addTool(server, logger, &mcp.Tool{Name: "create_agent_persona", Description: "Create a named, loadable agent persona."}, p.CreatePersona); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "update_agent_persona", Description: "Update persona fields (name, description, summary, detail, tags)."}, p.UpdatePersona); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "delete_agent_persona", Description: "Delete a persona by name."}, p.DeletePersona); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "list_agent_personas", Description: "List all personas, optionally filtered by tag."}, p.ListPersonas); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "get_agent_persona", Description: "Load a persona with assembled parts. detail=true returns full content. overrides replaces parts per type at runtime."}, p.GetPersona); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "get_persona_manifest", Description: "Lightweight structure-only view: parts, traits, skills, guardrails — no content. Includes on_demand_tools hints."}, p.GetPersonaManifest); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "compile_persona", Description: "Regenerate compiled_summary and compiled_detail from current parts and arc stage. Use compiled_summary for agents with tight context budgets."}, p.CompilePersona); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "create_agent_part", Description: "Create a reusable behaviour building block. Parts compose personas and can be overridden at load time by name."}, p.CreatePart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "update_agent_part", Description: "Update part fields (name, part_type, description, summary, content, tags)."}, p.UpdatePart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "delete_agent_part", Description: "Delete a part by name."}, p.DeletePart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "list_agent_parts", Description: "List parts, optionally filtered by part_type or tag."}, p.ListParts); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "get_agent_part", Description: "Fetch a single part with full content by name."}, p.GetPart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_persona_part", Description: "Link a part to a persona with optional order and priority."}, p.AddPersonaPart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "remove_persona_part", Description: "Unlink a part from a persona."}, p.RemovePersonaPart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_persona_skill", Description: "Link an agent_skill to a persona."}, p.AddPersonaSkill); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "remove_persona_skill", Description: "Unlink an agent_skill from a persona."}, p.RemovePersonaSkill); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_persona_guardrail", Description: "Link an agent_guardrail to a persona."}, p.AddPersonaGuardrail); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "remove_persona_guardrail", Description: "Unlink an agent_guardrail from a persona."}, p.RemovePersonaGuardrail); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "create_agent_trait", Description: "Create an atomic personality trait (personality, cognitive, emotional, social, behavioral)."}, p.CreateTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "update_agent_trait", Description: "Update trait fields."}, p.UpdateTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "delete_agent_trait", Description: "Delete a trait by name."}, p.DeleteTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "list_agent_traits", Description: "List traits, optionally filtered by trait_type or tag."}, p.ListTraits); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "get_agent_trait", Description: "Fetch a single trait with instruction by name."}, p.GetTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_persona_trait", Description: "Link a trait to a persona."}, p.AddPersonaTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "remove_persona_trait", Description: "Unlink a trait from a persona."}, p.RemovePersonaTrait); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "create_character_arc", Description: "Define a named character progression arc."}, p.CreateCharacterArc); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "list_character_arcs", Description: "List all character arcs."}, p.ListCharacterArcs); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_arc_stage", Description: "Add an ordered stage to a character arc."}, p.AddArcStage); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "add_stage_part", Description: "Link a part to an arc stage — active when the persona is at that stage."}, p.AddStagePart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "remove_stage_part", Description: "Unlink a part from an arc stage."}, p.RemoveStagePart); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "assign_persona_arc", Description: "Attach an arc to a persona and set the starting stage."}, p.AssignPersonaArc); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "advance_persona_stage", Description: "Move persona to the next stage in its arc."}, p.AdvancePersonaStage); err != nil {
return err
}
if err := addTool(server, logger, &mcp.Tool{Name: "reset_persona_stage", Description: "Reset persona to the first stage of its arc."}, p.ResetPersonaStage); err != nil {
return err
}
return nil
}
func registerDescribeTools(server *mcp.Server, logger *slog.Logger, toolSet ToolSet) error { func registerDescribeTools(server *mcp.Server, logger *slog.Logger, toolSet ToolSet) error {
if err := addTool(server, logger, &mcp.Tool{ if err := addTool(server, logger, &mcp.Tool{
Name: "describe_tools", Name: "describe_tools",
Description: "Call first each session. All tools with categories and usage notes. Categories: system, thoughts, projects, files, admin, maintenance, skills, plans, chat, meta.", Description: "Call first each session. All tools with categories and usage notes. Categories: system, thoughts, projects, files, admin, maintenance, skills, personas, plans, chat, meta.",
}, toolSet.Describe.Describe); err != nil { }, toolSet.Describe.Describe); err != nil {
return err return err
} }
@@ -664,6 +770,41 @@ func BuildToolCatalog() []tools.ToolEntry {
{Name: "remove_project_guardrail", Description: "Unlink an agent guardrail from a project. Pass project explicitly when your client does not preserve MCP sessions.", Category: "skills"}, {Name: "remove_project_guardrail", Description: "Unlink an agent guardrail from a project. Pass project explicitly when your client does not preserve MCP sessions.", Category: "skills"},
{Name: "list_project_guardrails", Description: "List all guardrails linked to a project. Call this at the start of every project session to load agent constraints before generating new ones. Only create new guardrails if none are returned. Pass project explicitly when your client does not preserve MCP sessions.", Category: "skills"}, {Name: "list_project_guardrails", Description: "List all guardrails linked to a project. Call this at the start of every project session to load agent constraints before generating new ones. Only create new guardrails if none are returned. Pass project explicitly when your client does not preserve MCP sessions.", Category: "skills"},
// personas
{Name: "create_agent_persona", Description: "Create a named, loadable agent persona that composes parts, traits, skills, and guardrails.", Category: "personas"},
{Name: "update_agent_persona", Description: "Update persona fields.", Category: "personas"},
{Name: "delete_agent_persona", Description: "Delete a persona by name.", Category: "personas"},
{Name: "list_agent_personas", Description: "List all personas, optionally filtered by tag.", Category: "personas"},
{Name: "get_agent_persona", Description: "Load a persona with all assembled parts. detail=true returns full content. overrides replaces parts per type at runtime without modifying the persona.", Category: "personas"},
{Name: "get_persona_manifest", Description: "Lightweight discovery: returns persona structure (parts, traits, skills, guardrails) with no content, plus on_demand_tools hints. Call this first for unknown personas.", Category: "personas"},
{Name: "compile_persona", Description: "Regenerate compiled_summary and compiled_detail from current parts and arc stage. Agents with tight context budgets can use compiled_summary directly.", Category: "personas"},
{Name: "create_agent_part", Description: "Create a reusable behaviour building block. Part types: system, agent, soul, identity, skill, specialization, tone, goal, context, protocol, backstory, motivation, voice, archetype, flaw, relationship.", Category: "personas"},
{Name: "update_agent_part", Description: "Update part fields.", Category: "personas"},
{Name: "delete_agent_part", Description: "Delete a part by name.", Category: "personas"},
{Name: "list_agent_parts", Description: "List parts, optionally filtered by part_type or tag.", Category: "personas"},
{Name: "get_agent_part", Description: "Fetch a single part with full content by name.", Category: "personas"},
{Name: "add_persona_part", Description: "Link a part to a persona with optional order and priority.", Category: "personas"},
{Name: "remove_persona_part", Description: "Unlink a part from a persona.", Category: "personas"},
{Name: "add_persona_skill", Description: "Link an agent_skill to a persona.", Category: "personas"},
{Name: "remove_persona_skill", Description: "Unlink an agent_skill from a persona.", Category: "personas"},
{Name: "add_persona_guardrail", Description: "Link an agent_guardrail to a persona.", Category: "personas"},
{Name: "remove_persona_guardrail", Description: "Unlink an agent_guardrail from a persona.", Category: "personas"},
{Name: "create_agent_trait", Description: "Create an atomic personality trait. Trait types: personality, cognitive, emotional, social, behavioral.", Category: "personas"},
{Name: "update_agent_trait", Description: "Update trait fields.", Category: "personas"},
{Name: "delete_agent_trait", Description: "Delete a trait by name.", Category: "personas"},
{Name: "list_agent_traits", Description: "List traits, optionally filtered by trait_type or tag.", Category: "personas"},
{Name: "get_agent_trait", Description: "Fetch a single trait with instruction by name. Use this for on-demand trait loading.", Category: "personas"},
{Name: "add_persona_trait", Description: "Link a trait to a persona.", Category: "personas"},
{Name: "remove_persona_trait", Description: "Unlink a trait from a persona.", Category: "personas"},
{Name: "create_character_arc", Description: "Define a named character progression arc with ordered stages.", Category: "personas"},
{Name: "list_character_arcs", Description: "List all character arcs.", Category: "personas"},
{Name: "add_arc_stage", Description: "Add an ordered stage to a character arc.", Category: "personas"},
{Name: "add_stage_part", Description: "Link a part to an arc stage — overrides matching persona parts when that stage is active.", Category: "personas"},
{Name: "remove_stage_part", Description: "Unlink a part from an arc stage.", Category: "personas"},
{Name: "assign_persona_arc", Description: "Attach a character arc to a persona and set the starting stage.", Category: "personas"},
{Name: "advance_persona_stage", Description: "Move persona to the next stage in its arc.", Category: "personas"},
{Name: "reset_persona_stage", Description: "Reset persona to the first stage of its arc.", Category: "personas"},
// chat // chat
{Name: "save_chat_history", Description: "Save a chat session's message history for later retrieval. Stores messages with optional title, summary, channel, agent, and project metadata.", Category: "chat"}, {Name: "save_chat_history", Description: "Save a chat session's message history for later retrieval. Stores messages with optional title, summary, channel, agent, and project metadata.", Category: "chat"},
{Name: "get_chat_history", Description: "Retrieve a saved chat history by its UUID or session_id. Returns the full message list.", Category: "chat"}, {Name: "get_chat_history", Description: "Retrieve a saved chat history by its UUID or session_id. Returns the full message list.", Category: "chat"},
@@ -671,7 +812,7 @@ func BuildToolCatalog() []tools.ToolEntry {
{Name: "delete_chat_history", Description: "Permanently delete a saved chat history by id.", Category: "chat"}, {Name: "delete_chat_history", Description: "Permanently delete a saved chat history by id.", Category: "chat"},
// meta // meta
{Name: "describe_tools", Description: "Call this first in every session. Returns all available MCP tools with names, descriptions, categories, and your accumulated usage notes. Filter by category to narrow results. Available categories: system, thoughts, projects, files, admin, household, maintenance, calendar, meals, crm, skills, plans, chat, meta.", Category: "meta"}, {Name: "describe_tools", Description: "Call this first in every session. Returns all available MCP tools with names, descriptions, categories, and your accumulated usage notes. Filter by category to narrow results. Available categories: system, thoughts, projects, files, admin, household, maintenance, calendar, meals, crm, skills, personas, plans, chat, meta.", Category: "meta"},
{Name: "annotate_tool", Description: "Persist usage notes, gotchas, or workflow patterns for a specific tool. Notes survive across sessions and are returned by describe_tools. Call this whenever you discover something non-obvious about a tool's behaviour. Pass an empty string to clear notes.", Category: "meta"}, {Name: "annotate_tool", Description: "Persist usage notes, gotchas, or workflow patterns for a specific tool. Notes survive across sessions and are returned by describe_tools. Call this whenever you discover something non-obvious about a tool's behaviour. Pass an empty string to clear notes.", Category: "meta"},
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,873 @@
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
}

View File

@@ -0,0 +1,178 @@
package types
import (
"time"
"github.com/google/uuid"
)
// ──────────────────────────────────────────────
// Core entities
// ──────────────────────────────────────────────
type Persona struct {
ID int64 `json:"id"`
GUID uuid.UUID `json:"guid"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Summary string `json:"summary"`
Detail string `json:"detail,omitempty"`
CompiledSummary string `json:"compiled_summary,omitempty"`
CompiledDetail string `json:"compiled_detail,omitempty"`
CompiledAt *time.Time `json:"compiled_at,omitempty"`
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Part struct {
ID int64 `json:"id"`
GUID uuid.UUID `json:"guid"`
Name string `json:"name"`
PartType string `json:"part_type"`
Description string `json:"description,omitempty"`
Summary string `json:"summary"`
Content string `json:"content,omitempty"`
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Trait struct {
ID int64 `json:"id"`
GUID uuid.UUID `json:"guid"`
Name string `json:"name"`
TraitType string `json:"trait_type"`
Description string `json:"description,omitempty"`
Instruction string `json:"instruction,omitempty"`
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CharacterArc struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Summary string `json:"summary,omitempty"`
Stages []ArcStage `json:"stages,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ArcStage struct {
ID int64 `json:"id"`
ArcID int64 `json:"arc_id"`
Name string `json:"name"`
StageOrder int `json:"stage_order"`
Description string `json:"description,omitempty"`
Condition string `json:"condition,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// ──────────────────────────────────────────────
// Assembled persona (get_agent_persona result)
// ──────────────────────────────────────────────
// PersonaFull is returned by get_agent_persona. Parts, skills, guardrails, and
// traits are assembled according to priority order:
//
// 1. runtime overrides (highest)
// 2. active arc-stage parts
// 3. persona-linked parts (base)
type PersonaFull struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Body string `json:"body"` // summary or detail, depending on mode
CompiledSummary string `json:"compiled_summary,omitempty"`
Tags []string `json:"tags"`
Parts []AssembledPart `json:"parts"`
Skills []PersonaSkillEntry `json:"skills"`
Guardrails []PersonaGuardrailEntry `json:"guardrails"`
Traits []PersonaTraitEntry `json:"traits"`
Arc *PersonaArcState `json:"arc,omitempty"`
Detail bool `json:"detail"` // whether full detail was requested
}
type AssembledPart struct {
Name string `json:"name"`
PartType string `json:"part_type"`
Description string `json:"description,omitempty"`
Body string `json:"body"` // summary or content, depending on detail mode
Tags []string `json:"tags"`
Source string `json:"source"` // "persona" | "arc_stage" | "override"
}
type PersonaSkillEntry struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Content string `json:"content,omitempty"` // only when detail=true
Tags []string `json:"tags"`
}
type PersonaGuardrailEntry struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Content string `json:"content,omitempty"` // only when detail=true
Severity string `json:"severity,omitempty"` // only when detail=true
Tags []string `json:"tags"`
}
type PersonaTraitEntry struct {
ID int64 `json:"id"`
Name string `json:"name"`
TraitType string `json:"trait_type"`
Description string `json:"description,omitempty"`
Instruction string `json:"instruction,omitempty"` // only when detail=true
Tags []string `json:"tags"`
}
type PersonaArcState struct {
ArcName string `json:"arc_name"`
StageName string `json:"stage_name"`
StageOrder int `json:"stage_order"`
Description string `json:"description,omitempty"`
Condition string `json:"condition,omitempty"`
}
// ──────────────────────────────────────────────
// Manifest (get_persona_manifest result)
// ──────────────────────────────────────────────
type PersonaManifest struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Parts []ManifestPart `json:"parts"`
Traits []ManifestTrait `json:"traits"`
Skills []ManifestSkill `json:"skills"`
Guardrails []ManifestGuardrail `json:"guardrails"`
Arc *PersonaArcState `json:"arc,omitempty"`
OnDemandTools []string `json:"on_demand_tools"`
}
type ManifestPart struct {
Name string `json:"name"`
PartType string `json:"part_type"`
Description string `json:"description,omitempty"`
}
type ManifestTrait struct {
Name string `json:"name"`
TraitType string `json:"trait_type"`
Description string `json:"description,omitempty"`
}
type ManifestSkill struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
type ManifestGuardrail struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Severity string `json:"severity"`
}

File diff suppressed because it is too large Load Diff

132
schema/agent_personas.dbml Normal file
View File

@@ -0,0 +1,132 @@
Table agent_personas {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
description text [not null, default: '']
summary text [not null]
detail text [not null, default: '']
compiled_summary text [not null, default: '']
compiled_detail text [not null, default: '']
compiled_at timestamptz
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
}
Table agent_parts {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
part_type text [not null]
description text [not null, default: '']
summary text [not null]
content text [not null, default: '']
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
}
Table agent_persona_parts {
persona_id bigint [not null, ref: > agent_personas.id]
part_id bigint [not null, ref: > agent_parts.id]
part_order int [not null, default: 0]
priority int [not null, default: 0]
indexes {
(persona_id, part_id) [pk]
persona_id
}
}
Table agent_persona_skills {
persona_id bigint [not null, ref: > agent_personas.id]
skill_id bigint [not null, ref: > agent_skills.id]
indexes {
(persona_id, skill_id) [pk]
persona_id
}
}
Table agent_persona_guardrails {
persona_id bigint [not null, ref: > agent_personas.id]
guardrail_id bigint [not null, ref: > agent_guardrails.id]
indexes {
(persona_id, guardrail_id) [pk]
persona_id
}
}
Table agent_traits {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
trait_type text [not null]
description text [not null, default: '']
instruction text [not null, default: '']
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
}
Table agent_persona_traits {
persona_id bigint [not null, ref: > agent_personas.id]
trait_id bigint [not null, ref: > agent_traits.id]
indexes {
(persona_id, trait_id) [pk]
persona_id
}
}
Table character_arcs {
id bigserial [pk]
name text [unique, not null]
description text [not null, default: '']
summary text [not null, default: '']
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
}
Table arc_stages {
id bigserial [pk]
arc_id bigint [not null, ref: > character_arcs.id]
name text [not null]
stage_order int [not null, default: 0]
description text [not null, default: '']
condition text [not null, default: '']
created_at timestamptz [not null, default: `now()`]
}
Table arc_stage_parts {
stage_id bigint [not null, ref: > arc_stages.id]
part_id bigint [not null, ref: > agent_parts.id]
indexes {
(stage_id, part_id) [pk]
}
}
Table persona_arc {
persona_id bigint [pk, ref: > agent_personas.id]
arc_id bigint [not null, ref: > character_arcs.id]
current_stage_id bigint [not null, ref: > arc_stages.id]
updated_at timestamptz [not null, default: `now()`]
}
// Cross-file refs (for relspecgo merge)
Ref: agent_persona_parts.persona_id > agent_personas.id [delete: cascade]
Ref: agent_persona_parts.part_id > agent_parts.id [delete: cascade]
Ref: agent_persona_skills.persona_id > agent_personas.id [delete: cascade]
Ref: agent_persona_skills.skill_id > agent_skills.id [delete: cascade]
Ref: agent_persona_guardrails.persona_id > agent_personas.id [delete: cascade]
Ref: agent_persona_guardrails.guardrail_id > agent_guardrails.id [delete: cascade]
Ref: agent_persona_traits.persona_id > agent_personas.id [delete: cascade]
Ref: agent_persona_traits.trait_id > agent_traits.id [delete: cascade]
Ref: arc_stages.arc_id > character_arcs.id [delete: cascade]
Ref: arc_stage_parts.stage_id > arc_stages.id [delete: cascade]
Ref: arc_stage_parts.part_id > agent_parts.id [delete: cascade]
Ref: persona_arc.persona_id > agent_personas.id [delete: cascade]
Ref: persona_arc.arc_id > character_arcs.id
Ref: persona_arc.current_stage_id > arc_stages.id