Configuration¶
Configuration loading and dataclass definitions.
YAML configuration loading with environment variable substitution.
Loads the application config from a YAML file (default: ~/.agent-queue/config.yaml), substitutes ${ENV_VAR} references with environment variable values, and maps the result into typed dataclass instances. Also supports loading a .env file from the same directory as the config file for local development.
The config is loaded once at startup and passed to all major components (orchestrator, Discord bot, scheduler, adapters). Individual sections are represented by dedicated dataclasses so each component can accept only the config it needs.
See specs/config.md for the full specification of all configuration fields.
Attributes¶
HOT_RELOADABLE_SECTIONS
module-attribute
¶
HOT_RELOADABLE_SECTIONS = {'scheduling', 'monitoring', 'hook_engine', 'archive', 'llm_logging', 'pause_retry', 'agents_config', 'auto_task', 'logging', 'agent_profiles', 'rate_limits'}
Config sections that can be safely updated at runtime without restart.
RESTART_REQUIRED_SECTIONS
module-attribute
¶
RESTART_REQUIRED_SECTIONS = {'discord', 'data_dir', 'workspace_dir', 'database_path', 'chat_provider', 'memory', 'health_check'}
Config sections that require a full restart to take effect.
Classes¶
ConfigError
dataclass
¶
A single configuration validation error or warning.
Used by per-section validate() methods and AppConfig.validate()
to collect ALL issues before reporting, so operators can fix everything
in one pass.
ConfigValidationError ¶
Bases: Exception
Raised when the application configuration fails validation checks.
Contains a list of all validation errors found, not just the first one, so operators can fix all issues in one pass.
Source code in src/config.py
PerProjectChannelsConfig
dataclass
¶
PerProjectChannelsConfig(auto_create: bool = False, naming_convention: str = '{project_id}', category_name: str = '', private: bool = True)
Configuration for automatic per-project Discord channel management.
DiscordConfig
dataclass
¶
DiscordConfig(bot_token: str = '', guild_id: str = '', channels: dict[str, str] = (lambda: {'channel': 'agent-queue', 'agent_questions': 'agent-questions'})(), authorized_users: list[str] = list(), per_project_channels: PerProjectChannelsConfig = PerProjectChannelsConfig())
Discord bot connection and channel routing settings.
AgentsDefaultConfig
dataclass
¶
AgentsDefaultConfig(heartbeat_interval_seconds: int = 30, stuck_timeout_seconds: int = 0, graceful_shutdown_timeout_seconds: int = 30)
Default timeouts for agent health monitoring and graceful shutdown.
SchedulingConfig
dataclass
¶
Controls how the scheduler distributes agent capacity across projects.
rolling_window_hours defines the lookback period for proportional credit accounting. min_task_guarantee ensures every active project gets at least one task slot regardless of credit balance.
PauseRetryConfig
dataclass
¶
PauseRetryConfig(rate_limit_backoff_seconds: int = 60, token_exhaustion_retry_seconds: int = 300, rate_limit_max_retries: int = 3, rate_limit_max_backoff_seconds: int = 300)
Backoff and retry timing for rate-limited and token-exhausted tasks.
Controls both the in-process exponential backoff (before a task is paused) and the longer pause durations (after a task enters PAUSED state and waits for resume_after to elapse).
AutoTaskConfig
dataclass
¶
AutoTaskConfig(enabled: bool = True, plan_file_patterns: list[str] = (lambda: ['.claude/plan.md', 'plan.md', 'docs/plans/*.md', 'plans/*.md', 'docs/plan.md'])(), inherit_repo: bool = True, inherit_approval: bool = True, base_priority: int = 100, chain_dependencies: bool = True, rebase_between_subtasks: bool = False, mid_chain_rebase: bool = True, mid_chain_rebase_push: bool = False, max_plan_depth: int = 1, max_steps_per_plan: int = 5, use_llm_parser: bool = False, llm_parser_model: str = '', skip_if_implemented: bool = True)
Configuration for auto-generating tasks from implementation plans.
ArchiveConfig
dataclass
¶
ArchiveConfig(enabled: bool = True, after_hours: float = 24.0, statuses: list[str] = (lambda: ['COMPLETED', 'FAILED', 'BLOCKED'])())
Configuration for automatic archiving of terminal tasks.
When enabled, the orchestrator automatically archives tasks that have
been in a terminal status (COMPLETED, FAILED, BLOCKED) for longer than
after_hours. This keeps the active task list clean without
requiring manual /archive-tasks commands.
MonitoringConfig
dataclass
¶
Configuration for monitoring stuck or stalled tasks.
MemoryConfig
dataclass
¶
MemoryConfig(enabled: bool = False, embedding_provider: str = 'openai', embedding_model: str = '', embedding_base_url: str = '', embedding_api_key: str = '', milvus_uri: str = '~/.agent-queue/memsearch/milvus.db', milvus_token: str = '', max_chunk_size: int = 1500, overlap_lines: int = 2, auto_remember: bool = True, auto_recall: bool = True, recall_top_k: int = 5, compact_enabled: bool = False, compact_interval_hours: int = 24, index_notes: bool = True, index_sessions: bool = False)
Configuration for the semantic memory subsystem (memsearch).
All fields have safe defaults — the subsystem is disabled unless
enabled is explicitly set to True in the YAML config.
See notes/memsearch-integration.md for full documentation.
LoggingConfig
dataclass
¶
Configuration for structured logging and output format.
Controls the Python stdlib logging setup. When format is "json",
all log output is emitted as single-line JSON objects suitable for log
aggregation systems. The "text" format (default) is human-readable
with correlation context appended.
ChatProviderConfig
dataclass
¶
ChatProviderConfig(provider: str = 'anthropic', model: str = '', base_url: str = '', keep_alive: str = '1h')
LLM provider settings for the Discord chat agent (not the coding agents).
LLMLoggingConfig
dataclass
¶
Configuration for logging LLM inputs/outputs to JSONL files.
AgentProfileConfig
dataclass
¶
AgentProfileConfig(id: str = '', name: str = '', description: str = '', model: str = '', permission_mode: str = '', allowed_tools: list[str] = list(), mcp_servers: dict[str, dict] = dict(), system_prompt_suffix: str = '', install: dict = dict())
Configuration for an agent profile loaded from YAML.
Profiles from YAML are synced to the database at startup. Profiles can also be created dynamically via Discord commands.
HealthCheckConfig
dataclass
¶
Configuration for the HTTP health check server.
When enabled, the daemon exposes /health and /ready endpoints
on the configured port for external monitoring and load balancer probes.
AppConfig
dataclass
¶
AppConfig(data_dir: str = (lambda: os.path.expanduser('~/.agent-queue'))(), workspace_dir: str = (lambda: os.path.expanduser('~/agent-queue-workspaces'))(), database_path: str = (lambda: os.path.expanduser('~/.agent-queue/agent-queue.db'))(), profile: str = '', env: str = 'production', discord: DiscordConfig = DiscordConfig(), agents_config: AgentsDefaultConfig = AgentsDefaultConfig(), scheduling: SchedulingConfig = SchedulingConfig(), pause_retry: PauseRetryConfig = PauseRetryConfig(), chat_provider: ChatProviderConfig = ChatProviderConfig(), hook_engine: HookEngineConfig = HookEngineConfig(), health_check: HealthCheckConfig = HealthCheckConfig(), logging: LoggingConfig = LoggingConfig(), monitoring: MonitoringConfig = MonitoringConfig(), archive: ArchiveConfig = ArchiveConfig(), auto_task: AutoTaskConfig = AutoTaskConfig(), memory: MemoryConfig = MemoryConfig(), llm_logging: LLMLoggingConfig = LLMLoggingConfig(), agent_profiles: list[AgentProfileConfig] = list(), global_token_budget_daily: int | None = None, rate_limits: dict[str, dict[str, int]] = dict(), _config_path: str = '')
Top-level application configuration aggregating all subsystem configs.
Instantiated once by load_config() at startup and threaded through to all major components. Each component reads only its relevant sub-config.
The env field selects the environment profile (dev, staging, production).
When set, load_config will look for an override file named
config.{env}.yaml in the same directory as the main config file and
deep-merge it over the base config.
The validate() method performs fail-fast checks on critical settings.
The reload_non_critical() method returns a fresh config with only
non-critical settings updated from disk for hot-reloading.
Functions¶
validate ¶
Validate all configuration settings, delegating to per-section validators.
Returns a list of all ConfigError instances found (errors and warnings).
Does NOT raise — callers decide how to handle errors. The load_config()
function still raises ConfigValidationError for backward compatibility.
Source code in src/config.py
reload_non_critical ¶
Return a new AppConfig with non-critical settings refreshed from disk.
Non-critical settings (safe to change at runtime without restart): - scheduling, pause_retry, auto_task, archive, monitoring - hook_engine, llm_logging
Critical settings (NOT reloaded — require restart): - discord, database_path, workspace_dir, chat_provider, memory, health_check
Returns a new AppConfig instance; the caller is responsible for swapping references. If the config file cannot be read or parsed, the current config is returned unchanged and the error is logged.
Source code in src/config.py
ConfigWatcher ¶
Watches the config file for changes and emits events on reload.
Uses mtime-based polling (not filesystem events) for maximum portability.
On change detection, loads the new config, validates it, diffs against
the current config, and emits config.reloaded / config.restart_needed
events via the EventBus.
Only hot-reloadable sections are applied; restart-required sections trigger a warning event but are not applied.
Source code in src/config.py
Attributes¶
Functions¶
start ¶
stop
async
¶
Stop the background polling task.
reload
async
¶
Reload configuration from disk, diff, and emit events.
Returns a summary dict with changed_sections,
restart_required, and applied keys.
Source code in src/config.py
Functions¶
diff_configs ¶
Compare two AppConfig instances and return the set of changed section names.
Uses dataclasses.asdict() for deep comparison of each section.
Skips internal fields (prefixed with _).
Source code in src/config.py
load_config ¶
Load and validate application configuration from a YAML file.
Processing order
- Load
.envfrom the config file's directory (without overriding existing env vars) - Parse the base YAML file
- Determine the environment profile (
AGENT_QUEUE_ENVenv var, orenvfield in config, default"production") - If an overlay file
config.{env}.yamlexists in the same directory, deep-merge it over the base config - If a profile is specified (via
--profileCLI arg orAGENT_QUEUE_PROFILEenv var), load the profile overlay fromprofiles/{profile}.yamlrelative to the config directory and deep-merge it over the config - Recursively substitute
${ENV_VAR}references in all strings - Map sections into typed dataclass instances
- Run
validate()to catch misconfiguration early
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Path to the base YAML config file. |
required |
profile
|
str | None
|
Optional profile name. Falls back to |
None
|
Source code in src/config.py
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 | |