package models import "time" // The AI agent registry: the one source of truth for which agents exist, which // skills they run and which tools those skills may call. The console's Agent // Studio reads it (and edits the few operator-owned columns); the AI_engine // will read it too. Plan: krow_talent_app/docs/agent-platform-plan.md. // // Agents, tools and seeded skills are DEFINED IN CODE (internal/ai/registry) // and upserted on every boot. Only the columns marked "operator-owned" below // change at runtime, only by roleid 1, and every change writes an // AIRegistryAudit row in the same transaction. A tool can never be invented // from the console: a tool with no implementation behind it is a lie on screen. // // JSON documents are stored as jsonb in string fields, the same way // AgentDecision stores its context — the controllers re-emit them as raw JSON. // AIAgent is one agent, in AI_engine or in the console. type AIAgent struct { Agentid string `json:"agentid" gorm:"primaryKey;column:agentid;size:64"` Name string `json:"name" gorm:"column:name;not null"` Runtime string `json:"runtime" gorm:"column:runtime;size:20;not null"` // engine, console Classref string `json:"classref" gorm:"column:classref"` // where it is implemented, file:line Purpose string `json:"purpose" gorm:"column:purpose"` // Wakeon is what makes the agent run: a NATS subject, a direct task, an // operator prompt. Free text for people, not parsed. Wakeon string `json:"wakeon" gorm:"column:wakeon"` // Status is what the code does today, not what the docs claim: // live, partial, simulation, broken, unmerged, retired. Status string `json:"status" gorm:"column:status;size:20;not null"` // Llmdecision names the model call the agent makes, empty when it makes none. Llmdecision string `json:"llmdecision" gorm:"column:llmdecision"` // Hasautonomygate is true for the agents whose writes are behind an // autonomy switch in AI_engine (Dispatch, Exception, Express). Autonomy can // only be set on these. Hasautonomygate bool `json:"hasautonomygate" gorm:"column:hasautonomygate;default:false"` Sortorder int `json:"sortorder" gorm:"column:sortorder;default:0"` // Operator-owned. Autonomous bool `json:"autonomous" gorm:"column:autonomous;default:false"` Model string `json:"model" gorm:"column:model;size:64"` // empty = the engine's own default Updatedby *int `json:"updatedby" gorm:"column:updatedby"` Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"` Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"` } func (AIAgent) TableName() string { return "aiagents" } // AITool is one capability a skill may call. Entirely code-defined. type AITool struct { Toolname string `json:"toolname" gorm:"primaryKey;column:toolname;size:64"` Description string `json:"description" gorm:"column:description;not null"` // Kind is read, write or notify. A write or notify tool always requires // confirmation unless its agent is autonomous. Kind string `json:"kind" gorm:"column:kind;size:10;not null"` Target string `json:"target" gorm:"column:target"` // the system it touches Implementedat string `json:"implementedat" gorm:"column:implementedat"` // file:line today Inputschema string `json:"-" gorm:"column:inputschema;type:jsonb"` Requiresconfirmation bool `json:"requiresconfirmation" gorm:"column:requiresconfirmation;default:false"` Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"` Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"` } func (AITool) TableName() string { return "aitools" } // AISkill is a named behaviour of one agent, using one or more tools. type AISkill struct { Skillid string `json:"skillid" gorm:"primaryKey;column:skillid;size:64"` Agentid string `json:"agentid" gorm:"column:agentid;size:64;not null;index"` Title string `json:"title" gorm:"column:title;not null"` Category string `json:"category" gorm:"column:category;size:40"` Description string `json:"description" gorm:"column:description"` Sampleprompt string `json:"sampleprompt" gorm:"column:sampleprompt"` // Source is engine, console (the ops-layer skills), or custom (created in // Agent Studio). Custom skills are never touched by the seed. Source string `json:"source" gorm:"column:source;size:20;not null"` Thresholdsschema string `json:"-" gorm:"column:thresholdsschema;type:jsonb"` // Operator-owned. // No gorm default on purpose: with `default:true` gorm omits a false value // from the INSERT and the database default wins, so a skill seeded OFF came // up ON (caught by TestPGSkillSeededOffStaysOff). Every insert sets it. Enabled bool `json:"enabled" gorm:"column:enabled;not null"` Thresholds string `json:"-" gorm:"column:thresholds;type:jsonb"` // Version increases on every operator change, so a consumer holding a copy // can tell it is stale. Version int `json:"version" gorm:"column:version;default:1"` Updatedby *int `json:"updatedby" gorm:"column:updatedby"` Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"` Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"` } func (AISkill) TableName() string { return "aiskills" } // AISkillTool links a skill to a tool it may call. type AISkillTool struct { Skillid string `json:"skillid" gorm:"primaryKey;column:skillid;size:64"` Toolname string `json:"toolname" gorm:"primaryKey;column:toolname;size:64"` } func (AISkillTool) TableName() string { return "aiskilltools" } // AIRegistryAudit is one operator change to the registry. Written in the same // transaction as the change, so a change without its audit row cannot exist. type AIRegistryAudit struct { Auditid int64 `json:"auditid" gorm:"primaryKey;column:auditid;autoIncrement"` Entity string `json:"entity" gorm:"column:entity;size:20;not null;index:idx_airegistryaudit_entity"` // agent, skill Entityid string `json:"entityid" gorm:"column:entityid;size:64;not null;index:idx_airegistryaudit_entity"` Field string `json:"field" gorm:"column:field;size:40;not null"` Oldvalue string `json:"-" gorm:"column:oldvalue;type:jsonb"` Newvalue string `json:"-" gorm:"column:newvalue;type:jsonb"` Changedby int `json:"changedby" gorm:"column:changedby;not null"` // Changedbyemail is the login's email from its token. Recorded alongside // the id because an admin login with no appusers row carries user id 0 — // found in the Phase 2 end-to-end run, where every audit row said "0". Changedbyemail string `json:"changedbyemail" gorm:"column:changedbyemail;size:255"` Changedat time.Time `json:"changedat" gorm:"column:changedat;default:CURRENT_TIMESTAMP;index"` } func (AIRegistryAudit) TableName() string { return "airegistryaudit" }