Skip to main content

zeph_config/
subagent.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use serde::{Deserialize, Serialize};
5
6// ── PermissionMode ─────────────────────────────────────────────────────────
7
8/// Controls tool execution and prompt interactivity for a sub-agent.
9///
10/// For sub-agents (non-interactive), `Default`, `AcceptEdits`, `DontAsk`, and
11/// `BypassPermissions` are functionally equivalent — sub-agents never prompt the
12/// user. The meaningful differentiator is `Plan` mode, which suppresses all tool
13/// execution and returns only the plan text.
14#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
15#[serde(rename_all = "snake_case")]
16pub enum PermissionMode {
17    /// Standard behavior — prompt for each action (sub-agents auto-approve).
18    #[default]
19    Default,
20    /// Auto-accept file edits without prompting.
21    AcceptEdits,
22    /// Auto-approve all tool calls without prompting.
23    DontAsk,
24    /// Unrestricted tool access; emits a warning when loaded.
25    BypassPermissions,
26    /// Read-only planning: tools are visible in the catalog but execution is blocked.
27    Plan,
28}
29
30// ── MemoryScope ────────────────────────────────────────────────────────────
31
32/// Persistence scope for sub-agent memory files.
33///
34/// Determines where the agent's `MEMORY.md` and topic files are stored across sessions.
35#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
36#[serde(rename_all = "snake_case")]
37pub enum MemoryScope {
38    /// User-level: `~/.zeph/agent-memory/<name>/`.
39    User,
40    /// Project-level: `.zeph/agent-memory/<name>/`.
41    Project,
42    /// Local-only: `.zeph/agent-memory-local/<name>/`.
43    Local,
44}
45
46// ── ToolPolicy ─────────────────────────────────────────────────────────────
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum ToolPolicy {
51    AllowList(Vec<String>),
52    DenyList(Vec<String>),
53    InheritAll,
54}
55
56// ── SkillFilter ────────────────────────────────────────────────────────────
57
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
59pub struct SkillFilter {
60    pub include: Vec<String>,
61    pub exclude: Vec<String>,
62}
63
64impl SkillFilter {
65    #[must_use]
66    pub fn is_empty(&self) -> bool {
67        self.include.is_empty() && self.exclude.is_empty()
68    }
69}
70
71// ── HookDef / HookType / HookMatcher / SubagentHooks ──────────────────────
72
73/// The type of hook — currently only shell command hooks are supported.
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
75#[serde(rename_all = "snake_case")]
76pub enum HookType {
77    Command,
78}
79
80fn default_hook_timeout() -> u64 {
81    30
82}
83
84/// A single hook definition.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct HookDef {
87    #[serde(rename = "type")]
88    pub hook_type: HookType,
89    pub command: String,
90    #[serde(default = "default_hook_timeout")]
91    pub timeout_secs: u64,
92    /// When `true`, a non-zero exit code or timeout causes the calling operation to fail.
93    /// When `false` (default), errors are logged but execution continues.
94    #[serde(default)]
95    pub fail_closed: bool,
96}
97
98/// Tool-name matcher with associated hooks.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct HookMatcher {
101    pub matcher: String,
102    pub hooks: Vec<HookDef>,
103}
104
105/// Per-agent frontmatter hook collections (`PreToolUse` / `PostToolUse`).
106#[derive(Debug, Clone, Default, Serialize, Deserialize)]
107#[serde(rename_all = "PascalCase")]
108pub struct SubagentHooks {
109    #[serde(default)]
110    pub pre_tool_use: Vec<HookMatcher>,
111    #[serde(default)]
112    pub post_tool_use: Vec<HookMatcher>,
113}
114
115impl SubagentHooks {
116    #[must_use]
117    pub fn is_empty(&self) -> bool {
118        self.pre_tool_use.is_empty() && self.post_tool_use.is_empty()
119    }
120}