3.1: Create AgentConfig dataclass and multi-agent config

New AgentConfig dataclass in config.py with fields:
- name, display_name, personality_file, model
- tools (whitelist), skills (filter), memory_scope

Loaded from config.yaml under agents: key. Defaults to single
agent for backward compatibility when section is omitted.

config.yaml now includes 4 agent configs: default, writer,
researcher, ops — each with appropriate tool/skill whitelists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cora-start
PeninsulaInd 2026-02-17 10:06:12 -06:00
parent d05b589651
commit 6c2c28e9b0
2 changed files with 57 additions and 0 deletions

View File

@ -66,6 +66,19 @@ class EmailConfig:
enabled: bool = False enabled: bool = False
@dataclass
class AgentConfig:
"""Per-agent configuration for multi-agent support."""
name: str = "default"
display_name: str = "CheddahBot"
personality_file: str = "" # path to SOUL-like .md file, empty = default
model: str = "" # model override, empty = use global chat_model
tools: list[str] | None = None # tool name whitelist, None = all
skills: list[str] | None = None # skill name filter, None = auto
memory_scope: str = "" # memory namespace, empty = shared
@dataclass @dataclass
class Config: class Config:
chat_model: str = "openai/gpt-4o-mini" chat_model: str = "openai/gpt-4o-mini"
@ -81,6 +94,7 @@ class Config:
clickup: ClickUpConfig = field(default_factory=ClickUpConfig) clickup: ClickUpConfig = field(default_factory=ClickUpConfig)
press_advantage: PressAdvantageConfig = field(default_factory=PressAdvantageConfig) press_advantage: PressAdvantageConfig = field(default_factory=PressAdvantageConfig)
email: EmailConfig = field(default_factory=EmailConfig) email: EmailConfig = field(default_factory=EmailConfig)
agents: list[AgentConfig] = field(default_factory=lambda: [AgentConfig()])
# Derived paths # Derived paths
root_dir: Path = field(default_factory=lambda: ROOT_DIR) root_dir: Path = field(default_factory=lambda: ROOT_DIR)
@ -128,6 +142,20 @@ def load_config() -> Config:
if hasattr(cfg.email, k): if hasattr(cfg.email, k):
setattr(cfg.email, k, v) setattr(cfg.email, k, v)
# Multi-agent configs
if "agents" in data and isinstance(data["agents"], list):
cfg.agents = []
for agent_data in data["agents"]:
if isinstance(agent_data, dict):
ac = AgentConfig()
for k, v in agent_data.items():
if hasattr(ac, k):
setattr(ac, k, v)
cfg.agents.append(ac)
# Ensure at least one agent
if not cfg.agents:
cfg.agents = [AgentConfig()]
# Env var overrides (CHEDDAH_ prefix) # Env var overrides (CHEDDAH_ prefix)
cfg.openrouter_api_key = os.getenv("OPENROUTER_API_KEY", "") cfg.openrouter_api_key = os.getenv("OPENROUTER_API_KEY", "")
if cm := os.getenv("CHEDDAH_CHAT_MODEL"): if cm := os.getenv("CHEDDAH_CHAT_MODEL"):

View File

@ -56,3 +56,32 @@ clickup:
company_name: "Client" company_name: "Client"
target_url: "IMSURL" target_url: "IMSURL"
branded_url: "SocialURL" branded_url: "SocialURL"
# Multi-agent configuration
# Each agent gets its own personality, tool whitelist, and memory scope.
# The first agent is the default. Omit this section for single-agent mode.
agents:
- name: default
display_name: CheddahBot
# tools: null = all tools, [] = no tools
# skills: null = auto (all skills matching agent name)
# memory_scope: "" = shared memory
- name: writer
display_name: Writing Agent
personality_file: "" # future: identity/WRITER.md
skills: [press-release-writer, press-release-schema]
tools: [write_press_releases, submit_press_release, delegate_task, remember, search_memory]
memory_scope: "" # shares memory with default
- name: researcher
display_name: Research Agent
personality_file: "" # future: identity/RESEARCHER.md
tools: [web_search, web_fetch, delegate_task, remember, search_memory]
memory_scope: ""
- name: ops
display_name: Ops Agent
personality_file: "" # future: identity/OPS.md
tools: [run_command, delegate_task, list_files, read_file, remember, search_memory]
memory_scope: ""