# -*- coding: utf-8 -*- """Per-agent run-scoped workspace configuration helpers.""" from dataclasses import dataclass, field from pathlib import Path from typing import Any, Dict, List, Optional import yaml @dataclass(frozen=True) class AgentWorkspaceConfig: """Structured agent config loaded from runs//agents//agent.yaml.""" values: Dict[str, Any] = field(default_factory=dict) @property def prompt_files(self) -> Optional[List[str]]: raw = self.values.get("prompt_files") if not isinstance(raw, list): return None files = [ str(item).strip() for item in raw if isinstance(item, str) and str(item).strip() ] return files or None @property def enabled_skills(self) -> List[str]: return _normalized_string_list(self.values.get("enabled_skills")) @property def disabled_skills(self) -> List[str]: return _normalized_string_list(self.values.get("disabled_skills")) @property def active_tool_groups(self) -> Optional[List[str]]: groups = _normalized_string_list(self.values.get("active_tool_groups")) return groups or None @property def disabled_tool_groups(self) -> List[str]: return _normalized_string_list(self.values.get("disabled_tool_groups")) def get(self, key: str, default: Any = None) -> Any: return self.values.get(key, default) def _normalized_string_list(raw: Any) -> List[str]: if not isinstance(raw, list): return [] seen: List[str] = [] for item in raw: if not isinstance(item, str): continue value = item.strip() if value and value not in seen: seen.append(value) return seen def load_agent_workspace_config(path: Path) -> AgentWorkspaceConfig: """Load agent.yaml if present.""" if not path.exists() or not path.is_file(): return AgentWorkspaceConfig() raw = path.read_text(encoding="utf-8").strip() if not raw: return AgentWorkspaceConfig() parsed = yaml.safe_load(raw) or {} if not isinstance(parsed, dict): parsed = {} return AgentWorkspaceConfig(values=parsed)