87 lines
2.9 KiB
Go
87 lines
2.9 KiB
Go
package skills
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
|
|
)
|
|
|
|
// Workflow is a Skill expanded into its executable form: the base system prompt
|
|
// plus the ordered steps, references, and guardrails, ready to be handed to the
|
|
// loop/agent executor (M2). The Runner only assembles workflows; it does not
|
|
// execute commands.
|
|
type Workflow struct {
|
|
Name string
|
|
Description string
|
|
Prompt string
|
|
Steps []sdk.Step
|
|
Refs []sdk.Ref
|
|
Guardrails []sdk.Guardrail
|
|
}
|
|
|
|
// Runner expands skills into workflows. It is the M5 placeholder for the
|
|
// skill-execution wrapper: expansion (the real deliverable) is wired now, while
|
|
// Run awaits M12/loop integration.
|
|
type Runner struct {
|
|
reg *registry.Registry
|
|
log *slog.Logger
|
|
}
|
|
|
|
// NewRunner builds a Runner over the given registry, used by ExpandByName to
|
|
// look up registered skills. log may be nil.
|
|
func NewRunner(reg *registry.Registry, log *slog.Logger) *Runner {
|
|
return &Runner{reg: reg, log: sourcedLog(log)}
|
|
}
|
|
|
|
// Expand converts s into a Workflow, copying the step, ref, and guardrail slices
|
|
// so the caller owns the result. It returns ErrInvalidSkill when s is nil or has
|
|
// a blank name.
|
|
func (r *Runner) Expand(ctx context.Context, s sdk.Skill) (Workflow, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
return Workflow{}, fmt.Errorf("skills: runner: expand: %w", err)
|
|
}
|
|
if s == nil || strings.TrimSpace(s.Name()) == "" {
|
|
return Workflow{}, fmt.Errorf("skills: runner: expand: %w", ErrInvalidSkill)
|
|
}
|
|
wf := Workflow{
|
|
Name: s.Name(),
|
|
Description: s.Description(),
|
|
Prompt: s.Prompt(),
|
|
Steps: cloneSteps(s.Steps()),
|
|
Refs: cloneRefs(s.Refs()),
|
|
Guardrails: cloneGuardrails(s.Guardrails()),
|
|
}
|
|
r.log.Info("skill expanded", "skill", wf.Name)
|
|
return wf, nil
|
|
}
|
|
|
|
// ExpandByName looks up a skill in the registry by name and expands it. It
|
|
// returns ErrNilRegistrar when the runner has no registry, the registry's error
|
|
// (registry.ErrNotFound) when the name is absent, and ErrNotFound when the
|
|
// registered value is not a Skill.
|
|
func (r *Runner) ExpandByName(ctx context.Context, name string) (Workflow, error) {
|
|
if r.reg == nil {
|
|
return Workflow{}, fmt.Errorf("skills: runner: expand %q: %w", name, ErrNilRegistrar)
|
|
}
|
|
cap, err := r.reg.Get(name)
|
|
if err != nil {
|
|
return Workflow{}, fmt.Errorf("skills: runner: expand %q: %w", name, err)
|
|
}
|
|
skill, ok := cap.Value.(sdk.Skill)
|
|
if !ok {
|
|
return Workflow{}, fmt.Errorf("skills: runner: expand %q: %w", name, ErrNotFound)
|
|
}
|
|
return r.Expand(ctx, skill)
|
|
}
|
|
|
|
// Run is a placeholder for executing a registered skill end to end. Execution
|
|
// lands in the loop/agent executor (M2) and the full wiring (M12); it always
|
|
// returns ErrRunPending.
|
|
func (r *Runner) Run(_ context.Context, _ string, _ any) (any, error) {
|
|
return nil, ErrRunPending
|
|
}
|