agents

package
v0.1.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var BuiltinCommands = map[string]bool{
	"quit": true, "exit": true, "help": true, "clear": true,
	"sessions": true, "session": true, "model": true, "config": true,
	"setup": true, "update": true, "copy": true, "research": true,
	"swarm": true, "search": true,
}

BuiltinCommands is the set of slash command names reserved by the TUI. Agent commands and skills must not use these names.

View Source
var BuiltinNames = []string{"explorer", "reviewer"}

BuiltinNames lists the names of all bundled specialist agents.

View Source
var ErrToolNotPersistable = errors.New("tool cannot be persisted as always-allow")

ErrToolNotPersistable is returned when a tool cannot be persisted to a per-agent always-allow list. Currently this only fires for high-risk tools (see isHighRiskTool); the runtime check enforces the same gate even if a hand-edited config.yaml manages to bypass this function.

Functions

func AgentsAttachingSkill

func AgentsAttachingSkill(agentsDir, skillName string) ([]string, error)

AgentsAttachingSkill returns the names of agents whose _attached.yaml manifest references the given skill. The result is sorted alphabetically and is always a non-nil slice (empty slice when no agents attach the skill), so JSON responses render as "[]" rather than "null".

Errors from reading a single agent's manifest are skipped — a corrupt manifest for agent A should not hide attachments in agent B. Only a filesystem error opening agentsDir itself is returned.

func AppendAlwaysAllowTool added in v0.1.9

func AppendAlwaysAllowTool(agentsDir, agentName, tool string) error

AppendAlwaysAllowTool adds a tool name to the agent's permissions.always_allow_tools list in <agentsDir>/<agentName>/config.yaml.

Concurrency: serialized via flock on <agentDir>/.config.lock. The lock file is persistent (never deleted) — see schedule.go for rationale.

First-write: creates the agent directory and an otherwise-empty config.yaml.

Idempotent: duplicate calls are no-ops (the list is deduplicated and sorted).

Defense-in-depth: high-risk tools (isHighRiskTool) are rejected with ErrToolNotPersistable. The runtime check at internal/agent/loop.go checkPermissionAndApproval also refuses to honor such entries.

func AtomicWrite

func AtomicWrite(path string, data []byte) error

AtomicWrite writes data to path via temp file + rename.

func AttachSkill

func AttachSkill(agentsDir, agentName, skillName string) error

AttachSkill adds a skill name to an agent's attached-skill manifest.

func DeleteAgentCommand

func DeleteAgentCommand(agentsDir, agentName, cmdName string) error

DeleteAgentCommand removes a single command file.

func DeleteAgentDir

func DeleteAgentDir(agentsDir, name string) error

DeleteAgentDir removes the entire agent directory.

func DeleteAgentSkill

func DeleteAgentSkill(agentsDir, agentName, skillName string) error

DeleteAgentSkill removes a single skill directory.

func DeleteAttachedSkills

func DeleteAttachedSkills(agentsDir, agentName string) error

DeleteAttachedSkills removes the _attached.yaml manifest.

func DetachSkill

func DetachSkill(agentsDir, agentName, skillName string) error

DetachSkill removes a skill name from an agent's attached-skill manifest.

func DetectTriggerConflicts added in v0.0.98

func DetectTriggerConflicts(agentsDir, agentName string, schedules []ScheduleRef) []string

DetectTriggerConflicts returns human-readable warnings when an agent has BOTH a non-zero heartbeat.every AND at least one enabled schedule targeting it. The returned slice is nil when there is no conflict.

Contract:

  • agentsDir + agentName are the usual LoadAgent inputs.
  • schedules is the full list (e.g. schedule.Manager.List()) — entries with a non-matching Agent name or Enabled=false are ignored.
  • Missing/malformed agent files produce no warnings (empty slice, nil err). Callers must not panic. Visibility only, never a hard error.

func EnsureBuiltins

func EnsureBuiltins(agentsDir, currentVersion string) error

EnsureBuiltins syncs embedded agent definitions to agentsDir/_builtin/. Skips if the on-disk version matches currentVersion (idempotent). Uses write-to-temp-then-rename for atomicity: .version is written last.

func HighRiskTools added in v0.1.9

func HighRiskTools() []string

HighRiskTools returns a copy of the tools that cannot be persisted to a per-agent always-allow list. Exposed for cross-package consistency tests.

func IsBuiltinAgent

func IsBuiltinAgent(name string) bool

IsBuiltinAgent returns true if the given name matches a bundled agent.

func IsToolAlwaysAllowable added in v0.1.9

func IsToolAlwaysAllowable(toolName string) bool

IsToolAlwaysAllowable reports whether a tool may be persisted to an agent's permissions.always_allow_tools list. High-risk tools that require fresh human re-approval each call (publish_to_web, generate_image, edit_image) are not persistable. The runtime enforces the same gate independently — see internal/agent/loop.go checkPermissionAndApproval — so a hand-edited config.yaml cannot bypass the prompt.

func LoadGlobalSkills

func LoadGlobalSkills(shannonDir string) ([]*skills.Skill, error)

LoadGlobalSkills loads skills from the global skills directory (~/.shannon/skills/). Only installed (global) skills are returned — bundled skills must be explicitly installed first (except those auto-installed by EnsureBuiltinSkills).

func MaterializeBuiltin

func MaterializeBuiltin(agentsDir, name string) error

MaterializeBuiltin copies all definition files from _builtin/<name>/ to <name>/ in agentsDir. Used before CRUD writes to ensure the user-override directory is self-contained. MEMORY.md is NOT copied (it already lives at the top-level runtime dir). Returns nil if the builtin dir doesn't exist.

func ParseAgentMention

func ParseAgentMention(msg string) (string, string)

func ReadAttachedSkills

func ReadAttachedSkills(agentsDir, agentName string) ([]string, error)

ReadAttachedSkills reads an agent's attached-skill manifest. Returns (nil, nil) when the manifest does not exist.

func RemoveAlwaysAllowTool added in v0.1.9

func RemoveAlwaysAllowTool(agentsDir, agentName, tool string) error

RemoveAlwaysAllowTool removes a tool name from the agent's permissions.always_allow_tools list. No-op if the tool is not present, the list is empty, or config.yaml does not exist.

If the resulting list is empty AND no other permissions sub-fields are set, the permissions: top-level key is dropped to keep YAML clean.

func SetAttachedSkills

func SetAttachedSkills(agentsDir, agentName string, names []string) error

SetAttachedSkills replaces an agent's attached-skill manifest with a normalized set. Names are deduplicated and sorted; an empty set removes the manifest.

func ValidateAgentName

func ValidateAgentName(name string) error

func ValidateCommandName

func ValidateCommandName(name string) error

ValidateCommandName checks that a command/skill name is valid and doesn't collide with built-in slash commands.

func ValidateToolsFilter

func ValidateToolsFilter(f *AgentToolsFilter) error

ValidateToolsFilter checks that allow and deny are not both set.

func WriteAgentCommand

func WriteAgentCommand(agentsDir, agentName, cmdName, content string) error

WriteAgentCommand writes a single command file.

func WriteAgentConfig

func WriteAgentConfig(agentsDir, name string, cfg *AgentConfigAPI) error

WriteAgentConfig writes config.yaml from the API shape.

Serializes on <agentDir>/.config.lock to prevent lost-update races against AppendAlwaysAllowTool / RemoveAlwaysAllowTool (which read-modify-write the same file under the same lock).

func WriteAgentMemory

func WriteAgentMemory(agentsDir, name, memory string) error

WriteAgentMemory writes MEMORY.md atomically.

func WriteAgentPrompt

func WriteAgentPrompt(agentsDir, name, prompt string) error

WriteAgentPrompt writes AGENT.md atomically.

func WriteAgentSkill

func WriteAgentSkill(agentsDir, agentName string, skill *skills.Skill) error

WriteAgentSkill writes a skill as a SKILL.md file with YAML frontmatter. Uses proper YAML marshalling to handle special characters in values.

Directory is keyed by Slug (URL-safe identifier). Falls back to Name for skills that predate the Name/Slug split where Slug is unset.

func WriteAttachedSkills

func WriteAttachedSkills(agentsDir, agentName string, names []string) error

WriteAttachedSkills writes the _attached.yaml manifest for an agent.

Types

type Agent

type Agent struct {
	Name     string
	Prompt   string
	Memory   string
	Config   *AgentConfig      // nil = inherit everything (backwards-compatible)
	Commands map[string]string // agent-scoped slash commands (name → content)
	Skills   []*skills.Skill   // agent-scoped skills (prompt, tool_chain, sub_agent)
}

Agent represents a loaded agent definition.

func LoadAgent

func LoadAgent(agentsDir, name string) (*Agent, error)

func (*Agent) ToAPI

func (a *Agent) ToAPI() *AgentAPI

ToAPI converts a loaded Agent to the API response shape.

type AgentAPI

type AgentAPI struct {
	Name       string             `json:"name"`
	Prompt     string             `json:"prompt"`
	Memory     *string            `json:"memory"`             // null if no MEMORY.md
	Config     *AgentConfigAPI    `json:"config"`             // null if no config.yaml
	Commands   map[string]string  `json:"commands"`           // null if no commands
	Skills     []skills.SkillMeta `json:"skills"`             // null if no skills
	Builtin    bool               `json:"builtin"`            // true if agent is a bundled builtin
	Overridden bool               `json:"overridden"`         // true if builtin has user override
	Warnings   []string           `json:"warnings,omitempty"` // non-fatal config advisories (e.g. heartbeat⊕schedule double-fire)
}

AgentAPI is the JSON representation of an agent for the HTTP API.

type AgentConfig

type AgentConfig struct {
	CWD         string                  `yaml:"cwd"`
	MCPServers  *AgentMCPConfig         `yaml:"-"` // parsed manually for _inherit
	Tools       *AgentToolsFilter       `yaml:"tools"`
	Agent       *AgentModelConfig       `yaml:"agent"`
	AutoApprove *bool                   `yaml:"auto_approve"`
	Permissions *AgentPermissionsConfig `yaml:"permissions,omitempty"`
	Watch       []WatchEntry            `yaml:"watch,omitempty"`
	Heartbeat   *HeartbeatConfig        `yaml:"heartbeat,omitempty"`
}

AgentConfig is the per-agent config overlay loaded from config.yaml.

type AgentConfigAPI

type AgentConfigAPI struct {
	CWD         string                  `json:"cwd,omitempty"`
	Tools       *AgentToolsFilter       `json:"tools,omitempty"`
	MCPServers  *AgentMCPConfigAPI      `json:"mcp_servers,omitempty"`
	Agent       *AgentModelConfig       `json:"agent,omitempty"`
	Permissions *AgentPermissionsConfig `json:"permissions,omitempty"`
	Watch       []WatchEntry            `json:"watch,omitempty"`
	Heartbeat   *HeartbeatConfig        `json:"heartbeat,omitempty"`
}

AgentConfigAPI is the JSON representation of agent config.

type AgentCreateRequest

type AgentCreateRequest struct {
	Name     string            `json:"name"`
	Prompt   string            `json:"prompt"`
	Memory   *string           `json:"memory,omitempty"`
	Config   *AgentConfigAPI   `json:"config,omitempty"`
	Commands map[string]string `json:"commands,omitempty"`
	Skills   []*skills.Skill   `json:"skills,omitempty"`
}

AgentCreateRequest parses a POST /agents request body.

func (*AgentCreateRequest) Validate

func (r *AgentCreateRequest) Validate() error

Validate checks required fields and runs all validators.

type AgentEntry

type AgentEntry struct {
	Name     string `json:"name"`
	Builtin  bool   `json:"builtin"`  // loaded from _builtin
	Override bool   `json:"override"` // user-defined agent overrides a builtin
}

AgentEntry represents an agent in the listing with source metadata.

func ListAgents

func ListAgents(agentsDir string) ([]AgentEntry, error)

type AgentMCPConfig

type AgentMCPConfig struct {
	Inherit bool
	Servers map[string]AgentMCPServerRef
}

AgentMCPConfig holds MCP server configs with an optional inherit flag. When Inherit is false (default), only the servers listed here are used. When Inherit is true, these servers are merged on top of the global set. This struct is populated programmatically by parseAgentConfig, not by direct YAML unmarshaling.

type AgentMCPConfigAPI

type AgentMCPConfigAPI struct {
	Inherit bool                         `json:"inherit"`
	Servers map[string]AgentMCPServerRef `json:"servers,omitempty"`
}

AgentMCPConfigAPI is the JSON-friendly MCP config.

type AgentMCPServerRef

type AgentMCPServerRef struct {
	Command   string            `yaml:"command" json:"command,omitempty"`
	Args      []string          `yaml:"args,omitempty" json:"args,omitempty"`
	Env       map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
	Type      string            `yaml:"type,omitempty" json:"type,omitempty"`
	URL       string            `yaml:"url,omitempty" json:"url,omitempty"`
	Disabled  bool              `yaml:"disabled,omitempty" json:"disabled,omitempty"`
	Context   string            `yaml:"context,omitempty" json:"context,omitempty"`
	KeepAlive bool              `yaml:"keep_alive,omitempty" json:"keep_alive,omitempty"`
}

AgentMCPServerRef mirrors the fields needed for per-agent MCP server config. We keep it simple — the full MCPServerConfig is resolved at merge time.

type AgentModelConfig

type AgentModelConfig struct {
	Model         *string  `yaml:"model" json:"model,omitempty"`
	MaxIterations *int     `yaml:"max_iterations" json:"max_iterations,omitempty"`
	Temperature   *float64 `yaml:"temperature" json:"temperature,omitempty"`
	MaxTokens     *int     `yaml:"max_tokens" json:"max_tokens,omitempty"`
	ContextWindow *int     `yaml:"context_window" json:"context_window,omitempty"`

	IdleSoftTimeoutSecs *int `yaml:"idle_soft_timeout_secs" json:"idle_soft_timeout_secs,omitempty"`
	IdleHardTimeoutSecs *int `yaml:"idle_hard_timeout_secs" json:"idle_hard_timeout_secs,omitempty"`
}

AgentModelConfig holds per-agent model/iteration overrides.

type AgentPermissionsConfig added in v0.1.9

type AgentPermissionsConfig struct {
	// AlwaysAllowTools lists tool names whose approval prompts the user has
	// chosen to skip permanently for this agent. High-risk tools (see
	// agent.DisallowsAutoApproval) are never honored here even if present —
	// the runtime check enforces defense-in-depth.
	AlwaysAllowTools []string `yaml:"always_allow_tools,omitempty" json:"always_allow_tools,omitempty"`
}

AgentPermissionsConfig carries per-agent permission overrides. Distinct from tools.allow (which is a schema filter — controls what the LLM can see): these entries are approval bypasses applied at execution time.

func (*AgentPermissionsConfig) Clone added in v0.1.9

Clone returns a deep copy. ToAPI uses this so the API response cannot share slice backing arrays with the in-memory Agent — an HTTP handler that mutates the returned slice would otherwise leak into the next call's response. Returns nil if p is nil.

func (*AgentPermissionsConfig) IsEmpty added in v0.1.9

func (p *AgentPermissionsConfig) IsEmpty() bool

IsEmpty reports whether the struct carries no effective configuration. WriteAgentConfig uses this to decide whether to emit the permissions: block at all, avoiding "permissions: {}" noise in YAML. Update this whenever a new field is added to AgentPermissionsConfig.

type AgentToolsFilter

type AgentToolsFilter struct {
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty"`
	Deny  []string `yaml:"deny,omitempty" json:"deny,omitempty"`
}

AgentToolsFilter controls which local tools an agent can access. If Allow is non-empty, only those tools are available. If Deny is non-empty, all tools except those are available. If both are empty, all tools are available (backwards-compatible).

type AgentUpdateRequest

type AgentUpdateRequest struct {
	Prompt   *string           `json:"prompt,omitempty"`
	Memory   json.RawMessage   `json:"memory,omitempty"` // string or null
	Config   json.RawMessage   `json:"config,omitempty"` // object or null
	Commands map[string]string `json:"commands,omitempty"`
	Skills   []*skills.Skill   `json:"skills,omitempty"`
}

AgentUpdateRequest is a partial update — only non-nil fields are applied.

type HeartbeatConfig

type HeartbeatConfig struct {
	Every           string `yaml:"every" json:"every"`
	ActiveHours     string `yaml:"active_hours,omitempty" json:"active_hours,omitempty"`
	Model           string `yaml:"model,omitempty" json:"model,omitempty"`
	IsolatedSession *bool  `yaml:"isolated_session,omitempty" json:"isolated_session,omitempty"`
}

HeartbeatConfig configures periodic heartbeat checks for an agent. IsolatedSession defaults to true (nil = true). Use pointer for YAML omit-means-default.

func (*HeartbeatConfig) IsIsolatedSession

func (h *HeartbeatConfig) IsIsolatedSession() bool

IsIsolatedSession returns the effective value (default true).

type ScheduleRef added in v0.0.98

type ScheduleRef struct {
	ID      string
	Agent   string
	Enabled bool
}

ScheduleRef is the minimal view of a schedule needed by DetectTriggerConflicts. It intentionally mirrors the subset of internal/schedule.Schedule that the warning logic consumes, so this package does not import internal/schedule (and so tests can construct a slice without a real schedule store).

type WatchEntry

type WatchEntry struct {
	Path string `yaml:"path" json:"path"`
	Glob string `yaml:"glob,omitempty" json:"glob,omitempty"`
}

WatchEntry defines a single file system watch path for an agent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL