pub struct SubAgentDef {
pub name: String,
pub description: String,
pub model: Option<ModelSpec>,
pub tools: ToolPolicy,
pub disallowed_tools: Vec<String>,
pub permissions: SubAgentPermissions,
pub skills: SkillFilter,
pub system_prompt: String,
pub hooks: SubagentHooks,
pub memory: Option<MemoryScope>,
pub source: Option<String>,
pub file_path: Option<PathBuf>,
}Expand description
Parsed and validated sub-agent definition loaded from a .md file.
A SubAgentDef is the runtime representation of a sub-agent’s configuration.
Definitions are loaded from Markdown files with YAML (or deprecated TOML) frontmatter
and a system prompt body.
§File format
---
name: code-reviewer
description: Reviews pull requests for correctness and style
model: claude-sonnet-4
tools:
allow:
- shell
- Read
permissions:
max_turns: 15
timeout_secs: 300
skills:
include:
- "git-*"
---
You are an expert code reviewer. Focus on correctness, style, and security.§Errors
SubAgentDef::parse returns SubAgentError::Parse if the frontmatter is malformed
and SubAgentError::Invalid if semantic constraints are violated.
Fields§
§name: StringUnique identifier for this agent (ASCII alphanumeric + hyphen/underscore, max 64 chars).
description: StringHuman-readable description shown in /agent list output.
model: Option<ModelSpec>Override the default LLM model for this agent. None inherits the parent’s provider.
tools: ToolPolicyBase tool access policy derived from tools.allow or tools.deny in frontmatter.
disallowed_tools: Vec<String>Additional denylist applied after the base tools policy.
Populated from tools.except in YAML frontmatter. Deny wins: tools listed
here are blocked even when they appear in tools.allow.
§Serde asymmetry (IMP-CRIT-04)
Deserialization reads this field from the nested tools.except key in YAML/TOML
frontmatter. Serialization (via #[derive(Serialize)]) writes it as a flat
top-level disallowed_tools key — not under tools. Round-trip serialization
is therefore not supported: a serialized SubAgentDef cannot be parsed back
as a valid frontmatter file. This is intentional for the current MVP but must
be addressed before v1.0.0 (see GitHub issue filed under IMP-CRIT-04).
permissions: SubAgentPermissionsRuntime permission settings: secrets, turn limits, background mode, timeouts.
skills: SkillFilterGlob patterns controlling which skills are visible to this agent.
system_prompt: StringThe markdown body of the definition file, used as the agent’s system prompt.
hooks: SubagentHooksPer-agent hooks (PreToolUse / PostToolUse) from frontmatter.
Hooks are only honored for project-level and CLI-level definitions. User-level definitions (~/.zeph/agents/) have hooks stripped on load.
memory: Option<MemoryScope>Persistent memory scope. When set, a memory directory is created at spawn time
and MEMORY.md content is injected into the system prompt.
source: Option<String>Scope label and filename of the definition file (populated by load / load_all).
Stored as "<scope>/<filename>" (e.g., "project/my-agent.md").
The full absolute path is intentionally not stored to avoid leaking local
filesystem layout in diagnostics and /agent list output.
file_path: Option<PathBuf>Full filesystem path of the definition file (populated by load_with_boundary).
Used internally by edit/delete operations. Not included in diagnostics output.
Implementations§
Source§impl SubAgentDef
impl SubAgentDef
Sourcepub fn parse(content: &str) -> Result<Self, SubAgentError>
pub fn parse(content: &str) -> Result<Self, SubAgentError>
Parse a sub-agent definition from its frontmatter+markdown content.
The primary format uses YAML frontmatter delimited by ---:
---
name: my-agent
description: Does something useful
model: claude-sonnet-4-20250514
tools:
allow:
- shell
permissions:
max_turns: 10
skills:
include:
- "git-*"
---
You are a helpful agent.TOML frontmatter (+++) is supported as a deprecated fallback and will emit a
tracing::warn! message. It will be removed in v1.0.0.
§Errors
Returns SubAgentError::Parse if the frontmatter delimiters are missing or the
content is malformed, and SubAgentError::Invalid if required fields are empty or
tools.allow and tools.deny are both specified.
Sourcepub fn load(path: &Path) -> Result<Self, SubAgentError>
pub fn load(path: &Path) -> Result<Self, SubAgentError>
Load a single definition from a .md file.
When boundary is provided, the file’s canonical path must start with
boundary — this rejects symlinks that escape the allowed directory.
§Errors
Returns SubAgentError::Parse if the file cannot be read, exceeds 256 KiB,
escapes the boundary via symlink, or fails to parse.
Sourcepub fn load_all(paths: &[PathBuf]) -> Result<Vec<Self>, SubAgentError>
pub fn load_all(paths: &[PathBuf]) -> Result<Vec<Self>, SubAgentError>
Load all definitions from a list of paths (files or directories).
Paths are processed in order; when two entries share the same agent
name, the first one wins (higher-priority path takes precedence).
Non-existent directories are silently skipped.
For directory entries from user/extra dirs: parse errors are warned and skipped.
For CLI file entries (is_cli_source = true): parse errors are hard failures.
§Errors
Returns SubAgentError if a CLI-sourced .md file fails to parse.
Sourcepub fn load_all_with_sources(
ordered_paths: &[PathBuf],
cli_agents: &[PathBuf],
config_user_dir: Option<&PathBuf>,
extra_dirs: &[PathBuf],
) -> Result<Vec<Self>, SubAgentError>
pub fn load_all_with_sources( ordered_paths: &[PathBuf], cli_agents: &[PathBuf], config_user_dir: Option<&PathBuf>, extra_dirs: &[PathBuf], ) -> Result<Vec<Self>, SubAgentError>
Load all definitions with scope context for source tracking and security checks.
cli_agents — CLI paths (hard errors on parse failure, no boundary check).
config_user_dir — optional user-level dir override.
extra_dirs — extra dirs from config.
§Errors
Returns SubAgentError if a CLI-sourced .md file fails to parse.
Source§impl SubAgentDef
impl SubAgentDef
Sourcepub fn serialize_to_markdown(&self) -> String
pub fn serialize_to_markdown(&self) -> String
Serialize the definition to YAML frontmatter + markdown body.
Uses WritableRawDef (with correct tools.except nesting) to avoid the
IMP-CRIT-04 serde asymmetry. The result can be re-parsed with SubAgentDef::parse.
§Panics
Panics if serde_norway serialization fails (should not happen for valid structs).
Sourcepub fn save_atomic(&self, dir: &Path) -> Result<PathBuf, SubAgentError>
pub fn save_atomic(&self, dir: &Path) -> Result<PathBuf, SubAgentError>
Write definition to {dir}/{self.name}.md atomically using temp+rename.
Creates parent directories if needed. Uses tempfile::NamedTempFile in the same
directory for automatic cleanup on failure.
§Errors
Returns SubAgentError::Invalid if the agent name fails validation (prevents path traversal).
Returns SubAgentError::Io if directory creation, write, or rename fails.
Sourcepub fn delete_file(path: &Path) -> Result<(), SubAgentError>
pub fn delete_file(path: &Path) -> Result<(), SubAgentError>
Delete a definition file from disk.
§Errors
Returns SubAgentError::Io if the file does not exist or cannot be removed.
Trait Implementations§
Source§impl Clone for SubAgentDef
impl Clone for SubAgentDef
Source§fn clone(&self) -> SubAgentDef
fn clone(&self) -> SubAgentDef
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more