185 lines
5.4 KiB
Go
185 lines
5.4 KiB
Go
package definition
|
|
|
|
import "strings"
|
|
|
|
// The closed vocabulary a definition is allowed to name.
|
|
//
|
|
// Every table here is a transcription of a frontend table, and the conformance
|
|
// suite asserts each one against the vocabulary the JavaScript actually
|
|
// exports (testdata/oracle.json, `vocabulary`) — so a page added to
|
|
// surfaces.js or an icon added to vocabulary.js fails a test here rather than
|
|
// silently making the two ends disagree about what is valid.
|
|
//
|
|
// Nothing is looked up dynamically and nothing is constructed: a definition
|
|
// names a key and this file answers whether that key exists.
|
|
|
|
// pageSurfaces mirrors SKILL_SURFACES in src/lib/skills/surfaces.js — id first,
|
|
// then the alternative spellings an author may use for it.
|
|
var pageSurfaces = []struct {
|
|
ID string
|
|
Aliases []string
|
|
}{
|
|
{ID: "control-center"},
|
|
{ID: "positions"},
|
|
{ID: "create-position", Aliases: []string{"new-position"}},
|
|
{ID: "candidates"},
|
|
{ID: "hired-history", Aliases: []string{"hired"}},
|
|
{ID: "talent-pool"},
|
|
{ID: "krow-forge", Aliases: []string{"university", "forge"}},
|
|
{ID: "analytics"},
|
|
{ID: "activity"},
|
|
{ID: "workspace-agent-configure"},
|
|
{ID: "settings"},
|
|
{ID: "workspace"},
|
|
{ID: "workspace-agents"},
|
|
{ID: "workspace-skills"},
|
|
{ID: "workspace-skill-configure"},
|
|
{ID: "skill-development"},
|
|
{ID: "profile"},
|
|
{ID: "candidates-analysis"},
|
|
}
|
|
|
|
// surfaceByKey resolves every spelling — canonical or alias — to its canonical
|
|
// id.
|
|
var surfaceByKey = func() map[string]string {
|
|
m := map[string]string{}
|
|
for _, s := range pageSurfaces {
|
|
m[s.ID] = s.ID
|
|
for _, a := range s.Aliases {
|
|
m[a] = s.ID
|
|
}
|
|
}
|
|
return m
|
|
}()
|
|
|
|
// SupportedPages is every canonical page id, in declaration order. The order is
|
|
// the order the frontend lists them in when it refuses an unsupported page, and
|
|
// that message is compared byte for byte.
|
|
var SupportedPages = func() []string {
|
|
out := make([]string, len(pageSurfaces))
|
|
for i, s := range pageSurfaces {
|
|
out[i] = s.ID
|
|
}
|
|
return out
|
|
}()
|
|
|
|
// normalizeKey is surfaces.js's own: trimmed, lower-cased, with spaces and
|
|
// underscores read as dashes. `Talent Pool` and `talent_pool` both reach
|
|
// `talent-pool`; the canonical keys never widen, only what an author may type
|
|
// to reach them.
|
|
func normalizeKey(page any) string {
|
|
s := strings.ToLower(jsTrim(jsString(page)))
|
|
if page == nil {
|
|
s = ""
|
|
}
|
|
return strings.Map(func(r rune) rune {
|
|
if r == ' ' || r == '_' || jsIsSpace(r) {
|
|
return '-'
|
|
}
|
|
return r
|
|
}, s)
|
|
}
|
|
|
|
// SurfaceExists reports whether a declared page name refers to a real surface.
|
|
func SurfaceExists(page any) bool {
|
|
_, ok := surfaceByKey[normalizeKey(page)]
|
|
return ok
|
|
}
|
|
|
|
// CanonicalPage is the canonical id a declared page name refers to, or "".
|
|
func CanonicalPage(page any) string { return surfaceByKey[normalizeKey(page)] }
|
|
|
|
/* ── Agent vocabulary — src/lib/agents/vocabulary.js ─────────────────────── */
|
|
|
|
var (
|
|
AgentStatuses = []string{"draft", "published", "archived"}
|
|
ReasoningModes = []string{"fast", "balanced", "deep"}
|
|
KnowledgeKinds = []string{"note", "link", "skill-reference"}
|
|
AgentAccess = []string{"all", "specific"}
|
|
PermissionRole = []string{"manager", "editor", "viewer"}
|
|
AgentIcons = []string{
|
|
"owliver", "sparkles", "briefcase", "users", "user-check",
|
|
"layers", "graduation-cap", "bar-chart", "activity", "shield",
|
|
}
|
|
)
|
|
|
|
const (
|
|
DefaultAgentStatus = "draft"
|
|
DefaultReasoning = "balanced"
|
|
DefaultAgentIcon = "owliver"
|
|
DefaultKnowledgeKind = "note"
|
|
DefaultAgentAccess = "all"
|
|
DefaultPermission = "viewer"
|
|
)
|
|
|
|
func contains(list []string, want string) bool {
|
|
for _, v := range list {
|
|
if v == want {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/* ── Skill vocabulary ────────────────────────────────────────────────────── */
|
|
|
|
// SkillStatuses is the whole of a skill's lifecycle. There is no version and no
|
|
// publish step: migration 000005 states the same two values.
|
|
var SkillStatuses = []string{"active", "inactive"}
|
|
|
|
// levelHeadings are the rungs a workforce skill may define, and the reason a
|
|
// definition is read as workforce rather than assistant. Order is the ladder's.
|
|
var levelHeadings = []string{"Beginner", "Intermediate", "Advanced", "Expert"}
|
|
|
|
// slugify is uiConfig.js's, used to derive an id from a name.
|
|
//
|
|
// String(value || '').toLowerCase().trim()
|
|
// .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
|
|
//
|
|
// The lower-casing happens BEFORE the class replacement, so an upper-case
|
|
// letter becomes itself rather than a dash.
|
|
func slugify(value any) string {
|
|
if !jsTruthy(value) {
|
|
return ""
|
|
}
|
|
s := jsTrim(strings.ToLower(jsString(value)))
|
|
|
|
var b strings.Builder
|
|
dash := false
|
|
for _, r := range s {
|
|
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
|
b.WriteRune(r)
|
|
dash = false
|
|
continue
|
|
}
|
|
if !dash {
|
|
b.WriteByte('-')
|
|
dash = true
|
|
}
|
|
}
|
|
out := b.String()
|
|
out = strings.TrimPrefix(out, "-")
|
|
out = strings.TrimSuffix(out, "-")
|
|
return out
|
|
}
|
|
|
|
// isDefinitionID is the id format the frontend validator enforces and the
|
|
// definition_id CHECK in migration 000005 restates.
|
|
func isDefinitionID(id string) bool {
|
|
if id == "" {
|
|
return false
|
|
}
|
|
for i, r := range id {
|
|
lower := r >= 'a' && r <= 'z'
|
|
digit := r >= '0' && r <= '9'
|
|
if lower || digit {
|
|
continue
|
|
}
|
|
if r == '-' && i > 0 {
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|