41659b2412
The skill half of the persona/skill pair, as a clean redesign (not a faithful lift of mort's 60-method skills.Storage kitchen sink): - skill.go/skill_version.go/validate.go/inputs.go/schedule.go moved clean; the only host couplings severed: llms.IsTierName -> model.IsTierName, and the chatbot DefaultChatbotInputName const localized. - store.go: a DELIBERATELY LEAN SkillStore — skill lifecycle (CRUD + visibility) + versioning + scheduling ONLY. The KV/file/quota sub-stores that were fused into mort's interface are the tools/ store seams; email/channel grants stay host concerns. - runnable.go: Skill.ToRunnable() lowers a skill into run.RunnableAgent (flat tool list, no palette — composition is a host concern); DueAt() helper. - memory.go: NewMemory() — zero-dep in-process SkillStore (visibility filters, newest-first versions). Tests: ToRunnable mapping, visibility (public/shared/private) listing, version ordering + lookup. No mort dependency (go.mod tidy clean); core imports ZERO from skill. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.0 KiB
Go
58 lines
2.0 KiB
Go
package skill
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSkillToRunnable(t *testing.T) {
|
|
s := &Skill{
|
|
ID: "s1", Name: "summarizer", SystemPrompt: "summarize well", ModelTier: "fast",
|
|
MaxIterations: 4, MaxRuntime: 20 * time.Second, Tools: []string{"summarize", "now"},
|
|
}
|
|
r := s.ToRunnable()
|
|
if r.ID != "s1" || r.ModelTier != "fast" || r.MaxIterations != 4 || len(r.LowLevelTools) != 2 {
|
|
t.Fatalf("ToRunnable mapping wrong: %+v", r)
|
|
}
|
|
// A skill exposes a flat tool list, not a palette.
|
|
if len(r.SkillPalette) != 0 || len(r.SubAgentPalette) != 0 {
|
|
t.Errorf("skill should have empty palettes, got %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestMemoryStoreVisibilityAndVersions(t *testing.T) {
|
|
ctx := context.Background()
|
|
m := NewMemory()
|
|
pub := &Skill{ID: "a", Name: "pub", OwnerID: "o1", Visibility: VisibilityPublic}
|
|
shared := &Skill{ID: "b", Name: "shr", OwnerID: "o1", Visibility: VisibilityShared, SharedWith: []string{"bob"}}
|
|
priv := &Skill{ID: "c", Name: "prv", OwnerID: "o1", Visibility: VisibilityPrivate}
|
|
for _, s := range []*Skill{pub, shared, priv} {
|
|
if err := m.Save(ctx, s); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if ps, _ := m.ListPublic(ctx); len(ps) != 1 || ps[0].ID != "a" {
|
|
t.Errorf("ListPublic = %+v", ps)
|
|
}
|
|
if ss, _ := m.ListSharedWith(ctx, "bob"); len(ss) != 1 || ss[0].ID != "b" {
|
|
t.Errorf("ListSharedWith(bob) = %+v", ss)
|
|
}
|
|
if ss, _ := m.ListSharedWith(ctx, "carol"); len(ss) != 0 {
|
|
t.Errorf("ListSharedWith(carol) should be empty, got %+v", ss)
|
|
}
|
|
if all, _ := m.ListByOwner(ctx, "o1"); len(all) != 3 {
|
|
t.Errorf("ListByOwner = %d, want 3", len(all))
|
|
}
|
|
// Versions: newest-first, fetchable by id.
|
|
m.AppendVersion(ctx, SkillVersion{ID: "v1", SkillID: "a", Version: "1.0.0"})
|
|
m.AppendVersion(ctx, SkillVersion{ID: "v2", SkillID: "a", Version: "1.1.0"})
|
|
vs, _ := m.ListVersionsBySkill(ctx, "a", 10)
|
|
if len(vs) != 2 || vs[0].ID != "v2" {
|
|
t.Errorf("versions newest-first wrong: %+v", vs)
|
|
}
|
|
if got, err := m.GetVersionByID(ctx, "v1"); err != nil || got.Version != "1.0.0" {
|
|
t.Errorf("GetVersionByID: %v %+v", err, got)
|
|
}
|
|
}
|