from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass class ConfigAdapterResult: config: dict[str, Any] warnings: list[str] = field(default_factory=list) migrated_fields: list[str] = field(default_factory=list) def adapt_config(config: dict[str, Any] | None) -> ConfigAdapterResult: if not config: return ConfigAdapterResult(config={}) result = ConfigAdapterResult(config=dict(config)) _adapt_streaming(result) _adapt_security(result) _adapt_capabilities(result) _adapt_dm_scope(result) return result def _adapt_streaming(result: ConfigAdapterResult) -> None: streaming = result.config.get("streaming", {}) if isinstance(streaming, str): result.config["streaming"] = {"mode": streaming} result.warnings.append("streaming config was a string, converted to {mode: ...}") result.migrated_fields.append("streaming") legacy_stream = result.config.pop("streamMode_ENABLED", None) if legacy_stream is not None: result.config.setdefault("streaming", {}) if isinstance(result.config["streaming"], str): result.config["streaming"] = {"mode": result.config["streaming"]} result.config["streaming"]["mode"] = "auto" result.warnings.append("Legacy streamMode_ENABLED migrated to streaming.mode=auto") result.migrated_fields.append("streamMode_ENABLED") def _adapt_security(result: ConfigAdapterResult) -> None: security = result.config.get("security", {}) if isinstance(security, str): result.config["security"] = {"mode": security} result.warnings.append("security config was a string, converted to object") result.migrated_fields.append("security") def _adapt_capabilities(result: ConfigAdapterResult) -> None: legacy_exec_approvals = result.config.pop("execApprovalsEnabled", None) if legacy_exec_approvals is not None: caps = result.config.setdefault("capabilities", {}) caps["execApprovals"] = bool(legacy_exec_approvals) result.warnings.append("Legacy execApprovalsEnabled migrated to capabilities.execApprovals") result.migrated_fields.append("execApprovalsEnabled") def _adapt_dm_scope(result: ConfigAdapterResult) -> None: legacy_dm = result.config.pop("dm_chat", None) if legacy_dm is not None: result.config["dm_scope"] = legacy_dm result.warnings.append("Legacy dm_chat migrated to dm_scope") result.migrated_fields.append("dm_chat")