from __future__ import annotations

import json
import os
import shutil
import threading
import webbrowser
from pathlib import Path

from .avatar_chat import (
    AvatarSuggestedAction,
    build_avatar_chat_result,
    format_diagnostics_evidence_message,
    format_memory_diagnostics_message,
    format_plan_preview_steps,
    human_step_label,
    human_step_status,
    keyboard_type_success_message_key,
    safe_format_template,
)
from .avatar_goal_verification import format_goal_verification_message
from .avatar_auto_execute import (
    plan_auto_start_delay_ms,
    should_auto_execute_action,
    should_auto_execute_plan,
)
from .avatar_execution_status import (
    build_chat_timeline_events,
    build_done_timeline_event,
    build_execute_timeline_event,
    build_pipeline_timeline_chip,
    build_timeline_block,
    format_model_chip,
    format_timeline_line,
    sanitize_backend_error_snippet,
)
from .avatar_chat_input_bar import (
    AvatarChatInputBar,
    build_avatar_chat_input_bar,
    install_avatar_chat_file_drop,
    refresh_input_viewport,
    refresh_mode_button,
    refresh_placeholder_visibility,
    refresh_primary_button,
    refresh_tag_chip,
    refresh_working_badge,
)
from .avatar_plan_session import (
    active_session_covers_preset,
    detect_plan_session_preset,
)
from .avatar_voice_input import request_voice_input
from .i18n import human_execution_error_reason, human_execution_outcome_label, language_options, translate
from .profile_language import resolve_ui_language
from .pairing_dialog import show_pairing_dialog
from .gtk_alert_confirm import present_confirm_alert
from .modal_prompt import show_modal_action_required, show_modal_action_required_blocking
from ..active.plan_execution_control import (
    INTERACTIVE_MODAL_MAX_SEC,
    VISION_RECOVERY_MODAL_MAX_SEC,
    PlanExecutionControl,
)
from .agent_status_overlay import AgentStatusState, get_agent_status_overlay
from .pending_confirmation_ui import pending_awaiting_owner_decision, pending_requires_user_action
from .avatar_chat_scroll import chat_scroll_bottom_value, should_follow_new_message
from .startup_splash import resolve_splash_logo_path
from .security_panel import (
    ACTION_SESSION_UI_CARDS,
    FULL_ASSISTANCE_DURATION_MINUTES,
    PROFILE_PRESET_KEYS,
    RECOMMENDED_SAFE_LIFETIME_KEY,
    RECOMMENDED_SAFE_PROFILE_KEY,
    SIMPLIFIED_TASK_SESSION_CARDS,
    apply_permission_preset_selection,
    build_full_assistance_session_spec,
    build_profile_summary,
    build_safety_summary_body,
    build_security_status_snapshot,
    capability_plain_i18n_key,
    dedupe_sessions_by_preset,
    normalize_session_preset_key,
    profile_display_i18n_key,
)
from ...shared.permission_profiles import SESSION_LIFETIME_KEYS, allowed_session_lifetimes
from .window_branding import apply_window_icon, apply_window_wm_class, resolve_brand_icon_path
from ..active.double_confirm_flow import requires_double_confirm, wrap_confirmation_callback
from ..capability_manager import CAPABILITY_DEFINITIONS, CRITICAL_CAPABILITIES, HIGH_RISK_CAPABILITIES
from ..orchestra_status_service import OrchestraStatusSnapshot, OrchestraStatusState, probe_orchestra_status
from ...shared.ui_capability_grants import PERMISSION_PRESETS, UI_CAPABILITY_KEYS

AVATAR_CHAT_MAX_MESSAGES = 100
DASHBOARD_REFRESH_MIN_INTERVAL_SEC = 5.0
ORCHESTRA_STATUS_REFRESH_SEC = 30

# CSS for capability risk badges, status labels, trust badge
_CAP_CSS = b"""
label.risk-badge { border-radius: 4px; padding: 1px 6px; font-size: 0.74em; font-weight: bold; }
label.risk-low    { background-color: #1d7340; color: #ffffff; }
label.risk-medium { background-color: #b5621b; color: #ffffff; }
label.risk-high   { background-color: #c0392b; color: #ffffff; }
label.risk-critical { background-color: #7b0000; color: #ffffff; }
label.status-enabled  { color: #1d7340; font-weight: bold; }
label.status-disabled { color: #888888; }
label.status-expired  { color: #c0392b; font-style: italic; }
label.trust-level-value { color: #1a73e8; font-weight: bold; font-size: 1.05em; }
label.cap-col-header   { font-weight: bold; }
label.cap-name         { font-weight: bold; }
label.cap-desc         { font-size: 0.85em; color: #666666; }
label.active-cap-name  { font-weight: bold; color: #1d7340; }
label.page-title       { font-size: 1.15em; font-weight: bold; }
label.kpi-title        { font-size: 0.8em; color: #666666; }
label.kpi-value        { font-size: 1.05em; font-weight: bold; }
frame.kpi-card         { padding: 4px; }
label.footer-value     { font-weight: bold; }
box.onboarding-banner  { background-color: alpha(#1a73e8, 0.08); border: 1px solid alpha(#1a73e8, 0.28); border-radius: 8px; }
box.pending-confirmation-banner { background-color: alpha(#c0392b, 0.12); border: 2px solid alpha(#c0392b, 0.55); border-radius: 8px; }
label.pending-confirmation-title { font-weight: bold; font-size: 1.05em; color: #c0392b; }
label.orchestra-status-pill { border-radius: 999px; padding: 2px 10px; font-size: 0.82em; font-weight: bold; }
label.orchestra-cloud-active { background-color: #1d7340; color: #ffffff; }
label.orchestra-local-fallback { background-color: #b8860b; color: #ffffff; }
label.orchestra-offline { background-color: #c0392b; color: #ffffff; }
label.avatar-model-chip {
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 0.78em;
  font-weight: 600;
  background-color: alpha(#0b5fff, 0.10);
  border: 1px solid alpha(#0b5fff, 0.28);
  color: #0b5fff;
}
textview.chat-conversation { font-size: 0.95em; line-height: 1.5; padding: 8px; }
textview.chat-input { font-size: 0.95em; padding: 6px 8px; background: transparent; border: none; }
box.avatar-chat-input-shell { margin-top: 2px; }
label.avatar-chat-accuracy-disclaimer {
  margin-top: 6px;
  font-size: 0.78em;
  opacity: 0.72;
  color: #64748b;
}
label.avatar-chat-working-badge {
  background-color: alpha(#0b5fff, 0.10);
  border: 1px solid alpha(#0b5fff, 0.28);
  border-radius: 10px;
  padding: 2px 10px;
  font-size: 0.78em;
  font-weight: bold;
  color: #0b5fff;
}
box.avatar-chat-pill {
  border: 2px solid alpha(#64748b, 0.45);
  border-radius: 24px;
  padding: 8px 12px;
  background-color: #ffffff;
}
box.avatar-chat-pill:focus-within {
  border-color: #0b5fff;
  box-shadow: 0 0 0 2px alpha(#0b5fff, 0.18);
}
button.avatar-chat-icon-btn {
  min-width: 32px;
  min-height: 32px;
  border-radius: 16px;
  padding: 0;
}
button.avatar-chat-mode-btn {
  min-height: 32px;
  border-radius: 12px;
  padding: 4px 10px;
  font-size: 0.85em;
}
box.avatar-chat-tag-chip {
  background-color: alpha(#7c3aed, 0.10);
  border: 1px solid alpha(#7c3aed, 0.32);
  border-radius: 12px;
  padding: 2px 6px 2px 10px;
}
label.avatar-chat-tag-label { color: #6d28d9; font-size: 0.85em; font-weight: 600; }
button.avatar-chat-tag-dismiss {
  min-width: 22px;
  min-height: 22px;
  padding: 0;
  border-radius: 11px;
  font-size: 0.9em;
}
label.avatar-chat-placeholder { font-size: 0.95em; opacity: 0.55; }
button.avatar-chat-send-btn {
  min-width: 36px;
  min-height: 36px;
  border-radius: 18px;
  padding: 0;
  font-size: 1.05em;
}
button.avatar-chat-stop-btn {
  min-width: 36px;
  min-height: 36px;
  border-radius: 18px;
  padding: 0;
  font-size: 0.85em;
  background-color: #111111;
  color: #ffffff;
}
textview.chat-conversation avatar-speaker { font-weight: bold; color: #0b5fff; }
textview.chat-conversation user-speaker { font-weight: bold; color: #1e293b; }
textview.chat-conversation avatar-body { color: #334155; }
textview.chat-conversation user-body { color: #0f172a; background-color: alpha(#0b5fff, 0.06); border-radius: 6px; }
frame.section-card { margin-top: 4px; margin-bottom: 4px; }
label.section-heading { font-size: 1.0em; font-weight: bold; margin-bottom: 2px; }
"""


def _resolve_brand_icon_path() -> Path | None:
    """Resolve IuVe brand icon for console header and window."""
    return resolve_brand_icon_path() or resolve_splash_logo_path()


def apply_application_branding(application) -> None:
    """Set dock/taskbar icon metadata on the Gtk/Adw application."""
    icon_name = "iuve-connect-agent"
    try:
        if hasattr(application, "set_icon_name"):
            application.set_icon_name(icon_name)
    except Exception:
        pass
    icon_path = _resolve_brand_icon_path()
    if icon_path is None:
        return
    try:
        from gi.repository import Gdk, Gio  # type: ignore

        if hasattr(Gdk, "Texture"):
            texture = Gdk.Texture.new_from_filename(str(icon_path))
            if texture is not None and hasattr(application, "set_icon"):
                application.set_icon(texture)
        file_obj = Gio.File.new_for_path(str(icon_path))
        if hasattr(application, "set_icon"):
            application.set_icon(file_obj)
    except Exception:
        pass
    try:
        from gi.repository import Gtk  # type: ignore

        if hasattr(Gtk.Window, "set_default_icon_from_file"):
            Gtk.Window.set_default_icon_from_file(str(icon_path))
    except Exception:
        pass


def resolve_update_check_message(payload: dict[str, object], translate) -> str:
    status = str(payload.get("status") or "unknown").strip().lower()
    reason = str(payload.get("reason") or "").strip().lower()
    target_version = str(
        payload.get("target_version") or payload.get("available_version") or ""
    ).strip()
    current_version = str(payload.get("current_version") or "").strip()
    would_upgrade = bool(payload.get("would_upgrade", False))

    if status == "blocked" and reason == "device_not_paired":
        return translate("updates.pairing.required.message")
    if reason == "installed" or status in {"updated_in_place", "installed"}:
        return translate("updates.check.installed")
    if reason == "already_up_to_date" or status == "up_to_date":
        if current_version:
            return translate("updates.check.up_to_date_version").format(version=current_version)
        return translate("updates.check.up_to_date")
    if would_upgrade or reason in {"update_available", "eligible"}:
        if target_version:
            return translate("updates.check.available_version").format(version=target_version)
        return translate("updates.check.available")
    blocked_reason_keys = {
        "release_signature_policy_failed": "updates.check.signature_blocked",
        "invalid_release_checksum": "updates.check.invalid_release_checksum",
        "missing_release_version": "updates.check.missing_release_version",
        "channel_disabled": "updates.check.channel_disabled",
        "release_channel_mismatch": "updates.check.channel_mismatch",
        "unsupported_package_format": "updates.check.unsupported_package",
        "checksum_mismatch": "updates.check.checksum_mismatch",
        "dpkg_not_found": "updates.check.dpkg_not_found",
        "privilege_installer_unavailable": "updates.check.privilege_installer_unavailable",
    }
    if status == "blocked" and reason in blocked_reason_keys:
        message_key = blocked_reason_keys[reason]
        return translate(message_key)
    if status == "blocked" and reason.startswith("release_state_"):
        return translate("updates.check.release_unavailable")
    if status in {"dry_run", "ok"}:
        if target_version:
            return translate("updates.check.up_to_date_version").format(version=current_version or target_version)
        return translate("updates.check.up_to_date")
    if status == "failed" and reason in {"install_failed", "dpkg_install_failed"}:
        install_detail = str(
            payload.get("install_stderr") or payload.get("error") or ""
        ).strip()
        if current_version and target_version and current_version == target_version:
            return translate("updates.check.already_installed_version").format(version=current_version)
        if install_detail:
            return translate("updates.check.install_failed_detail").format(
                version=target_version or "latest",
                detail=install_detail[:240],
            )
        return translate("updates.check.install_failed").format(version=target_version or "latest")
    if status == "failed" and reason == "privilege_installer_unavailable":
        return translate("updates.check.privilege_installer_unavailable")
    if status == "failed" and reason == "release_lookup_failed":
        return translate("updates.check.release_lookup_failed")
    if status == "failed" and reason == "release_download_failed":
        return translate("updates.check.release_download_failed")
    if status == "failed" and reason == "update_check_exception":
        error_detail = str(payload.get("error") or reason).strip()
        if error_detail:
            return translate("updates.check.temporary_unavailable_detail").format(detail=error_detail[:240])
        return translate("updates.check.update_check_exception")
    error_detail = str(payload.get("error") or "").strip()
    if error_detail:
        return translate("updates.check.temporary_unavailable_detail").format(detail=error_detail[:240])
    if reason and reason not in {
        "signed_release",
        "trusted_official_checksum_channel",
        "current_version",
    }:
        return translate("updates.check.temporary_unavailable_detail").format(detail=reason[:240])
    return translate("updates.check.temporary_unavailable")


def create_main_window(adw_module, gtk_module, glib_module, *, application, status_adapter: StatusAdapter):
    """Build and return the main Agent Console window instance."""

    class MainWindow(adw_module.ApplicationWindow):
        def __init__(self) -> None:
            super().__init__(application=application)
            self._adapter = status_adapter
            self._lang = resolve_ui_language(
                profile_lang=None,
                cached_lang=self._adapter.load_cached_ui_language(),
                env_override=os.getenv("IUVE_AGENT_UI_LANG", ""),
            )
            options = language_options(self._lang)
            self._lang_codes = [code for code, _label in options]
            self._lang_labels = [label for _code, label in options]
            self._i18n_labels: list[tuple[object, str]] = []
            self._stack_pages: dict[str, object] = {}
            self._is_fullscreen = False
            self._avatar_default_applied = False
            self._glib = glib_module
            self._gtk = gtk_module
            self._adw = adw_module
            self._command_log_filter = "all"
            self._confirmation_dialog_active = False
            self._active_confirmation_dialog = None
            self._pending_command_id = ""
            self._avatar_plan_running = False
            self._avatar_plan_cancel_requested = False
            self._avatar_plan_execution_control = PlanExecutionControl()
            self._avatar_plan_finish_handled = False
            self._avatar_chat_mode = "auto"
            self._avatar_chat_tag_dismissed = False
            self._avatar_plan_session_state: str | None = None
            self._avatar_plan_session_preset: str | None = None
            self._avatar_plan_bound_session_id: str | None = None
            self._avatar_plan_session_plan_id: str | None = None
            self._avatar_resume_step_index: int = 0
            self._avatar_show_retry_step: bool = False
            self._avatar_send_in_flight = False
            self._avatar_auto_execute_blocked = False
            self._avatar_thinking_marker: int | None = None
            self._last_dashboard_refresh_at = 0.0
            self._last_avatar_context_refresh_at = 0.0
            self._avatar_prior_user_text: str | None = None
            self._orchestra_status_snapshot: OrchestraStatusSnapshot | None = None
            self._orchestra_status_probe_in_flight = False

            self.set_title(self._t("app.title"))
            self.set_default_size(1020, 680)

            # Inject CSS for capability widgets (after realize so display is available)
            self._css_provider = gtk_module.CssProvider()
            self._css_provider.load_from_data(_CAP_CSS)
            self.connect("realize", self._on_realize_inject_css)
            self._brand_icon_path = _resolve_brand_icon_path()
            apply_window_wm_class(self)

            root = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=12)
            root.set_margin_top(12)
            root.set_margin_bottom(12)
            root.set_margin_start(12)
            root.set_margin_end(12)

            header = adw_module.HeaderBar()
            brand_icon_path = _resolve_brand_icon_path()
            if brand_icon_path is not None:
                try:
                    brand_logo = gtk_module.Image.new_from_file(str(brand_icon_path))
                    brand_logo.set_pixel_size(28)
                    brand_logo.set_margin_end(6)
                    header.pack_start(brand_logo)
                except Exception:
                    pass
            self._header_title_label = gtk_module.Label(label=self._t("app.title"))
            header.set_title_widget(self._header_title_label)

            lang_model = gtk_module.StringList.new(self._lang_labels)
            self._lang_switch = gtk_module.DropDown.new(lang_model, None)
            try:
                idx = self._lang_codes.index(self._lang)
            except ValueError:
                idx = 0
            self._lang_switch.set_selected(idx)
            self._lang_switch.connect("notify::selected", self._on_language_changed)

            self._header_fullscreen_btn = gtk_module.Button(label=self._t("header.fullscreen.enter"))
            self._header_fullscreen_btn.connect("clicked", self._on_toggle_fullscreen)
            header.pack_end(self._header_fullscreen_btn)

            self._header_settings_btn = gtk_module.Button(label=self._t("header.settings"))
            self._header_settings_btn.connect("clicked", self._on_header_settings_clicked)
            header.pack_end(self._header_settings_btn)

            self._header_refresh_btn = gtk_module.Button(label=self._t("header.refresh"))
            self._header_refresh_btn.connect("clicked", self._on_header_refresh_clicked)
            header.pack_end(self._header_refresh_btn)

            self._header_update_btn = gtk_module.Button(label=self._t("action.check_update"))
            self._header_update_btn.connect("clicked", self._on_header_check_update_clicked)
            header.pack_end(self._header_update_btn)

            self._header_profile_btn = gtk_module.Button(label=self._t("header.profile"))
            self._header_profile_btn.connect("clicked", self._on_header_profile_clicked)
            header.pack_end(self._header_profile_btn)

            header.pack_end(self._lang_switch)
            root.append(header)

            # Compact runtime summary in header area so chat can stay dominant.
            self._header_status_prefixes: dict[str, object] = {}
            self._header_status_values: dict[str, object] = {}
            header_status = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=12)
            header_status.add_css_class("toolbar")

            def _header_slot(key: str, label_key: str):
                wrap = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=4)
                prefix = gtk_module.Label(label=f"{self._t(label_key)}:")
                prefix.set_sensitive(False)
                value = gtk_module.Label(label="-")
                value.add_css_class("footer-value")
                wrap.append(prefix)
                wrap.append(value)
                header_status.append(wrap)
                self._header_status_prefixes[key] = prefix
                self._header_status_values[key] = value

            _header_slot("device", "header.device")
            _header_slot("trust", "header.trust")
            _header_slot("avatar_mode", "header.avatar_mode")
            _header_slot("active_permissions", "header.active_permissions")
            _header_slot("connection", "header.connection")

            orchestra_wrap = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=4)
            orchestra_prefix = gtk_module.Label(label=f"{self._t('header.orchestra')}:")
            orchestra_prefix.set_sensitive(False)
            self._orchestra_status_btn = gtk_module.Button()
            self._orchestra_status_btn.add_css_class("flat")
            self._orchestra_status_btn.set_tooltip_text(self._t("status.orchestra.cloud_active"))
            self._orchestra_status_pill = gtk_module.Label(label="-")
            self._orchestra_status_pill.add_css_class("orchestra-status-pill")
            self._orchestra_status_btn.set_child(self._orchestra_status_pill)
            self._orchestra_status_btn.connect("clicked", self._on_orchestra_status_clicked)
            orchestra_wrap.append(orchestra_prefix)
            orchestra_wrap.append(self._orchestra_status_btn)
            self._avatar_model_chip = gtk_module.Label(label="")
            self._avatar_model_chip.add_css_class("avatar-model-chip")
            self._avatar_model_chip.set_visible(False)
            orchestra_wrap.append(self._avatar_model_chip)
            header_status.append(orchestra_wrap)
            self._header_status_prefixes["orchestra"] = orchestra_prefix

            _header_slot("owner", "header.owner")
            root.append(header_status)

            self._pending_confirmation_banner = self._build_pending_confirmation_banner(gtk_module)
            root.append(self._pending_confirmation_banner)

            split = gtk_module.Paned.new(gtk_module.Orientation.HORIZONTAL)
            split.set_vexpand(True)
            root.append(split)

            self._stack = gtk_module.Stack()
            self._stack.set_vexpand(True)
            self._stack.set_hexpand(True)

            sidebar = gtk_module.StackSidebar()
            sidebar.set_stack(self._stack)
            sidebar.set_vexpand(True)
            sidebar.set_size_request(112, -1)
            split.set_start_child(sidebar)
            split.set_resize_start_child(False)
            split.set_shrink_start_child(False)
            split.set_end_child(self._stack)

            avatar_page = self._build_avatar_page(gtk_module)
            self._stack.add_titled(avatar_page, "avatar", self._t("nav.avatar"))
            self._stack_pages["avatar"] = avatar_page

            dashboard_page = self._wrap_page_scroll(gtk_module, self._build_dashboard_page(gtk_module))
            self._stack.add_titled(dashboard_page, "dashboard", self._t("nav.dashboard"))
            self._stack_pages["dashboard"] = dashboard_page
            devices_page = self._wrap_page_scroll(gtk_module, self._build_devices_page(gtk_module))
            self._stack.add_titled(devices_page, "devices", self._t("nav.devices"))
            self._stack_pages["devices"] = devices_page
            diagnostics_page = self._wrap_page_scroll(gtk_module, self._build_diagnostics_page(gtk_module))
            self._stack.add_titled(diagnostics_page, "diagnostics", self._t("nav.diagnostics"))
            self._stack_pages["diagnostics"] = diagnostics_page
            security_page = self._build_security_page(gtk_module)
            self._stack.add_titled(security_page, "security", self._t("nav.security"))
            self._stack_pages["security"] = security_page
            updates_page = self._wrap_page_scroll(gtk_module, self._build_updates_page(gtk_module))
            self._stack.add_titled(updates_page, "updates", self._t("nav.updates"))
            self._stack_pages["updates"] = updates_page

            self._stack.set_visible_child_name("avatar")

            self._adapter.set_device_capability_scan_callback(self._on_device_profile_updated)

            root.append(self._build_bottom_status_bar(gtk_module))

            self.set_content(root)
            self._refresh_dashboard(force=True)
            glib_module.idle_add(self._ensure_avatar_default_once)
            glib_module.idle_add(self._sync_owner_interface_language_on_startup)
            self.connect("realize", self._on_window_realized_poll_pending)
            glib_module.idle_add(self._poll_pending_command_confirmation)
            glib_module.timeout_add(1000, self._poll_pending_command_confirmation)
            glib_module.timeout_add_seconds(5, self._refresh_timer)
            glib_module.timeout_add_seconds(ORCHESTRA_STATUS_REFRESH_SEC, self._orchestra_status_refresh_timer)
            glib_module.idle_add(self._refresh_orchestra_status_async)

        def _on_window_realized_poll_pending(self, *_args) -> None:
            self._glib.idle_add(self._poll_pending_command_confirmation)

        def _t(self, key: str) -> str:
            return translate(self._lang, key)

        def _on_realize_inject_css(self, _widget):
            try:
                display = self.get_display()
                gtk_module.StyleContext.add_provider_for_display(
                    display, self._css_provider,
                    gtk_module.STYLE_PROVIDER_PRIORITY_APPLICATION,
                )
            except Exception:
                pass
            self._apply_window_icon()

        def _apply_window_icon(self) -> None:
            icon_path = getattr(self, "_brand_icon_path", None)
            if icon_path is not None:
                try:
                    if hasattr(self._gtk.Window, "set_default_icon_from_file"):
                        self._gtk.Window.set_default_icon_from_file(str(icon_path))
                except Exception:
                    pass
            apply_window_icon(self, icon_path=icon_path)

        def _agent_status_overlay(self):
            return get_agent_status_overlay(
                gtk_module=self._gtk,
                glib_module=self._glib,
                lang=self._lang,
            )

        def _set_agent_status(self, state: AgentStatusState) -> None:
            try:
                from ..active.agent_activity_state import set_agent_activity

                set_agent_activity(state.value)
                overlay = self._agent_status_overlay()
                overlay.set_locale(self._lang)
                overlay.set_state(state)
            except Exception:
                pass

        def _refresh_agent_status_overlay(self) -> None:
            try:
                pending = self._adapter.read_pending_command_confirmation()
                if (
                    pending_requires_user_action(pending)
                    and self._pending_needs_owner_decision(pending)
                ):
                    self._set_agent_status(AgentStatusState.BLOCKED)
                    return
                if getattr(self, "_avatar_plan_running", False):
                    self._set_agent_status(AgentStatusState.WORKING)
                    return
                snapshot = self._agent_status_overlay().snapshot()
                if snapshot.state is not AgentStatusState.ERROR:
                    self._set_agent_status(AgentStatusState.IDLE)
            except Exception:
                pass

        _INTERACTIVE_PLAN_STEP_TYPES = frozenset(
            {"browser_open_url", "open_url", "application_launch", "open_document_edit"}
        )

        def _present_action_required_modal(
            self,
            *,
            title: str,
            body: str,
            buttons: list[tuple[str, str]],
            on_response,
            default_button: str | None = None,
        ) -> None:
            self._bring_window_to_front()
            show_modal_action_required(
                self._gtk,
                self._adw,
                parent=self,
                title=title,
                body=body,
                buttons=buttons,
                on_response=on_response,
                default_button=default_button,
            )

        def _present_permission_required_modal(self, capability_key: str | None) -> None:
            capability_label = self._t(f"cap.{capability_key}") if capability_key else self._t("nav.security")
            self._present_action_required_modal(
                title=self._t("modal.permission_required.title"),
                body=safe_format_template(
                    self._t("modal.permission_required.body"),
                    capability=capability_label,
                ),
                buttons=[
                    ("cancel", self._t("modal.action.cancel")),
                    ("permissions", self._t("modal.action.open_permissions")),
                ],
                default_button="permissions",
                on_response=lambda response: self._on_avatar_open_permissions_clicked(None)
                if response == "permissions"
                else None,
            )

        def _wait_for_interactive_plan_step(self, command_type: str) -> None:
            if str(command_type or "").strip().lower() not in self._INTERACTIVE_PLAN_STEP_TYPES:
                return
            if self._avatar_plan_execution_control.is_cancelled():
                return
            self._set_agent_status(AgentStatusState.BLOCKED)
            show_modal_action_required_blocking(
                self._gtk,
                self._adw,
                self._glib,
                parent=self,
                title=self._t("modal.browser_wait.title"),
                body=self._t("modal.browser_wait.body"),
                buttons=[("done", self._t("modal.action.done"))],
                default_button="done",
                timeout_seconds=INTERACTIVE_MODAL_MAX_SEC,
                cancel_event=self._avatar_plan_execution_control.cancel_event,
            )
            if not self._avatar_plan_execution_control.is_cancelled():
                self._set_agent_status(AgentStatusState.WORKING)

        def _wait_for_vision_fail_recovery(self, context: dict) -> str:
            if self._avatar_plan_execution_control.is_cancelled():
                return "dismiss"
            self._set_agent_status(AgentStatusState.BLOCKED)
            attempt = int(context.get("attempt") or 1)
            max_attempts = int(context.get("max_attempts") or 3)
            expected_text = str(context.get("expected_text") or "").strip()
            findings = str(context.get("findings") or "").strip().lower()
            body = safe_format_template(
                self._t("modal.vision_fail.body"),
                attempt=attempt,
                max_attempts=max_attempts,
                expected_text=expected_text,
            )
            hint = self._t("modal.vision_fail.cloud_hint")
            if hint:
                body = f"{body}\n\n{hint}"
            response = show_modal_action_required_blocking(
                self._gtk,
                self._adw,
                self._glib,
                parent=self,
                title=self._t("modal.vision_fail.title"),
                body=body,
                buttons=[
                    ("dismiss", self._t("modal.action.cancel")),
                    ("retry", self._t("modal.vision_fail.retry")),
                ],
                default_button="retry",
                timeout_seconds=VISION_RECOVERY_MODAL_MAX_SEC,
                cancel_event=self._avatar_plan_execution_control.cancel_event,
            )
            if response == "cancel":
                return "dismiss"
            if not self._avatar_plan_execution_control.is_cancelled():
                self._set_agent_status(AgentStatusState.WORKING)
            return "retry" if response == "retry" else "dismiss"

        def _on_header_refresh_clicked(self, _btn):
            self._sync_owner_interface_language()

            def _worker() -> None:
                self._adapter.refresh_all_console_context()

                def _finish() -> bool:
                    self._apply_locale()
                    self._refresh_dashboard(force=True)
                    self._poll_pending_command_confirmation()
                    return False

                self._glib.idle_add(_finish)

            threading.Thread(target=_worker, daemon=True).start()

        def _on_header_check_update_clicked(self, _btn):
            self._on_check_update_clicked(_btn)

        def _on_header_settings_clicked(self, _btn):
            self._stack.set_visible_child_name("updates")

        def _ensure_avatar_default_once(self):
            if self._avatar_default_applied:
                return False
            self._avatar_default_applied = True
            try:
                self._stack.set_visible_child_name("avatar")
            except Exception:
                pass
            return False

        def _on_header_profile_clicked(self, _btn):
            snap = self._adapter.read_dashboard_snapshot()
            owner = self._adapter.read_owner_display_snapshot()
            account = str(owner.get("owner_email") or owner.get("owner_name") or "-")
            device = str(owner.get("device_name") or snap.get("device_state") or "-")
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("header.profile"),
                body=self._t("dialog.profile.body").format(
                    account=account,
                    device=device,
                    version=str(snap.get("app_version") or "-"),
                    trust=str(snap.get("trust_state") or "-"),
                ),
            )
            dialog.add_response("billing", self._t("saas.upgrade"))
            dialog.add_response("ok", self._t("dialog.ok"))
            dialog.set_close_response("ok")
            dialog.set_response_appearance("billing", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_dialog, response):
                if response == "billing":
                    self._adapter.open_billing_upgrade()
                _dialog.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_toggle_fullscreen(self, _btn):
            self._is_fullscreen = not self._is_fullscreen
            if self._is_fullscreen:
                self.fullscreen()
            else:
                self.unfullscreen()
            self._header_fullscreen_btn.set_label(
                self._t("header.fullscreen.exit") if self._is_fullscreen else self._t("header.fullscreen.enter")
            )

        def _build_dashboard_page(self, gtk_module):
            box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)

            self._onboarding_banner = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=10)
            self._onboarding_banner.add_css_class("onboarding-banner")
            self._onboarding_banner.set_margin_top(4)
            self._onboarding_banner.set_margin_bottom(4)
            self._onboarding_banner.set_margin_start(6)
            self._onboarding_banner.set_margin_end(6)

            self._onboarding_label = gtk_module.Label(label=self._t("onboarding.unpaired.banner"))
            self._onboarding_label.set_wrap(True)
            self._onboarding_label.set_xalign(0.0)
            self._onboarding_label.set_hexpand(True)
            self._onboarding_label.set_margin_top(8)
            self._onboarding_label.set_margin_bottom(8)
            self._onboarding_label.set_margin_start(10)
            self._onboarding_label.set_margin_end(10)
            self._i18n_labels.append((self._onboarding_label, "onboarding.unpaired.banner"))
            self._onboarding_banner.append(self._onboarding_label)

            self._pair_device_btn = gtk_module.Button(label=self._t("onboarding.pair.button"))
            self._pair_device_btn.set_margin_top(8)
            self._pair_device_btn.set_margin_bottom(8)
            self._pair_device_btn.set_margin_start(4)
            self._pair_device_btn.set_margin_end(10)
            self._pair_device_btn.add_css_class("suggested-action")
            self._pair_device_btn.connect("clicked", lambda _btn: self._show_pairing_dialog())
            self._onboarding_banner.append(self._pair_device_btn)
            box.append(self._onboarding_banner)

            self._dashboard_labels = {}
            grid = gtk_module.Grid(column_spacing=12, row_spacing=8)
            rows = [
                ("label.runtime_state", "runtime_state"),
                ("label.trust_state", "trust_state"),
                ("label.paired", "paired"),
                ("label.device_state", "device_state"),
                ("label.last_heartbeat_at", "last_heartbeat_at"),
                ("label.app_version", "app_version"),
                ("label.backend_base_url", "backend_base_url"),
                ("label.service_status", "service_status"),
            ]
            for idx, (label_key, key) in enumerate(rows):
                label_widget = gtk_module.Label(label=self._t(label_key))
                label_widget.set_xalign(0.0)
                value_widget = gtk_module.Label(label="-")
                value_widget.set_xalign(0.0)
                self._dashboard_labels[key] = value_widget
                self._i18n_labels.append((label_widget, label_key))
                grid.attach(label_widget, 0, idx, 1, 1)
                grid.attach(value_widget, 1, idx, 1, 1)
            box.append(grid)

            actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)

            self._open_connect_btn = gtk_module.Button(label=self._t("action.open_connect"))
            open_connect = self._open_connect_btn
            open_connect.connect("clicked", lambda _btn: self._adapter.open_iuve_connect())
            actions.append(open_connect)

            self._open_logs_btn = gtk_module.Button(label=self._t("action.open_logs"))
            open_logs = self._open_logs_btn
            open_logs.connect("clicked", lambda _btn: self._adapter.open_logs())
            actions.append(open_logs)

            self._restart_btn = gtk_module.Button(label=self._t("action.restart_agent"))
            restart = self._restart_btn
            restart.connect("clicked", self._on_restart_clicked)
            actions.append(restart)

            self._export_btn = gtk_module.Button(label=self._t("action.export_diagnostics"))
            export = self._export_btn
            export.connect("clicked", self._on_export_clicked)
            actions.append(export)

            box.append(actions)

            saas_title = gtk_module.Label(label=self._t("saas.title"))
            saas_title.set_xalign(0.0)
            saas_title.add_css_class("page-title")
            saas_title.set_margin_top(12)
            box.append(saas_title)

            self._saas_labels = {}
            saas_grid = gtk_module.Grid(column_spacing=12, row_spacing=6)
            saas_rows = [
                ("saas.plan", "plan"),
                ("saas.subscription", "subscription"),
                ("saas.capabilities", "capabilities"),
                ("saas.usage", "usage"),
                ("saas.providers", "providers"),
            ]
            for idx, (label_key, key) in enumerate(saas_rows):
                label_widget = gtk_module.Label(label=self._t(label_key))
                label_widget.set_xalign(0.0)
                value_widget = gtk_module.Label(label="-")
                value_widget.set_xalign(0.0)
                value_widget.set_wrap(True)
                self._saas_labels[key] = value_widget
                self._i18n_labels.append((label_widget, label_key))
                saas_grid.attach(label_widget, 0, idx, 1, 1)
                saas_grid.attach(value_widget, 1, idx, 1, 1)
            box.append(saas_grid)

            saas_actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._saas_refresh_btn = gtk_module.Button(label=self._t("saas.refresh"))
            self._saas_refresh_btn.connect("clicked", self._on_saas_refresh_clicked)
            saas_actions.append(self._saas_refresh_btn)
            self._saas_upgrade_btn = gtk_module.Button(label=self._t("saas.upgrade"))
            self._saas_upgrade_btn.add_css_class("suggested-action")
            self._saas_upgrade_btn.connect("clicked", self._on_saas_upgrade_clicked)
            saas_actions.append(self._saas_upgrade_btn)
            box.append(saas_actions)

            return box

        def _build_avatar_page(self, gtk_module):
            page = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=10)
            page.set_margin_top(8)
            page.set_margin_bottom(8)
            page.set_margin_start(8)
            page.set_margin_end(8)

            self._avatar_status_header = gtk_module.Label(label=self._t("avatar.title"))
            self._avatar_status_header.set_xalign(0.0)
            self._avatar_status_header.add_css_class("page-title")
            page.append(self._avatar_status_header)

            self._avatar_intro_label = gtk_module.Label(label=self._t("avatar.intro"))
            self._avatar_intro_label.set_xalign(0.0)
            self._avatar_intro_label.set_wrap(True)
            page.append(self._avatar_intro_label)

            self._avatar_onboarding_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._avatar_onboarding_row.add_css_class("onboarding-banner")
            self._avatar_onboarding_label = gtk_module.Label(label=self._t("avatar.unpaired.banner"))
            self._avatar_onboarding_label.set_xalign(0.0)
            self._avatar_onboarding_label.set_wrap(True)
            self._avatar_onboarding_label.set_hexpand(True)
            self._avatar_onboarding_row.append(self._avatar_onboarding_label)

            self._avatar_pair_device_btn = gtk_module.Button(label=self._t("avatar.unpaired.pair"))
            self._avatar_pair_device_btn.add_css_class("suggested-action")
            self._avatar_pair_device_btn.connect("clicked", lambda _btn: self._show_pairing_dialog())
            self._avatar_onboarding_row.append(self._avatar_pair_device_btn)

            self._avatar_onboarding_permissions_btn = gtk_module.Button(label=self._t("avatar.unpaired.permissions"))
            self._avatar_onboarding_permissions_btn.connect("clicked", self._on_avatar_open_permissions_clicked)
            self._avatar_onboarding_row.append(self._avatar_onboarding_permissions_btn)

            self._avatar_onboarding_diagnostics_btn = gtk_module.Button(label=self._t("avatar.unpaired.diagnostics"))
            self._avatar_onboarding_diagnostics_btn.connect("clicked", self._on_avatar_open_diagnostics_clicked)
            self._avatar_onboarding_row.append(self._avatar_onboarding_diagnostics_btn)
            page.append(self._avatar_onboarding_row)

            content = gtk_module.Paned.new(gtk_module.Orientation.HORIZONTAL)
            content.set_wide_handle(True)
            content.set_hexpand(True)
            content.set_vexpand(True)

            chat_col = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            chat_col.set_hexpand(True)
            chat_col.set_vexpand(True)

            def _focus_avatar_input(gesture, _n_press, x, y) -> None:
                widget = gesture.get_widget()
                try:
                    picked = widget.pick(x, y, gtk_module.PickFlags.DEFAULT)
                except Exception:
                    picked = None
                node = picked
                while node is not None:
                    if isinstance(node, gtk_module.Button):
                        return
                    node = node.get_parent() if hasattr(node, "get_parent") else None
                if hasattr(self, "_avatar_input_view"):
                    self._avatar_input_view.grab_focus()

            chat_click = gtk_module.GestureClick()
            chat_click.connect("pressed", _focus_avatar_input)
            chat_col.add_controller(chat_click)

            self._avatar_conversation_title = gtk_module.Label(label=self._t("avatar.conversation.title"))
            self._avatar_conversation_title.set_xalign(0.0)
            self._avatar_conversation_title.add_css_class("heading")
            chat_col.append(self._avatar_conversation_title)

            self._avatar_timeline_chip = gtk_module.Label(label="")
            self._avatar_timeline_chip.set_xalign(0.0)
            self._avatar_timeline_chip.set_wrap(True)
            self._avatar_timeline_chip.add_css_class("avatar-model-chip")
            self._avatar_timeline_chip.set_visible(False)
            chat_col.append(self._avatar_timeline_chip)

            self._avatar_conversation_view = gtk_module.TextView()
            self._avatar_conversation_view.set_editable(False)
            self._avatar_conversation_view.set_cursor_visible(False)
            self._avatar_conversation_view.set_wrap_mode(gtk_module.WrapMode.WORD_CHAR)
            self._avatar_conversation_view.add_css_class("chat-conversation")
            self._avatar_conversation_buffer = self._avatar_conversation_view.get_buffer()
            self._avatar_chat_tags_ready = False

            conversation_scroll = gtk_module.ScrolledWindow()
            conversation_scroll.set_policy(gtk_module.PolicyType.NEVER, gtk_module.PolicyType.AUTOMATIC)
            conversation_scroll.set_hexpand(True)
            conversation_scroll.set_vexpand(True)
            conversation_scroll.set_min_content_height(220)
            conversation_scroll.set_max_content_height(520)
            conversation_scroll.set_child(self._avatar_conversation_view)
            self._avatar_conversation_scroll = conversation_scroll
            chat_col.append(conversation_scroll)

            action_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._avatar_open_permissions_btn = gtk_module.Button(label=self._t("avatar.action.open_permissions"))
            self._avatar_open_permissions_btn.connect("clicked", self._on_avatar_open_permissions_clicked)
            action_row.append(self._avatar_open_permissions_btn)

            self._avatar_open_diagnostics_btn = gtk_module.Button(label=self._t("avatar.action.open_diagnostics"))
            self._avatar_open_diagnostics_btn.connect("clicked", self._on_avatar_open_diagnostics_clicked)
            action_row.append(self._avatar_open_diagnostics_btn)

            self._avatar_upgrade_tariff_btn = gtk_module.Button(label=self._t("saas.upgrade"))
            self._avatar_upgrade_tariff_btn.add_css_class("suggested-action")
            self._avatar_upgrade_tariff_btn.connect("clicked", self._on_saas_upgrade_clicked)
            self._avatar_upgrade_tariff_btn.set_visible(False)
            action_row.append(self._avatar_upgrade_tariff_btn)

            self._avatar_cancel_btn = gtk_module.Button(label=self._t("avatar.action.cancel"))
            self._avatar_cancel_btn.connect("clicked", self._on_avatar_cancel_clicked)
            action_row.append(self._avatar_cancel_btn)
            chat_col.append(action_row)

            self._avatar_suggested_action_row = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            self._avatar_suggested_action_row.add_css_class("onboarding-banner")
            self._avatar_suggested_action_row.set_visible(False)
            self._avatar_suggested_action_title = gtk_module.Label(label="")
            self._avatar_suggested_action_title.set_xalign(0.0)
            self._avatar_suggested_action_title.add_css_class("heading")
            self._avatar_suggested_action_row.append(self._avatar_suggested_action_title)
            self._avatar_suggested_action_body = gtk_module.Label(label="")
            self._avatar_suggested_action_body.set_xalign(0.0)
            self._avatar_suggested_action_body.set_wrap(True)
            self._avatar_suggested_action_row.append(self._avatar_suggested_action_body)
            suggested_btn_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._avatar_plan_spinner = gtk_module.Spinner()
            self._avatar_plan_spinner.set_visible(False)
            suggested_btn_row.append(self._avatar_plan_spinner)
            self._avatar_execute_btn = gtk_module.Button(label=self._t("avatar.action.execute"))
            self._avatar_execute_btn.add_css_class("suggested-action")
            self._avatar_execute_btn.connect("clicked", self._on_avatar_execute_clicked)
            suggested_btn_row.append(self._avatar_execute_btn)
            self._avatar_retry_step_btn = gtk_module.Button(label=self._t("avatar.action.retry_step"))
            self._avatar_retry_step_btn.add_css_class("suggested-action")
            self._avatar_retry_step_btn.connect("clicked", self._on_avatar_retry_step_clicked)
            self._avatar_retry_step_btn.set_visible(False)
            suggested_btn_row.append(self._avatar_retry_step_btn)
            self._avatar_suggested_action_row.append(suggested_btn_row)
            chat_col.append(self._avatar_suggested_action_row)
            self._avatar_pending_action: AvatarSuggestedAction | None = None

            self._avatar_session_proposal_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._avatar_session_proposal_row.add_css_class("onboarding-banner")
            self._avatar_session_proposal_row.set_visible(False)
            self._avatar_session_start_btn = gtk_module.Button(label=self._t("avatar.action.session_start"))
            self._avatar_session_start_btn.add_css_class("suggested-action")
            self._avatar_session_start_btn.connect("clicked", self._on_avatar_session_start_clicked)
            self._avatar_session_proposal_row.append(self._avatar_session_start_btn)
            self._avatar_session_skip_btn = gtk_module.Button(label=self._t("avatar.action.session_skip"))
            self._avatar_session_skip_btn.connect("clicked", self._on_avatar_session_skip_clicked)
            self._avatar_session_proposal_row.append(self._avatar_session_skip_btn)
            chat_col.append(self._avatar_session_proposal_row)

            self._avatar_chat_input_bar: AvatarChatInputBar = build_avatar_chat_input_bar(
                gtk_module,
                translate=self._t,
                on_send=self._on_avatar_send_clicked,
                on_stop=self._on_avatar_stop_clicked,
                on_mic=self._on_avatar_mic_clicked,
                on_attach=self._on_avatar_attach_clicked,
                on_tag_dismiss=self._on_avatar_tag_dismiss_clicked,
                on_mode_changed=self._on_avatar_chat_mode_changed,
            )
            self._avatar_input_view = self._avatar_chat_input_bar.input_view
            self._avatar_input_buffer = self._avatar_chat_input_bar.input_buffer
            self._avatar_input_scroll = self._avatar_chat_input_bar.input_scroll
            self._avatar_mic_btn = self._avatar_chat_input_bar.mic_btn
            self._avatar_send_btn = self._avatar_chat_input_bar.send_btn
            self._avatar_stop_btn = self._avatar_chat_input_bar.stop_btn
            self._avatar_input_buffer.connect("changed", self._on_avatar_input_changed)
            self._on_avatar_input_changed(self._avatar_input_buffer)
            chat_col.append(self._avatar_chat_input_bar.root)

            install_avatar_chat_file_drop(
                self._avatar_chat_input_bar,
                gtk_module,
                on_files_dropped=self._on_avatar_files_dropped,
            )

            try:
                from gi.repository import Gdk  # type: ignore

                key_controller = gtk_module.EventControllerKey.new()
                key_controller.connect("key-pressed", self._on_avatar_input_key_pressed)
                self._avatar_input_view.add_controller(key_controller)
                self._avatar_input_key_return = Gdk.KEY_Return
                self._avatar_input_key_kp_enter = Gdk.KEY_KP_Enter
                self._avatar_input_shift_mask = Gdk.ModifierType.SHIFT_MASK
            except Exception:
                pass

            self._avatar_accuracy_disclaimer = gtk_module.Label(
                label=self._t("avatar.chat.accuracy_disclaimer")
            )
            self._avatar_accuracy_disclaimer.set_xalign(0.0)
            self._avatar_accuracy_disclaimer.set_wrap(True)
            self._avatar_accuracy_disclaimer.set_vexpand(False)
            self._avatar_accuracy_disclaimer.add_css_class("avatar-chat-accuracy-disclaimer")
            chat_col.append(self._avatar_accuracy_disclaimer)

            self._avatar_advisory_note = gtk_module.Label(label=self._t("avatar.advisory.only"))
            self._avatar_advisory_note.set_xalign(0.0)
            self._avatar_advisory_note.set_wrap(True)
            self._avatar_advisory_note.set_vexpand(False)
            chat_col.append(self._avatar_advisory_note)

            context_scroll = gtk_module.ScrolledWindow()
            context_scroll.set_policy(gtk_module.PolicyType.NEVER, gtk_module.PolicyType.AUTOMATIC)
            context_scroll.set_hexpand(False)
            context_scroll.set_vexpand(True)
            context_scroll.set_size_request(240, -1)
            context_scroll.set_min_content_width(240)

            self._avatar_context_frame = gtk_module.Frame()
            self._avatar_context_frame.set_hexpand(False)
            self._avatar_context_frame.set_vexpand(True)
            context_col = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            context_col.set_margin_top(8)
            context_col.set_margin_bottom(8)
            context_col.set_margin_start(8)
            context_col.set_margin_end(8)
            self._avatar_context_frame.set_child(context_col)
            context_scroll.set_child(self._avatar_context_frame)

            self._avatar_context_title = gtk_module.Label(label=self._t("avatar.device_context.title"))
            self._avatar_context_title.set_xalign(0.0)
            self._avatar_context_title.add_css_class("heading")
            context_col.append(self._avatar_context_title)

            self._avatar_context_labels: dict[str, object] = {}
            self._avatar_context_rows: dict[str, object] = {}
            for label_key, key in [
                ("avatar.device_context.cpu", "cpu"),
                ("avatar.device_context.ram", "ram"),
                ("avatar.device_context.disk", "disk"),
                ("avatar.device_context.os", "os"),
                ("avatar.device_context.apps", "apps"),
                ("avatar.device_context.scan", "scan"),
                ("avatar.device_context.trust", "trust"),
                ("avatar.device_context.capabilities", "capabilities"),
                ("avatar.device_context.root", "root"),
            ]:
                row = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=2)
                title = gtk_module.Label(label=f"{self._t(label_key)}:")
                title.set_xalign(0.0)
                title.set_halign(gtk_module.Align.START)
                self._i18n_labels.append((title, label_key))
                value = gtk_module.Label(label="-")
                value.set_xalign(0.0)
                value.set_halign(gtk_module.Align.START)
                value.set_wrap(True)
                value.set_max_width_chars(28)
                value.set_selectable(True)
                self._avatar_context_labels[key] = value
                self._avatar_context_rows[key] = row
                row.append(title)
                row.append(value)
                context_col.append(row)

            content.set_start_child(chat_col)
            content.set_resize_start_child(True)
            content.set_shrink_start_child(False)
            content.set_end_child(context_scroll)
            content.set_resize_end_child(False)
            content.set_shrink_end_child(False)
            content.set_position(820)
            page.append(content)

            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t("avatar.chat.welcome"),
                role="avatar",
            )
            self._adapter.set_device_capability_scan_callback(self._on_device_profile_updated)
            self._adapter.ensure_device_capability_scan(reason="avatar_page_open")
            self._refresh_avatar_context()
            self._refresh_avatar_chat_input_bar()
            return page

        def _ensure_avatar_chat_tags(self) -> None:
            if getattr(self, "_avatar_chat_tags_ready", False):
                return
            buf = self._avatar_conversation_buffer
            try:
                from gi.repository import Pango  # type: ignore

                buf.create_tag("avatar-speaker", weight=Pango.Weight.BOLD, foreground="#0b5fff")
                buf.create_tag("user-speaker", weight=Pango.Weight.BOLD, foreground="#1e293b")
                buf.create_tag("system-speaker", weight=Pango.Weight.BOLD, foreground="#64748b")
                buf.create_tag("avatar-body", foreground="#334155")
                buf.create_tag("user-body", foreground="#0f172a")
                buf.create_tag("system-body", foreground="#64748b", style=Pango.Style.ITALIC)
            except Exception:
                buf.create_tag("avatar-speaker")
                buf.create_tag("user-speaker")
                buf.create_tag("system-speaker")
                buf.create_tag("avatar-body")
                buf.create_tag("user-body")
                buf.create_tag("system-body")
            self._avatar_chat_tags_ready = True

        def _avatar_append_message(self, speaker: str, text: str, *, role: str = "avatar"):
            if not hasattr(self, "_avatar_conversation_buffer"):
                return
            self._ensure_avatar_chat_tags()
            buf = self._avatar_conversation_buffer
            end = buf.get_end_iter()
            if buf.get_char_count() > 0:
                buf.insert(end, "\n\n")
            speaker_tag = {
                "avatar": "avatar-speaker",
                "user": "user-speaker",
                "system": "system-speaker",
            }.get(role, "avatar-speaker")
            body_tag = {
                "avatar": "avatar-body",
                "user": "user-body",
                "system": "system-body",
            }.get(role, "avatar-body")
            offset_start = buf.get_char_count()
            body = text.strip()
            buf.insert(buf.get_end_iter(), f"{speaker}\n{body}")
            speaker_end = offset_start + len(speaker) + 1
            body_end = offset_start + len(speaker) + 1 + len(body)
            buf.apply_tag_by_name(
                speaker_tag,
                buf.get_iter_at_offset(offset_start),
                buf.get_iter_at_offset(speaker_end),
            )
            buf.apply_tag_by_name(
                body_tag,
                buf.get_iter_at_offset(speaker_end),
                buf.get_iter_at_offset(body_end),
            )
            self._trim_avatar_conversation_if_needed()
            self._avatar_scroll_conversation_to_end()

        def _trim_avatar_conversation_if_needed(self) -> None:
            if not hasattr(self, "_avatar_conversation_buffer"):
                return
            buf = self._avatar_conversation_buffer
            text = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), True)
            if not text:
                return
            parts = [part for part in text.split("\n\n") if part.strip()]
            if len(parts) <= AVATAR_CHAT_MAX_MESSAGES:
                return
            trimmed = "\n\n".join(parts[-AVATAR_CHAT_MAX_MESSAGES:])
            buf.set_text(trimmed)
            self._avatar_thinking_marker = None

        def _set_avatar_send_busy(self, busy: bool) -> None:
            self._avatar_send_in_flight = bool(busy)
            if hasattr(self, "_avatar_send_btn"):
                self._avatar_send_btn.set_sensitive(not busy)
            if hasattr(self, "_avatar_mic_btn"):
                self._avatar_mic_btn.set_sensitive(not busy)
            application = self.get_application()
            if application is not None and hasattr(application, "set_busy"):
                try:
                    application.set_busy(busy)
                except Exception:
                    pass
            self._refresh_avatar_chat_input_bar()

        def _avatar_show_thinking(self) -> None:
            if not hasattr(self, "_avatar_conversation_buffer"):
                return
            self._avatar_thinking_marker = self._avatar_conversation_buffer.get_char_count()
            thinking_key = self._avatar_thinking_message_key()
            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t(thinking_key),
            )

        def _avatar_thinking_message_key(self) -> str:
            snapshot = getattr(self, "_orchestra_status_snapshot", None)
            if snapshot is not None:
                from ..orchestra_routing_tracker import (
                    last_routing_path,
                    resolve_effective_orchestra_state,
                )

                effective = resolve_effective_orchestra_state(snapshot, last_path=last_routing_path())
                if effective.value == "cloud_active":
                    return "avatar.chat.response.thinking_cloud"
            return "avatar.chat.response.thinking"

        def _avatar_clear_thinking(self) -> None:
            marker = getattr(self, "_avatar_thinking_marker", None)
            if marker is None or not hasattr(self, "_avatar_conversation_buffer"):
                return
            buf = self._avatar_conversation_buffer
            start = buf.get_iter_at_offset(int(marker))
            end = buf.get_end_iter()
            buf.delete(start, end)
            self._avatar_thinking_marker = None

        def _avatar_scroll_conversation_to_end(self) -> None:
            scroll = getattr(self, "_avatar_conversation_scroll", None)
            if scroll is None:
                return

            def _scroll() -> bool:
                adj = scroll.get_vadjustment()
                if adj is None:
                    return False
                upper = adj.get_upper()
                page = adj.get_page_size()
                adj.set_value(chat_scroll_bottom_value(upper=upper, page_size=page))
                return False

            self._glib.idle_add(_scroll)

        def _avatar_input_text(self) -> str:
            if not hasattr(self, "_avatar_input_buffer"):
                return ""
            start = self._avatar_input_buffer.get_start_iter()
            end = self._avatar_input_buffer.get_end_iter()
            return str(self._avatar_input_buffer.get_text(start, end, True) or "").strip()

        def _avatar_clear_input(self) -> None:
            if hasattr(self, "_avatar_input_buffer"):
                self._avatar_input_buffer.set_text("")

        def _avatar_append_input_text(self, text: str) -> None:
            if not hasattr(self, "_avatar_input_buffer"):
                return
            existing = self._avatar_input_text()
            merged = f"{existing}\n{text}".strip() if existing else str(text or "").strip()
            self._avatar_input_buffer.set_text(merged)

        def _on_avatar_input_changed(self, _buffer, _pspec=None) -> None:
            if not hasattr(self, "_avatar_chat_input_bar"):
                return
            text = self._avatar_input_text()
            refresh_input_viewport(self._avatar_chat_input_bar, text)
            refresh_placeholder_visibility(self._avatar_chat_input_bar, has_text=bool(text))

        def _avatar_active_session_count(self) -> int:
            try:
                return len(self._adapter.list_active_action_sessions())
            except Exception:
                return 0

        def _avatar_session_tag_title(self) -> str:
            if self._avatar_chat_tag_dismissed:
                return ""
            preset_key = str(self._avatar_plan_session_preset or "").strip()
            if preset_key and self._avatar_plan_session_state in {"active", "pending"}:
                return self._t(f"cap.action_sessions.preset.{preset_key}.user_title")
            sessions = self._adapter.list_active_action_sessions()
            if sessions:
                first = sessions[0]
                preset = str(first.get("preset") or first.get("preset_key") or "").strip()
                if preset:
                    return self._t(f"cap.action_sessions.preset.{preset}.user_title")
                return str(first.get("title") or "").strip()
            return ""

        def _refresh_avatar_chat_input_bar(self) -> None:
            if not hasattr(self, "_avatar_chat_input_bar"):
                return
            bar = self._avatar_chat_input_bar
            plan_running = bool(getattr(self, "_avatar_plan_running", False))
            send_busy = bool(getattr(self, "_avatar_send_in_flight", False))
            refresh_working_badge(
                bar,
                plan_running=plan_running or send_busy,
                active_session_count=self._avatar_active_session_count(),
                translate=self._t,
            )
            refresh_primary_button(bar, plan_running=plan_running)
            if hasattr(bar, "send_btn"):
                bar.send_btn.set_sensitive(not plan_running and not send_busy)
            refresh_tag_chip(
                bar,
                plan_running=plan_running and not self._avatar_chat_tag_dismissed,
                session_title=self._avatar_session_tag_title(),
                translate=self._t,
            )
            refresh_mode_button(bar, mode=self._avatar_chat_mode, translate=self._t)
            bar.attach_btn.set_tooltip_text(self._t("avatar.input.attach"))
            bar.mic_btn.set_tooltip_text(self._t("avatar.input.voice"))
            bar.send_btn.set_tooltip_text(self._t("avatar.action.send"))
            bar.stop_btn.set_tooltip_text(self._t("avatar.action.stop"))
            bar.tag_dismiss_btn.set_tooltip_text(self._t("avatar.chat.tag.dismiss"))
            bar.mode_btn.set_tooltip_text(self._t("avatar.chat.mode.hint"))
            bar.placeholder_label.set_label(self._t("avatar.input.placeholder"))

        def _on_avatar_chat_mode_changed(self, mode: str) -> None:
            normalized = str(mode or "auto").strip().lower()
            if normalized not in {"auto", "local", "cloud_assisted"}:
                normalized = "auto"
            self._avatar_chat_mode = normalized
            self._refresh_avatar_chat_input_bar()

        def _on_avatar_attach_clicked(self, _btn) -> None:
            self._on_avatar_open_permissions_clicked(_btn)

        def _on_avatar_files_dropped(self, paths: list[str]) -> None:
            from .avatar_file_drop import process_dropped_file

            for raw_path in paths:
                result = process_dropped_file(raw_path)
                message = safe_format_template(
                    self._t(result.message_key),
                    **result.message_params,
                )
                role = "user" if result.ok else "system"
                speaker = self._t("avatar.chat.user") if result.ok else self._t("avatar.chat.system")
                self._avatar_append_message(speaker, message, role=role)

        def _on_avatar_clipboard_paste(self) -> bool:
            """Ctrl/Cmd+V into chat: text → input; images/files → same attach path as drop."""
            try:
                from gi.repository import Gdk  # type: ignore
            except ImportError:
                return False
            try:
                display = self._gtk.Display.get_default() if hasattr(self, "_gtk") else None
                if display is None:
                    from gi.repository import Gtk as gtk_mod  # type: ignore

                    display = gtk_mod.Display.get_default()
                if display is None:
                    return False
                clipboard = display.get_clipboard()
            except Exception:
                return False

            from .avatar_chat_clipboard import (
                apply_clipboard_paste_plan,
                insert_text_into_buffer,
                parse_uri_list,
                plan_clipboard_paste,
                save_image_bytes_to_temp,
            )

            def _apply_plan(plan) -> None:
                apply_clipboard_paste_plan(
                    plan,
                    on_insert_text=lambda t: insert_text_into_buffer(
                        getattr(self, "_avatar_input_buffer", None),
                        t,
                    ),
                    on_files=self._on_avatar_files_dropped,
                )

            # Prefer file list / uri-list, then texture/image, then plain text.
            try:
                formats = clipboard.get_formats()
            except Exception:
                formats = None

            def _try_text() -> None:
                def _on_text(_source, result, _ud=None):
                    try:
                        text = clipboard.read_text_finish(result)
                    except Exception:
                        text = None
                    if text:
                        _apply_plan(plan_clipboard_paste(text=str(text)))

                try:
                    clipboard.read_text_async(None, _on_text, None)
                except Exception:
                    pass

            def _try_texture_then_text() -> None:
                def _on_texture(_source, result, _ud=None):
                    try:
                        texture = clipboard.read_texture_finish(result)
                    except Exception:
                        texture = None
                    if texture is not None:
                        try:
                            # Gdk.Texture → PNG bytes via save_to_png_bytes when available
                            if hasattr(texture, "save_to_png_bytes"):
                                gbytes = texture.save_to_png_bytes()
                                data = bytes(gbytes.get_data()) if gbytes is not None else b""
                            else:
                                data = b""
                            if data:
                                path = save_image_bytes_to_temp(data, suffix=".png")
                                if path:
                                    _apply_plan(plan_clipboard_paste(file_paths=[path]))
                                    return
                        except Exception:
                            pass
                    _try_text()

                try:
                    if formats is not None and hasattr(formats, "contain_gtype"):
                        # Fall through to text when no texture
                        pass
                    clipboard.read_texture_async(None, _on_texture, None)
                except Exception:
                    _try_text()

            def _try_files_then_image() -> None:
                def _on_value(_source, result, _ud=None):
                    try:
                        value = clipboard.read_value_finish(result)
                    except Exception:
                        value = None
                    paths: list[str] = []
                    if value is not None and hasattr(value, "get_files"):
                        for file_obj in value.get_files() or []:
                            path = file_obj.get_path()
                            if path:
                                paths.append(str(path))
                    if paths:
                        _apply_plan(plan_clipboard_paste(file_paths=paths))
                        return
                    _try_texture_then_text()

                try:
                    from gi.repository import Gdk as GdkMod  # type: ignore

                    clipboard.read_value_async(GdkMod.FileList, 0, None, _on_value, None)
                except Exception:
                    # uri-list string fallback then texture/text
                    def _on_uri(_source, result, _ud=None):
                        try:
                            raw = clipboard.read_text_finish(result)
                        except Exception:
                            raw = None
                        paths = parse_uri_list(raw)
                        if paths:
                            _apply_plan(plan_clipboard_paste(file_paths=paths))
                            return
                        _try_texture_then_text()

                    try:
                        clipboard.read_text_async(None, _on_uri, None)
                    except Exception:
                        _try_texture_then_text()

            _try_files_then_image()
            return True

        def _on_avatar_tag_dismiss_clicked(self, _btn) -> None:
            self._avatar_chat_tag_dismissed = True
            self._refresh_avatar_chat_input_bar()

        def _on_avatar_stop_clicked(self, _btn) -> None:
            if not getattr(self, "_avatar_plan_running", False):
                return
            if getattr(self, "_avatar_plan_finish_handled", False):
                return
            self._avatar_plan_cancel_requested = True
            self._avatar_plan_execution_control.request_cancel()
            self._avatar_plan_finish_handled = True
            self._avatar_plan_running = False
            self._set_avatar_plan_controls_busy(False)
            self._refresh_avatar_chat_input_bar()
            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t("avatar.chat.response.plan_cancelled"),
            )
            finished_plan = getattr(self, "_avatar_pending_plan", None)
            self._avatar_pending_plan = None
            self._update_avatar_plan_preview(None)
            if finished_plan:
                self._revoke_plan_bound_session(finished_plan, reason="plan_cancelled")
            self._reset_avatar_plan_session_state()
            self._refresh_command_log_panel()
            self._refresh_avatar_context()

        def _on_avatar_input_key_pressed(self, _controller, keyval, _keycode, state) -> bool:
            try:
                from gi.repository import Gdk  # type: ignore

                key_v = getattr(Gdk, "KEY_v", None) or getattr(Gdk, "KEY_V", None)
                ctrl = int(Gdk.ModifierType.CONTROL_MASK)
                meta = int(getattr(Gdk.ModifierType, "META_MASK", 0) or 0)
                super_mask = int(getattr(Gdk.ModifierType, "SUPER_MASK", 0) or 0)
                from .avatar_chat_clipboard import is_paste_keyval, paste_modifier_matches

                if key_v and is_paste_keyval(keyval, key_v=key_v) and paste_modifier_matches(
                    state=int(state),
                    control_mask=ctrl,
                    meta_mask=meta | super_mask,
                ):
                    return bool(self._on_avatar_clipboard_paste())
            except Exception:
                pass
            if keyval not in (
                getattr(self, "_avatar_input_key_return", None),
                getattr(self, "_avatar_input_key_kp_enter", None),
            ):
                return False
            if state & getattr(self, "_avatar_input_shift_mask", 0):
                return False
            self._on_avatar_send_clicked(None)
            return True

        def _on_avatar_send_clicked(self, _btn):
            if not hasattr(self, "_avatar_input_buffer"):
                return
            if getattr(self, "_avatar_send_in_flight", False):
                return
            user_text = self._avatar_input_text()
            if not user_text:
                return

            self._avatar_append_message(self._t("avatar.chat.user"), user_text, role="user")
            self._avatar_clear_input()
            self._avatar_auto_execute_blocked = False
            self._set_avatar_send_busy(True)
            self._avatar_show_thinking()

            def _worker() -> None:
                try:
                    result = self._build_avatar_chat_response(user_text)
                    error = ""
                except Exception as exc:
                    result = None
                    error = str(exc or "unknown_error")

                def _finish() -> bool:
                    self._avatar_clear_thinking()
                    self._set_avatar_send_busy(False)
                    if error:
                        self._avatar_append_message(
                            self._t("avatar.chat.avatar"),
                            safe_format_template(
                                self._t("avatar.chat.response.chat_error"),
                                reason=error,
                            ),
                        )
                        self._refresh_avatar_context(force=True)
                        self._refresh_orchestra_status_async()
                        return False
                    if result is not None:
                        self._apply_avatar_chat_response(result)
                        chat_result = result.get("chat_result")
                        if chat_result is not None and chat_result.response_key in {
                            "avatar.chat.response.llm_unavailable_cloud",
                            "avatar.chat.response.chat_error",
                        }:
                            self._refresh_orchestra_status_async()
                    return False

                self._glib.idle_add(_finish)

            threading.Thread(target=_worker, daemon=True).start()

        def _apply_avatar_chat_response(self, result: dict) -> None:
            timeline_block = str(result.get("timeline_block") or "").strip()
            if timeline_block:
                self._avatar_append_message(self._t("avatar.chat.system"), timeline_block, role="system")
            self._update_avatar_model_chip(str(result.get("model_chip") or ""))
            self._update_avatar_pipeline_chip(str(result.get("pipeline_chip") or ""))
            body = str(result.get("response") or "")
            diagnostic = str(result.get("diagnostic_snippet") or "").strip()
            if diagnostic:
                body = f"{body}\n\n{diagnostic}"
            self._avatar_append_message(self._t("avatar.chat.avatar"), body)
            self._update_avatar_billing_upgrade_cta(
                result["chat_result"].response_key == "avatar.chat.response.permission_blocked_plan"
            )
            if result["plan_preview"]:
                chat_result = result["chat_result"]
                if getattr(chat_result, "plan_resume", False):
                    self._avatar_resume_step_index = int(getattr(chat_result, "resume_step_index", 0) or 0)
                self._update_avatar_plan_preview(result["execution_plan"])
                self._maybe_propose_plan_action_session(
                    result["execution_plan"],
                    trust_state=str(result["snap"].get("trust_state") or ""),
                )
            else:
                self._update_avatar_suggested_action(
                    result["suggested_action"] if not result["advisory_only"] else None
                )
            self._refresh_avatar_context()
            self._maybe_auto_execute_avatar(
                chat_result=result["chat_result"],
                snap=result["snap"],
                ui_enabled=result["ui_enabled"],
            )
            chat_result = result["chat_result"]
            if chat_result.response_key in {
                "avatar.chat.response.permission_required",
                "avatar.chat.response.permission_blocked_plan",
            }:
                self._present_permission_required_modal(chat_result.required_capability)

        def _build_avatar_chat_response(self, user_text: str) -> dict:
            snap = self._adapter.read_dashboard_snapshot()
            ui_enabled = self._adapter.read_ui_capability_enabled_map()
            enabled = sum(1 for value in ui_enabled.values() if value)

            from iuve_connect_agents.shared.avatar_llm_intent import resolve_avatar_intent_for_chat

            intent_resolution = resolve_avatar_intent_for_chat(
                user_text,
                trust_state=str(snap.get("trust_state") or ""),
            )
            from ..active.capability_resolver import resolve_workspace_routing_mode
            from ..active.orchestra_decision_client import (
                fetch_orchestra_decision,
                should_use_orchestra_decision_first,
            )
            from .avatar_chat import (
                attach_orchestra_recovery_to_plan,
                build_orchestra_policy_hints_for_text,
            )
            from .status_adapter import ExecutionPlanFetchResult

            routing_mode = resolve_workspace_routing_mode(trust_state=str(snap.get("trust_state") or ""))
            intent_label = str(getattr(intent_resolution, "llm_intent", "") or intent_resolution.request_type or "")

            orchestra_decision_payload: dict | None = None
            plan_fetch: ExecutionPlanFetchResult
            prior_text = getattr(self, "_avatar_prior_user_text", None)
            orchestra_policy_hints = build_orchestra_policy_hints_for_text(
                user_text,
                prior_user_text=prior_text,
            )

            if should_use_orchestra_decision_first():
                decision_result = fetch_orchestra_decision(
                    user_text,
                    trust_state=str(snap.get("trust_state") or ""),
                    policy_hints=orchestra_policy_hints or None,
                )
                if decision_result.ok:
                    contract = (
                        decision_result.payload.get("execution_contract")
                        if isinstance(decision_result.payload.get("execution_contract"), dict)
                        else {}
                    )
                    orchestra_decision_payload = {
                        "ok": True,
                        "decision": decision_result.decision,
                        "execution_mode": decision_result.execution_mode,
                        "needs_planner": decision_result.needs_planner,
                        "planner_type": decision_result.planner_type,
                        "answer": decision_result.answer,
                        "execution_contract": contract,
                        "recovery_hints": decision_result.recovery_hints,
                        "replan_recommended": decision_result.replan_recommended,
                    }
                    if decision_result.execution_plan:
                        enriched_plan = attach_orchestra_recovery_to_plan(
                            dict(decision_result.execution_plan),
                            orchestra_decision=orchestra_decision_payload,
                            recovery_hints=decision_result.recovery_hints,
                        )
                        plan_fetch = ExecutionPlanFetchResult(
                            plan=enriched_plan,
                            planner_mode=str(contract.get("planner_mode") or "orchestra_decision"),
                        )
                    elif decision_result.decision == "answer_only":
                        plan_fetch = ExecutionPlanFetchResult()
                    else:
                        plan_fetch = self._adapter.fetch_execution_plan_v2(
                            user_text,
                            is_compound_hint=intent_resolution.compound.is_compound,
                        )
                else:
                    plan_fetch = self._adapter.fetch_execution_plan_v2(
                        user_text,
                        is_compound_hint=intent_resolution.compound.is_compound,
                    )
            else:
                plan_fetch = self._adapter.fetch_execution_plan_v2(
                    user_text,
                    is_compound_hint=intent_resolution.compound.is_compound,
                )

            chat_result = build_avatar_chat_result(
                user_text,
                trust_state=str(snap.get("trust_state") or ""),
                capabilities_enabled=enabled,
                ui_capability_enabled=ui_enabled,
                plan_blocked_ui_keys=self._adapter.read_plan_blocked_ui_keys(),
                cloud_suggestions=self._adapter.read_cloud_suggested_actions(),
                execution_plan=plan_fetch.plan,
                plan_fetch_error=plan_fetch.error,
                planner_mode=plan_fetch.planner_mode,
                prior_user_text=getattr(self, "_avatar_prior_user_text", None),
                orchestra_decision=orchestra_decision_payload,
            )
            self._avatar_prior_user_text = user_text

            from ..orchestra_routing_tracker import last_routing_model, last_routing_path
            from ..orchestra_status_service import OrchestraStatusState

            orchestra_snapshot = getattr(self, "_orchestra_status_snapshot", None)
            orchestra_state = (
                str(orchestra_snapshot.state.value)
                if orchestra_snapshot is not None
                else OrchestraStatusState.OFFLINE.value
            )
            orchestra_detail = str(getattr(orchestra_snapshot, "detail", "") or "") if orchestra_snapshot else ""
            model_chip = format_model_chip(
                lang=self._lang,
                route_path=last_routing_path().value,
                planner_mode=plan_fetch.planner_mode,
                orchestra_state=orchestra_state,
                local_model=last_routing_model(),
            )
            timeline_events = build_chat_timeline_events(
                intent_label=intent_label,
                routing_mode=routing_mode,
                planner_mode=plan_fetch.planner_mode,
                plan_fetch_error=plan_fetch.error or chat_result.plan_fetch_error,
                plan_preview=chat_result.plan_preview,
                advisory_consultation=(
                    chat_result.response_key == "avatar.chat.response.advisory_consultation_required"
                ),
                orchestra_detail=orchestra_detail,
                route_path=last_routing_path().value,
                orchestra_state=orchestra_state,
                local_model=last_routing_model(),
                lang=self._lang,
            )
            timeline_block = build_timeline_block(timeline_events, lang=self._lang)
            pipeline_chip = build_pipeline_timeline_chip(
                timeline_events,
                lang=self._lang,
                intent_label=intent_label,
                routing_mode=routing_mode,
            )
            diagnostic_snippet = ""
            fetch_err = plan_fetch.error or chat_result.plan_fetch_error
            if fetch_err:
                snippet = sanitize_backend_error_snippet(fetch_err)
                if snippet:
                    diagnostic_snippet = safe_format_template(
                        self._t("avatar.chat.response.orchestra_error_detail"),
                        detail=snippet,
                    )

            response = self._format_avatar_chat_response(chat_result, snap=snap, ui_enabled=ui_enabled)
            return {
                "response": response,
                "chat_result": chat_result,
                "snap": snap,
                "ui_enabled": ui_enabled,
                "plan_preview": chat_result.plan_preview,
                "execution_plan": chat_result.execution_plan,
                "suggested_action": chat_result.suggested_action,
                "advisory_only": chat_result.advisory_only,
                "timeline_block": timeline_block,
                "pipeline_chip": pipeline_chip,
                "model_chip": model_chip,
                "diagnostic_snippet": diagnostic_snippet,
            }

        def _format_avatar_chat_response(self, result, *, snap: dict, ui_enabled: dict) -> str:
            if result.required_capability and result.advisory_only:
                capability_label = self._t(f"cap.{result.required_capability}")
                return safe_format_template(
                    self._t(result.response_key),
                    capability=capability_label,
                )
            if result.response_key == "avatar.chat.response.diagnostics":
                return safe_format_template(
                    self._t(result.response_key),
                    runtime=str(snap.get("runtime_state") or "-"),
                    trust=str(snap.get("trust_state") or "-"),
                    heartbeat=str(snap.get("last_heartbeat_at") or "-"),
                )
            if result.plan_fetch_error and result.execution_plan:
                from iuve_connect_agents.desktop.ui.status_adapter import normalize_plan_fetch_error_code

                error_code = normalize_plan_fetch_error_code(result.plan_fetch_error)
                if error_code == "invalid_device_auth_token":
                    warning = self._t("avatar.chat.response.plan_fetch_auth_failed")
                else:
                    warning = safe_format_template(
                        self._t("avatar.chat.response.plan_fetch_failed"),
                        reason=result.plan_fetch_error,
                    )
                goal = str(result.execution_plan.get("goal_summary") or "")
                steps_text = format_plan_preview_steps(result.execution_plan)
                preview = safe_format_template(
                    self._t("avatar.chat.response.plan_preview"),
                    goal=goal,
                    steps=steps_text,
                    step_count=len(result.execution_plan.get("steps") or []),
                )
                fallback_note = safe_format_template(
                    self._t("avatar.chat.response.plan_fetch_fallback"),
                    reason=result.plan_fetch_error,
                )
                return f"{warning}\n{fallback_note}\n\n{preview}"
            if result.plan_fetch_error:
                from iuve_connect_agents.desktop.ui.status_adapter import normalize_plan_fetch_error_code

                error_code = normalize_plan_fetch_error_code(result.plan_fetch_error)
                if error_code == "invalid_device_auth_token":
                    return self._t("avatar.chat.response.plan_fetch_auth_failed")
                return safe_format_template(
                    self._t("avatar.chat.response.plan_fetch_failed"),
                    reason=result.plan_fetch_error,
                )
            if result.response_key == "avatar.chat.response.plan_only" and result.response_params:
                return safe_format_template(self._t(result.response_key), **result.response_params)
            if result.plan_preview and result.execution_plan:
                goal = str(result.execution_plan.get("goal_summary") or "")
                steps_text = format_plan_preview_steps(result.execution_plan)
                return safe_format_template(
                    self._t("avatar.chat.response.plan_preview"),
                    goal=goal,
                    steps=steps_text,
                    step_count=len(result.execution_plan.get("steps") or []),
                )
            if result.response_key == "avatar.chat.response.advisory_consultation_required":
                return self._t(result.response_key)
            if result.response_key == "avatar.chat.response.plan_already_completed":
                return self._t(result.response_key)
            if result.response_key == "avatar.chat.response.plan_resume" and result.response_params:
                return safe_format_template(self._t(result.response_key), **result.response_params)
            if (
                result.response_key == "avatar.chat.response.action_ready"
                and result.suggested_action
                and should_auto_execute_action(
                    result.suggested_action,
                    trust_state=str(snap.get("trust_state") or ""),
                    ui_caps=ui_enabled,
                )
            ):
                action = result.suggested_action
                return safe_format_template(
                    self._t("avatar.chat.response.action_auto"),
                    action=self._t(action.title_key),
                )
            if result.response_key == "avatar.chat.response.action_ready" and result.suggested_action:
                action = result.suggested_action
                return safe_format_template(
                    self._t(result.response_key),
                    action=self._t(action.title_key),
                )
            if result.response_params:
                rendered = safe_format_template(self._t(result.response_key), **result.response_params)
                answer_text = str(result.response_params.get("answer") or "")
                if answer_text:
                    try:
                        from iuve_connect_agents.shared.answer_quality import (
                            AnswerQualityPipeline,
                            StructuredAnswerContract,
                        )

                        pipeline = AnswerQualityPipeline()
                        contract = pipeline.process(
                            answer_text,
                            question=str(getattr(result, "user_text", "") or ""),
                            channel="desktop_agent",
                        )
                        rendered, _updated = pipeline.process_rendered(rendered, contract)
                    except Exception:
                        pass
                return rendered
            return self._t(result.response_key)

        def _update_avatar_model_chip(self, label: str) -> None:
            text = str(label or "").strip()
            widget = getattr(self, "_avatar_model_chip", None)
            if widget is None:
                return
            if text:
                widget.set_label(text)
                widget.set_visible(True)
            else:
                widget.set_label("")
                widget.set_visible(False)

        def _update_avatar_pipeline_chip(self, label: str) -> None:
            text = str(label or "").strip()
            widget = getattr(self, "_avatar_timeline_chip", None)
            if widget is None:
                return
            if text:
                widget.set_label(text)
                widget.set_visible(True)
            else:
                widget.set_label("")
                widget.set_visible(False)

        def _reset_avatar_plan_session_state(self) -> None:
            self._avatar_plan_session_state = None
            self._avatar_plan_session_preset = None
            self._avatar_plan_bound_session_id = None
            self._avatar_plan_session_plan_id = None
            if hasattr(self, "_avatar_session_proposal_row"):
                self._avatar_session_proposal_row.set_visible(False)

        def _plan_binding_id(self, plan: dict) -> str:
            return str(plan.get("plan_id") or getattr(self, "_avatar_plan_session_plan_id", "") or "").strip()

        def _maybe_propose_plan_action_session(self, plan: dict | None, *, trust_state: str) -> None:
            self._reset_avatar_plan_session_state()
            if not plan or not isinstance(plan, dict):
                return
            preset_key = detect_plan_session_preset(plan)
            plan_id = self._plan_binding_id(plan)
            if not plan_id:
                import secrets

                plan_id = f"plan_{secrets.token_hex(4)}"
                plan["plan_id"] = plan_id
            self._avatar_plan_session_preset = preset_key
            self._avatar_plan_session_plan_id = plan_id
            active_sessions = self._adapter.list_active_action_sessions()
            if active_session_covers_preset(active_sessions, preset_key):
                bound = self._adapter.find_active_plan_bound_session(plan_id)
                if bound is not None:
                    self._avatar_plan_session_state = "started"
                    self._avatar_plan_bound_session_id = str(bound.get("session_id") or "")
                    return
            self._avatar_plan_session_state = "ready"
            if hasattr(self, "_avatar_session_proposal_row"):
                self._avatar_session_proposal_row.set_visible(False)

        def _present_session_proposal_modal(self, preset_key: str, plan: dict) -> None:
            title = self._t(f"cap.action_sessions.preset.{preset_key}.user_title")
            duration = self._t(f"cap.action_sessions.preset.{preset_key}.duration")

            def _on_response(response: str) -> None:
                if response == "start":
                    self._start_plan_bound_session(preset_key, plan)
                elif response == "skip":
                    self._on_avatar_session_skip_clicked()

            self._present_action_required_modal(
                title=self._t("modal.session_start.title"),
                body=safe_format_template(
                    self._t("modal.session_start.body"),
                    title=title,
                    duration=duration,
                ),
                buttons=[
                    ("skip", self._t("avatar.action.session_skip")),
                    ("start", self._t("modal.session_start.confirm")),
                ],
                default_button="start",
                on_response=_on_response,
            )

        def _ensure_plan_bound_session(self, plan: dict) -> bool:
            plan_id = self._plan_binding_id(plan)
            if not plan_id:
                import secrets

                plan_id = f"plan_{secrets.token_hex(4)}"
                plan["plan_id"] = plan_id
                self._avatar_plan_session_plan_id = plan_id
            existing = self._adapter.find_active_plan_bound_session(plan_id)
            if existing is not None:
                self._avatar_plan_bound_session_id = str(existing.get("session_id") or "")
                self._avatar_plan_session_state = "started"
                return True
            preset_key = str(self._avatar_plan_session_preset or detect_plan_session_preset(plan) or "").strip()
            result = self._adapter.create_plan_bound_action_session(
                preset_key=preset_key,
                plan_id=plan_id,
                plan=plan,
            )
            session = dict(result.get("session") or {})
            session_id = str(session.get("session_id") or "")
            if not session_id:
                return False
            self._avatar_plan_bound_session_id = session_id
            self._avatar_plan_session_state = "started"
            self._avatar_plan_session_preset = preset_key
            self._refresh_action_sessions_panel()
            self._refresh_command_log_panel()
            return True

        def _start_plan_bound_session(self, preset_key: str, plan: dict) -> bool:
            self._avatar_plan_session_preset = preset_key
            return self._ensure_plan_bound_session(plan)

        def _revoke_plan_bound_session(self, plan: dict | None, *, reason: str = "plan_completed") -> None:
            plan_id = self._plan_binding_id(plan or {})
            if plan_id:
                self._adapter.revoke_plan_bound_action_session(plan_id, reason=reason)
            elif self._avatar_plan_bound_session_id:
                self._adapter.revoke_action_session(self._avatar_plan_bound_session_id)
            self._avatar_plan_bound_session_id = None
            self._refresh_action_sessions_panel()
            self._refresh_command_log_panel()

        def _on_avatar_session_start_clicked(self, _btn=None) -> None:
            preset_key = str(self._avatar_plan_session_preset or "").strip()
            plan = getattr(self, "_avatar_pending_plan", None)
            if not preset_key or not plan:
                return

            def _start_if_confirmed(response: str) -> None:
                if response != "start":
                    return
                self._start_plan_bound_session(preset_key, plan)

            self._present_action_required_modal(
                title=self._t("modal.session_start.title"),
                body=self._t("modal.session_start.body"),
                buttons=[
                    ("cancel", self._t("modal.action.cancel")),
                    ("start", self._t("modal.session_start.confirm")),
                ],
                default_button="start",
                on_response=_start_if_confirmed,
            )

        def _on_avatar_session_skip_clicked(self, _btn=None) -> None:
            if self._avatar_plan_session_state != "pending":
                return
            self._avatar_plan_session_state = "skipped"
            if hasattr(self, "_avatar_session_proposal_row"):
                self._avatar_session_proposal_row.set_visible(False)
            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t("avatar.chat.response.session_skipped"),
            )

        def _on_avatar_mic_clicked(self, _btn) -> None:
            if not hasattr(self, "_avatar_mic_btn"):
                return
            self._avatar_mic_btn.set_sensitive(False)

            def _worker() -> None:
                result = request_voice_input()

                def _finish() -> bool:
                    self._avatar_mic_btn.set_sensitive(True)
                    if result.ok and result.text:
                        self._avatar_append_input_text(result.text)
                    elif result.error_key:
                        self._avatar_append_message(
                            self._t("avatar.chat.avatar"),
                            self._t(result.error_key),
                        )
                    return False

                self._glib.idle_add(_finish)

            threading.Thread(target=_worker, daemon=True).start()

        def _maybe_auto_execute_avatar(self, *, chat_result, snap: dict, ui_enabled: dict) -> None:
            if getattr(self, "_avatar_auto_execute_blocked", False):
                return
            trust = str(snap.get("trust_state") or "")
            if chat_result.suggested_action and not chat_result.advisory_only:
                if should_auto_execute_action(
                    chat_result.suggested_action,
                    trust_state=trust,
                    ui_caps=ui_enabled,
                ):
                    self._avatar_append_message(
                        self._t("avatar.chat.avatar"),
                        self._t("avatar.chat.response.auto_executing"),
                    )
                    self._execute_avatar_action_auto(chat_result.suggested_action)
                return

            if chat_result.plan_preview and chat_result.execution_plan:
                if chat_result.plan_resume or should_auto_execute_plan(
                    chat_result.execution_plan,
                    trust_state=trust,
                    ui_caps=ui_enabled,
                ):
                    delay_ms = 0 if chat_result.plan_resume else plan_auto_start_delay_ms(chat_result.execution_plan)
                    plan = dict(chat_result.execution_plan)
                    resume_index = int(getattr(chat_result, "resume_step_index", 0) or 0)

                    def _start_plan() -> bool:
                        if getattr(self, "_avatar_pending_plan", None) is plan:
                            self._ensure_plan_bound_session(plan)
                            self._update_avatar_plan_preview(plan)
                            if chat_result.plan_resume:
                                self._avatar_append_message(
                                    self._t("avatar.chat.avatar"),
                                    self._t("avatar.chat.response.auto_executing_plan"),
                                )
                            else:
                                self._avatar_append_message(
                                    self._t("avatar.chat.avatar"),
                                    self._t("avatar.chat.response.auto_executing_plan"),
                                )
                            self._execute_avatar_plan(plan, start_step_index=resume_index)
                        return False

                    self._glib.timeout_add(delay_ms, _start_plan)

        def _execute_avatar_action_auto(self, action: AvatarSuggestedAction) -> None:
            def _worker() -> None:
                prepared = self._adapter.prepare_avatar_action(
                    command_type=action.command_type,
                    payload=dict(action.payload),
                    scope=dict(action.scope),
                )

                def _finish() -> bool:
                    if not prepared.ok or prepared.envelope is None or prepared.decision is None:
                        reason = str(prepared.message or prepared.status)
                        if "trust_not_trusted" in reason:
                            self._avatar_auto_execute_blocked = True
                        message = self._t("avatar.chat.response.execution_denied").format(
                            reason=reason
                        )
                        self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                        return False
                    outcome = self._adapter.execute_avatar_action(
                        prepared.envelope,
                        prepared.decision,
                        confirmation="approve",
                    )
                    self._avatar_report_execution_outcome(outcome)
                    return False

                self._glib.idle_add(_finish)

            threading.Thread(target=_worker, daemon=True).start()

        def _update_avatar_plan_preview(self, plan: dict | None, *, show_retry: bool = False) -> None:
            self._avatar_pending_action = None
            self._avatar_pending_plan = plan
            self._avatar_show_retry_step = bool(show_retry and plan)
            if not hasattr(self, "_avatar_suggested_action_row"):
                return
            if not plan:
                self._avatar_suggested_action_row.set_visible(False)
                self._avatar_pending_plan = None
                self._avatar_show_retry_step = False
                if hasattr(self, "_avatar_retry_step_btn"):
                    self._avatar_retry_step_btn.set_visible(False)
                self._reset_avatar_plan_session_state()
                return
            goal = str(plan.get("goal_summary") or "")
            steps_text = format_plan_preview_steps(plan)
            body = self._t("avatar.plan.preview.body").format(
                goal=goal,
                steps=steps_text,
                step_count=len(plan.get("steps") or []),
            )
            if self._avatar_plan_session_state == "started" or getattr(self, "_avatar_plan_running", False):
                body = (
                    f"{body}\n\n"
                    f"{self._t('avatar.plan.session.allowed')}\n"
                    f"{self._t('avatar.plan.session.auto_exec')}"
                )
            self._avatar_suggested_action_title.set_label(self._t("avatar.plan.preview.title"))
            self._avatar_suggested_action_body.set_label(body)
            self._avatar_suggested_action_row.set_visible(True)
            from iuve_connect_agents.shared.feature_flags import avatar_plan_executor_enabled

            if hasattr(self, "_avatar_execute_btn"):
                plan_only = str(plan.get("execution_mode") or "").strip().lower() == "plan_only"
                self._avatar_execute_btn.set_visible(
                    avatar_plan_executor_enabled() and not plan_only
                )
                self._avatar_execute_btn.set_label(
                    self._t("avatar.action.execute_plan")
                    if avatar_plan_executor_enabled()
                    else self._t("avatar.action.execute")
                )
            if hasattr(self, "_avatar_advisory_note"):
                self._avatar_advisory_note.set_visible(not avatar_plan_executor_enabled())
                note_key = (
                    "avatar.plan.preview.execute_note"
                    if avatar_plan_executor_enabled()
                    else "avatar.plan.preview.note"
                )
                self._avatar_advisory_note.set_label(self._t(note_key))
            if hasattr(self, "_avatar_retry_step_btn"):
                self._avatar_retry_step_btn.set_visible(self._avatar_show_retry_step)

        def _on_avatar_retry_step_clicked(self, _btn=None) -> None:
            if getattr(self, "_avatar_plan_running", False):
                return
            pending_plan = getattr(self, "_avatar_pending_plan", None)
            if not pending_plan:
                from ..active.avatar_paused_plan_store import avatar_paused_plan_store

                paused = avatar_paused_plan_store().load()
                if paused is None or paused.status != "paused":
                    self._avatar_append_message(
                        self._t("avatar.chat.avatar"),
                        self._t("avatar.chat.response.plan_already_completed"),
                    )
                    return
                pending_plan = dict(paused.plan_json)
                self._avatar_resume_step_index = paused.step_index
                self._update_avatar_plan_preview(pending_plan, show_retry=True)
            resume_index = int(getattr(self, "_avatar_resume_step_index", 0) or 0)
            self._execute_avatar_plan(dict(pending_plan), start_step_index=resume_index)

        def _update_avatar_suggested_action(self, action: AvatarSuggestedAction | None) -> None:
            self._avatar_pending_action = action
            self._avatar_pending_plan = None
            if not hasattr(self, "_avatar_suggested_action_row"):
                return
            if action is None:
                self._avatar_suggested_action_row.set_visible(False)
                if hasattr(self, "_avatar_advisory_note"):
                    self._avatar_advisory_note.set_visible(True)
                return
            self._avatar_suggested_action_title.set_label(self._t(action.title_key))
            body = self._t(action.description_key)
            if action.command_type in {"browser_open_url", "open_url"}:
                body = body.format(url=str(action.payload.get("url") or ""))
            self._avatar_suggested_action_body.set_label(body)
            self._avatar_suggested_action_row.set_visible(True)
            if hasattr(self, "_avatar_advisory_note"):
                self._avatar_advisory_note.set_visible(False)
            if hasattr(self, "_avatar_execute_btn"):
                self._avatar_execute_btn.set_visible(True)
                self._avatar_execute_btn.set_label(self._t("avatar.action.execute"))

        def _on_avatar_execute_clicked(self, _btn=None) -> None:
            if getattr(self, "_avatar_plan_running", False):
                return
            pending_plan = getattr(self, "_avatar_pending_plan", None)
            if pending_plan:
                def _execute_if_confirmed(response: str) -> None:
                    if response != "execute":
                        return
                    self._ensure_plan_bound_session(pending_plan)
                    self._update_avatar_plan_preview(pending_plan)
                    resume_index = int(getattr(self, "_avatar_resume_step_index", 0) or 0)
                    self._execute_avatar_plan(pending_plan, start_step_index=resume_index)

                self._present_action_required_modal(
                    title=self._t("modal.plan_execute.title"),
                    body=self._t("modal.plan_execute.body"),
                    buttons=[
                        ("cancel", self._t("modal.action.cancel")),
                        ("execute", self._t("modal.plan_execute.confirm")),
                    ],
                    default_button="execute",
                    on_response=_execute_if_confirmed,
                )
                return
            action = getattr(self, "_avatar_pending_action", None)
            if action is None:
                return
            self._execute_avatar_single_action(action)

        def _execute_avatar_single_action(self, action: AvatarSuggestedAction) -> None:
            """Run prepare/execute off the GTK main thread to avoid policy/network stalls."""

            def _worker() -> None:
                prepared = self._adapter.prepare_avatar_action(
                    command_type=action.command_type,
                    payload=dict(action.payload),
                    scope=dict(action.scope),
                )

                def _finish() -> bool:
                    if not prepared.ok or prepared.envelope is None or prepared.decision is None:
                        reason = human_execution_error_reason(self._lang, str(prepared.message or prepared.status))
                        if "trust_not_trusted" in str(prepared.message or prepared.status):
                            self._avatar_auto_execute_blocked = True
                        message = self._t("avatar.chat.response.execution_denied").format(reason=reason)
                        self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                        return False

                    if prepared.require_confirmation:
                        confirm_handler = wrap_confirmation_callback(
                            lambda env, dec: self._present_command_confirmation_dialog(env, dec)
                        )
                        result = confirm_handler(prepared.envelope, prepared.decision)
                        if result != "approve":
                            self._avatar_append_message(
                                self._t("avatar.chat.avatar"),
                                self._t("avatar.chat.response.no_action"),
                            )
                            return False
                        outcome = self._adapter.execute_avatar_action(
                            prepared.envelope,
                            prepared.decision,
                            confirmation="approve",
                        )
                        self._avatar_report_execution_outcome(outcome)
                        return False

                    outcome = self._adapter.execute_avatar_action(prepared.envelope, prepared.decision)
                    self._avatar_report_execution_outcome(outcome)
                    return False

                self._glib.idle_add(_finish)

            threading.Thread(target=_worker, daemon=True).start()

        def _set_avatar_plan_controls_busy(self, busy: bool) -> None:
            if hasattr(self, "_avatar_execute_btn"):
                self._avatar_execute_btn.set_sensitive(not busy)
                if busy:
                    self._avatar_execute_btn.set_label(self._t("avatar.action.executing_plan"))
                else:
                    pending_plan = getattr(self, "_avatar_pending_plan", None)
                    self._avatar_execute_btn.set_label(
                        self._t("avatar.action.execute_plan")
                        if pending_plan
                        else self._t("avatar.action.execute")
                    )
            if hasattr(self, "_avatar_cancel_btn"):
                self._avatar_cancel_btn.set_sensitive(not busy)
            if hasattr(self, "_avatar_stop_btn"):
                stop_key = (
                    "avatar.plan.session.stop_task"
                    if busy and self._avatar_plan_session_state == "started"
                    else "avatar.action.stop"
                )
                self._avatar_stop_btn.set_tooltip_text(self._t(stop_key))
            if hasattr(self, "_avatar_plan_spinner"):
                if busy:
                    self._avatar_plan_spinner.set_visible(True)
                    self._avatar_plan_spinner.start()
                else:
                    self._avatar_plan_spinner.stop()
                    self._avatar_plan_spinner.set_visible(False)
            self._refresh_avatar_chat_input_bar()

        def _execute_avatar_plan(self, plan: dict, *, start_step_index: int = 0) -> None:
            if str(plan.get("execution_mode") or "").strip().lower() == "plan_only" or plan.get(
                "no_execution_required"
            ):
                return
            if getattr(self, "_avatar_plan_running", False):
                return
            self._avatar_plan_running = True
            self._avatar_plan_cancel_requested = False
            self._avatar_plan_execution_control.clear()
            self._avatar_plan_finish_handled = False
            self._avatar_chat_tag_dismissed = False
            self._avatar_resume_step_index = max(0, int(start_step_index))
            self._avatar_show_retry_step = False
            if hasattr(self, "_avatar_retry_step_btn"):
                self._avatar_retry_step_btn.set_visible(False)
            self._set_avatar_plan_controls_busy(True)
            self._set_agent_status(AgentStatusState.WORKING)

            steps = plan.get("steps") or []
            goal = str(plan.get("goal_summary") or "")
            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t("avatar.chat.response.plan_started").format(
                    step_count=len(steps),
                    goal=goal,
                ),
            )

            def _on_step_progress(step_outcome, index: int, total: int) -> None:
                if self._avatar_plan_execution_control.is_cancelled():
                    return
                step_evidence = dict(getattr(step_outcome, "evidence", None) or {})
                web_status = str(
                    step_evidence.get("web_post_status") or step_evidence.get("draft_state") or ""
                ).strip()
                raw_status = web_status or str(step_outcome.status or ("ok" if step_outcome.ok else "failed"))
                step_label = human_step_label(str(step_outcome.command_type or ""), lang=self._lang)
                status_label = human_step_status(raw_status, lang=self._lang)
                timeline_line = format_timeline_line(
                    build_execute_timeline_event(
                        step_index=index,
                        step_total=total,
                        step_label=step_label,
                    ),
                    lang=self._lang,
                )
                message = self._t("avatar.chat.response.plan_step_progress").format(
                    index=index,
                    total=total,
                    step_label=step_label,
                    status=status_label,
                )
                combined = f"{timeline_line}\n{message}"

                def _append_on_main_thread() -> bool:
                    self._avatar_append_message(self._t("avatar.chat.system"), combined, role="system")
                    return False

                self._glib.idle_add(_append_on_main_thread)
                if step_outcome.ok and not self._avatar_plan_execution_control.is_cancelled():
                    self._wait_for_interactive_plan_step(str(step_outcome.command_type or ""))

            def _worker() -> None:
                try:
                    outcome = self._adapter.execute_avatar_plan(
                        plan,
                        step_callback=_on_step_progress,
                        vision_recovery_callback=self._wait_for_vision_fail_recovery,
                        start_step_index=self._avatar_resume_step_index,
                        cancel_check=self._avatar_plan_execution_control.is_cancelled,
                    )
                except Exception as exc:
                    self._glib.idle_add(self._handle_avatar_plan_finished, None, str(exc))
                    return
                self._glib.idle_add(self._handle_avatar_plan_finished, outcome, "")

            threading.Thread(target=_worker, daemon=True).start()

        def _handle_avatar_plan_finished(self, outcome, error_message: str = "") -> bool:
            if getattr(self, "_avatar_plan_finish_handled", False):
                return False
            self._avatar_plan_finish_handled = True
            finished_plan = getattr(self, "_avatar_pending_plan", None)
            cancelled = bool(getattr(self, "_avatar_plan_cancel_requested", False))
            self._avatar_plan_running = False
            self._avatar_plan_cancel_requested = False
            self._set_avatar_plan_controls_busy(False)
            if cancelled:
                self._set_agent_status(AgentStatusState.IDLE)
                return False
            if error_message:
                self._set_agent_status(AgentStatusState.ERROR)
                self._avatar_append_message(
                    self._t("avatar.chat.avatar"),
                    self._t("avatar.chat.response.plan_error").format(reason=error_message),
                )
            elif outcome is not None:
                from ..active.avatar_paused_plan_store import (
                    avatar_paused_plan_store,
                    paused_step_index_for_plan,
                )

                if outcome.ok:
                    avatar_paused_plan_store().mark_completed(str(outcome.plan_id or ""))
                    avatar_paused_plan_store().clear()
                    done_line = format_timeline_line(build_done_timeline_event(ok=True), lang=self._lang)
                    self._avatar_append_message(self._t("avatar.chat.system"), done_line, role="system")
                    message = self._t("avatar.chat.response.plan_completed").format(
                        step_count=len(outcome.completed_steps),
                        goal=str(outcome.goal_summary or ""),
                    )
                    self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                    verify_line = format_goal_verification_message(
                        goal_summary=str(outcome.goal_summary or ""),
                        completed_steps=outcome.completed_steps,
                    )
                    if verify_line:
                        self._avatar_append_message(self._t("avatar.chat.avatar"), verify_line)
                    for step in reversed(outcome.completed_steps):
                        step_evidence = dict(getattr(step, "evidence", None) or {})
                        if step_evidence.get("handler") != "keyboard_type_text":
                            continue
                        typed_text = str(step_evidence.get("text") or "").strip()
                        if typed_text:
                            self._avatar_append_message(
                                self._t("avatar.chat.avatar"),
                                self._t(keyboard_type_success_message_key(step_evidence)).format(
                                    text=typed_text,
                                ),
                            )
                        break
                    self._set_agent_status(AgentStatusState.IDLE)
                else:
                    self._set_agent_status(AgentStatusState.ERROR)
                    failed_label = str(outcome.failed_step_id or "?")
                    for step in outcome.completed_steps:
                        if step.step_id == outcome.failed_step_id or not step.ok:
                            failed_label = human_step_label(step.command_type, lang=self._lang)
                            break
                    message = self._t("avatar.chat.response.plan_failed").format(
                        reason=str(outcome.message or outcome.status),
                        step_label=failed_label,
                    )
                    self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                    if finished_plan and isinstance(finished_plan, dict):
                        step_index = paused_step_index_for_plan(
                            finished_plan,
                            str(outcome.failed_step_id or ""),
                        )
                        self._avatar_resume_step_index = step_index
                        avatar_paused_plan_store().save_paused(
                            plan=finished_plan,
                            step_index=step_index,
                            pause_reason=str(outcome.message or outcome.status),
                        )
                        self._update_avatar_plan_preview(finished_plan, show_retry=True)
                        self._avatar_pending_plan = finished_plan
                        self._refresh_command_log_panel()
                        self._refresh_avatar_context(force=True)
                        return False
            self._avatar_pending_plan = None
            self._update_avatar_plan_preview(None)
            if finished_plan:
                reason = "plan_completed" if outcome is not None and getattr(outcome, "ok", False) else "plan_cancelled"
                self._revoke_plan_bound_session(finished_plan, reason=reason)
            self._reset_avatar_plan_session_state()
            self._refresh_command_log_panel()
            self._refresh_avatar_context(force=True)
            self._refresh_agent_status_overlay()
            return False

        def _avatar_report_execution_outcome(self, outcome) -> None:
            evidence = dict(outcome.evidence or {})
            if outcome.ok:
                folder_path = str(evidence.get("path") or "").strip()
                if evidence.get("handler") == "open_folder" and folder_path:
                    message = self._t("avatar.chat.response.execution_ok_folder").format(
                        path=folder_path,
                        status=str(outcome.status or "ok"),
                    )
                    self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                    self._update_avatar_suggested_action(None)
                    self._refresh_command_log_panel()
                    self._refresh_avatar_context()
                    return
                if evidence.get("handler") == "application_launch":
                    app_name = str(evidence.get("application") or "application")
                    message = self._t("avatar.chat.response.execution_ok_app").format(
                        application=app_name,
                        status=str(outcome.status or "ok"),
                    )
                    self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                    self._update_avatar_suggested_action(None)
                    self._refresh_command_log_panel()
                    self._refresh_avatar_context()
                    return
                if evidence.get("handler") == "keyboard_type_text":
                    typed_text = str(evidence.get("text") or "").strip()
                    message = self._t(keyboard_type_success_message_key(evidence)).format(
                        text=typed_text,
                    )
                    self._avatar_append_message(self._t("avatar.chat.avatar"), message)
                    self._update_avatar_suggested_action(None)
                    self._refresh_command_log_panel()
                    self._refresh_avatar_context()
                    return
                if evidence.get("handler") == "mouse_move_relative":
                    self._avatar_append_message(
                        self._t("avatar.chat.avatar"),
                        self._t("avatar.chat.response.execution_ok_mouse"),
                    )
                    self._update_avatar_suggested_action(None)
                    self._refresh_command_log_panel()
                    self._refresh_avatar_context()
                    return
                if evidence.get("handler") in {"empty_trash", "trash_empty"}:
                    self._avatar_append_message(
                        self._t("avatar.chat.avatar"),
                        self._t("avatar.chat.response.execution_ok_trash"),
                    )
                    self._update_avatar_suggested_action(None)
                    self._refresh_command_log_panel()
                    self._refresh_avatar_context()
                    return
                memory_summary = evidence.get("memory_summary")
                if isinstance(memory_summary, dict) or evidence.get("metrics_table"):
                    memory_text = format_diagnostics_evidence_message(evidence)
                    if memory_text:
                        self._avatar_append_message(self._t("avatar.chat.avatar"), memory_text)
                        self._update_avatar_suggested_action(None)
                        self._refresh_command_log_panel()
                        self._refresh_avatar_context()
                        return
                if evidence.get("handler") == "describe_active_window":
                    description = str(evidence.get("description") or evidence.get("findings") or "").strip()
                    if description:
                        self._avatar_append_message(self._t("avatar.chat.avatar"), description)
                        self._update_avatar_suggested_action(None)
                        self._refresh_command_log_panel()
                        self._refresh_avatar_context()
                        return
                if evidence.get("handler") == "list_files_in_folder":
                    file_list = str(
                        evidence.get("markdown_table")
                        or evidence.get("file_list_text")
                        or evidence.get("empty_message")
                        or ""
                    ).strip()
                    count = int(evidence.get("match_count") or 0)
                    if file_list:
                        if count > 0:
                            header = self._t("avatar.chat.response.files_list_header").format(count=count)
                            self._avatar_append_message(
                                self._t("avatar.chat.avatar"),
                                f"{header}\n\n{file_list}",
                            )
                        else:
                            self._avatar_append_message(self._t("avatar.chat.avatar"), file_list)
                        self._update_avatar_suggested_action(None)
                        self._refresh_command_log_panel()
                        self._refresh_avatar_context()
                        return
                if evidence.get("handler") == "verify_screen_contains_text":
                    if evidence.get("page_loaded") or str(outcome.status or "").startswith("vision_verify_pass"):
                        self._avatar_append_message(
                            self._t("avatar.chat.avatar"),
                            self._t("avatar.chat.response.page_loaded_ok"),
                        )
                        self._update_avatar_suggested_action(None)
                        self._refresh_command_log_panel()
                        self._refresh_avatar_context()
                        return
                url = str(evidence.get("url") or "").strip()
                if url:
                    message = self._t("avatar.chat.response.execution_ok_url").format(
                        url=url,
                        status=human_execution_outcome_label(self._lang, str(outcome.status or "ok")),
                    )
                else:
                    message = self._t("avatar.chat.response.execution_ok")
            else:
                reason = human_execution_error_reason(
                    self._lang,
                    str(outcome.message or outcome.status or "failed").strip(),
                )
                stderr = str(evidence.get("stderr") or "").strip()
                if stderr and stderr not in reason:
                    reason = f"{reason} — {stderr[:240]}"
                message = self._t("avatar.chat.response.execution_failed").format(reason=reason)
            self._avatar_append_message(self._t("avatar.chat.avatar"), message)
            self._update_avatar_suggested_action(None)
            self._refresh_command_log_panel()
            self._refresh_avatar_context()

        def _on_avatar_open_permissions_clicked(self, _btn):
            self._stack.set_visible_child_name("security")

        def _on_avatar_open_diagnostics_clicked(self, _btn):
            self._stack.set_visible_child_name("diagnostics")

        def _on_diagnostics_refresh_scan_clicked(self, _btn):
            self._begin_diagnostics_device_scan_ui()
            result = self._adapter.refresh_device_scan()
            status = str(result.get("status") or "")
            if status == "failed":
                self._finish_diagnostics_device_scan_ui(
                    success=False,
                    reason=self._t("diagnostics.scan.status.interrupted"),
                )
                return
            self._refresh_avatar_context(force=True)

        def _clear_diagnostics_scan_watchdog(self) -> None:
            watchdog_id = int(getattr(self, "_diagnostics_scan_watchdog_id", 0) or 0)
            if watchdog_id:
                self._glib.source_remove(watchdog_id)
            self._diagnostics_scan_watchdog_id = 0

        def _on_diagnostics_scan_watchdog(self) -> bool:
            self._diagnostics_scan_watchdog_id = 0
            if not getattr(self, "_diagnostics_device_scan_active", False):
                return False
            self._finish_diagnostics_device_scan_ui(
                success=False,
                reason=self._t("diagnostics.scan.status.interrupted"),
            )
            return False

        def _begin_diagnostics_device_scan_ui(self) -> None:
            if not hasattr(self, "_diagnostics_refresh_scan_btn"):
                return
            self._clear_diagnostics_scan_watchdog()
            self._diagnostics_device_scan_active = True
            self._diagnostics_refresh_scan_btn.set_sensitive(False)
            self._diagnostics_refresh_scan_btn.set_label(self._t("diagnostics.action.refresh_scan.scanning"))
            if hasattr(self, "_diagnostics_scan_status_label"):
                self._diagnostics_scan_status_label.set_label(self._t("diagnostics.scan.status.scanning"))
                self._diagnostics_scan_status_label.set_visible(True)
            from iuve_connect_agents.desktop.active.device_capability_scanner import SCAN_TIMEOUT_SEC

            self._diagnostics_scan_watchdog_id = self._glib.timeout_add_seconds(
                SCAN_TIMEOUT_SEC + 10,
                self._on_diagnostics_scan_watchdog,
            )

        def _finish_diagnostics_device_scan_ui(
            self,
            *,
            success: bool,
            profile: dict | None = None,
            reason: str = "",
        ) -> None:
            if not getattr(self, "_diagnostics_device_scan_active", False):
                return
            self._clear_diagnostics_scan_watchdog()
            self._diagnostics_device_scan_active = False
            if hasattr(self, "_diagnostics_refresh_scan_btn"):
                self._diagnostics_refresh_scan_btn.set_sensitive(True)
                self._diagnostics_refresh_scan_btn.set_label(self._t("diagnostics.action.refresh_scan"))
            if not hasattr(self, "_diagnostics_scan_status_label"):
                return
            if success:
                apps_count = 0
                if isinstance(profile, dict):
                    apps_count = int(profile.get("applications_count") or 0)
                self._diagnostics_scan_status_label.set_label(
                    self._t("diagnostics.scan.status.complete").format(count=apps_count)
                )
                self._avatar_append_message(
                    self._t("avatar.chat.avatar"),
                    self._t("avatar.chat.response.device_scan_manual").format(count=apps_count),
                )
            else:
                detail = reason.strip() or self._t("diagnostics.scan.status.failed").format(reason="unknown")
                self._diagnostics_scan_status_label.set_label(detail)
            self._diagnostics_scan_status_label.set_visible(True)

        def _on_avatar_cancel_clicked(self, _btn):
            self._avatar_clear_input()
            self._update_avatar_suggested_action(None)
            self._update_avatar_plan_preview(None)
            self._reset_avatar_plan_session_state()
            self._avatar_append_message(self._t("avatar.chat.avatar"), self._t("avatar.chat.response.no_action"))

        def _refresh_avatar_context(self, *, force: bool = False):
            if not hasattr(self, "_avatar_context_labels"):
                return
            now = self._glib.get_monotonic_time() / 1_000_000
            if (
                not force
                and now - float(getattr(self, "_last_avatar_context_refresh_at", 0.0))
                < DASHBOARD_REFRESH_MIN_INTERVAL_SEC
            ):
                return
            self._last_avatar_context_refresh_at = now
            snap = self._adapter.read_dashboard_snapshot()
            caps = self._adapter.read_capability_snapshot()
            granted_ui, total_ui = self._adapter.read_active_ui_grant_counts()
            profile = self._adapter.read_device_capability_profile()
            root_enabled = bool(caps.get("root_operations", {}).get("enabled", False))

            cpu_pct = self._read_cpu_usage_percent()
            ram_pct = self._read_ram_usage_percent()
            disk_pct = self._read_disk_usage_percent()

            cpu_info = profile.get("cpu") if isinstance(profile.get("cpu"), dict) else {}
            memory = profile.get("memory") if isinstance(profile.get("memory"), dict) else {}
            disk_info = profile.get("disk") if isinstance(profile.get("disk"), dict) else {}
            os_info = profile.get("os") if isinstance(profile.get("os"), dict) else {}

            cpu_model = str(cpu_info.get("model") or "").strip()
            if cpu_model and cpu_pct is not None:
                self._set_context_text("cpu", f"{cpu_model[:28]} ({cpu_pct}%)")
            elif cpu_model:
                self._set_context_text("cpu", cpu_model[:36])
            else:
                self._set_context_metric("cpu", cpu_pct)

            total_mb = memory.get("total_mb")
            avail_mb = memory.get("available_mb")
            if total_mb and avail_mb is not None:
                ram_label = f"{avail_mb}/{total_mb} MB"
                if ram_pct is not None:
                    ram_label = f"{ram_label} ({ram_pct}%)"
                self._set_context_text("ram", ram_label)
            else:
                self._set_context_metric("ram", ram_pct)

            free_gb = disk_info.get("free_gb")
            if free_gb is not None:
                disk_label = f"{free_gb} GB free"
                if disk_pct is not None:
                    disk_label = f"{disk_label} ({disk_pct}%)"
                self._set_context_text("disk", disk_label)
            else:
                self._set_context_metric("disk", disk_pct)

            if os_info:
                os_label = f"{os_info.get('name', '?')} {os_info.get('version', '')}".strip()
                self._set_context_text("os", os_label[:40])
            else:
                self._set_context_text("os", None)

            apps_count = profile.get("applications_count")
            if apps_count is not None:
                self._set_context_text("apps", str(apps_count))
            else:
                self._set_context_text("apps", None)

            self._set_context_text("scan", self._format_profile_scan_time(profile.get("scanned_at")))

            self._avatar_context_labels["trust"].set_label(str(snap.get("trust_state") or "-"))
            self._avatar_context_labels["capabilities"].set_label(f"{granted_ui}/{total_ui}")
            self._avatar_context_labels["root"].set_label(
                self._t("cap.status.enabled") if root_enabled else self._t("cap.root_session.none")
            )

            if hasattr(self, "_avatar_context_frame"):
                has_primary_metrics = bool(profile) or any(
                    v is not None for v in (cpu_pct, ram_pct, disk_pct)
                )
                self._avatar_context_frame.set_visible(has_primary_metrics)

            if hasattr(self, "_avatar_status_header"):
                self._avatar_status_header.set_label(
                    self._t("avatar.title")
                )
            if hasattr(self, "_avatar_intro_label"):
                device_name = self._format_device_name(self._adapter.read_devices_snapshot())
                self._avatar_intro_label.set_label(
                    self._t("avatar.intro").format(device=device_name)
                )
            if hasattr(self, "_avatar_advisory_note"):
                paired = bool(snap.get("paired", False))
                trust_state = str(snap.get("trust_state") or "").strip().lower()
                can_execute = paired and trust_state == "trusted" and granted_ui > 0
                show_advisory = not can_execute
                if can_execute and (
                    getattr(self, "_avatar_pending_action", None) is not None
                    or getattr(self, "_avatar_pending_plan", None) is not None
                ):
                    show_advisory = False
                self._avatar_advisory_note.set_visible(show_advisory)
            if hasattr(self, "_avatar_onboarding_row"):
                trust_state = str(snap.get("trust_state") or "").strip().lower()
                runtime_state = str(snap.get("runtime_state") or "").strip().lower()
                paired = bool(snap.get("paired", False))
                needs_pairing = (not paired) or (trust_state == "unpaired" and runtime_state == "offline")
                self._avatar_onboarding_row.set_visible(needs_pairing)
            self._refresh_avatar_chat_input_bar()

        def _set_context_metric(self, key: str, value: int | None):
            row = self._avatar_context_rows.get(key)
            lbl = self._avatar_context_labels.get(key)
            if row is None or lbl is None:
                return
            if value is None:
                row.set_visible(False)
                return
            row.set_visible(True)
            lbl.set_label(f"{value}%")

        def _set_context_text(self, key: str, value: str | None):
            row = self._avatar_context_rows.get(key)
            lbl = self._avatar_context_labels.get(key)
            if row is None or lbl is None:
                return
            if not value:
                row.set_visible(False)
                return
            row.set_visible(True)
            lbl.set_label(str(value))

        def _format_profile_scan_time(self, scanned_at: object) -> str | None:
            raw = str(scanned_at or "").strip()
            if not raw:
                return None
            try:
                from datetime import datetime, timezone

                scanned = datetime.fromisoformat(raw.replace("Z", "+00:00"))
                age_min = int(
                    (datetime.now(timezone.utc) - scanned.astimezone(timezone.utc)).total_seconds() // 60
                )
                if age_min < 1:
                    return self._t("avatar.device_context.scan.just_now")
                if age_min < 60:
                    return self._t("avatar.device_context.scan.minutes_ago").format(m=age_min)
                return self._t("avatar.device_context.scan.hours_ago").format(h=max(1, age_min // 60))
            except ValueError:
                return raw[:16]

        def _on_device_profile_updated(self, profile: dict) -> None:
            from iuve_connect_agents.desktop.active.device_capability_scanner import format_human_summary_ru

            reason = str(profile.get("scan_reason") or "")
            scan_error = str(profile.get("scan_error") or "").strip()
            summary = format_human_summary_ru(profile)
            manual_refresh = reason == "manual_refresh" or reason.endswith("_manual_refresh")
            diagnostics_active = bool(getattr(self, "_diagnostics_device_scan_active", False))

            def _scan_timeout_reason() -> str:
                apps_count = int(profile.get("applications_count") or 0)
                if apps_count > 0:
                    return self._t("diagnostics.scan.status.interrupted.partial").format(count=apps_count)
                return self._t("diagnostics.scan.status.interrupted")

            def _apply() -> bool:
                self._refresh_avatar_context(force=True)
                if manual_refresh or diagnostics_active:
                    if scan_error.startswith("scan_timeout_"):
                        self._finish_diagnostics_device_scan_ui(
                            success=False,
                            profile=profile,
                            reason=_scan_timeout_reason(),
                        )
                    elif scan_error:
                        self._finish_diagnostics_device_scan_ui(
                            success=False,
                            reason=self._t("diagnostics.scan.status.failed").format(reason=scan_error),
                        )
                    else:
                        self._finish_diagnostics_device_scan_ui(success=True, profile=profile)
                elif reason in {"trusted_pair", "agent_start", "avatar_page_open"}:
                    self._avatar_append_message(
                        self._t("avatar.chat.avatar"),
                        safe_format_template(
                            self._t("avatar.chat.response.device_scan_onboarding"),
                            summary=summary,
                        ),
                    )
                return False

            self._glib.idle_add(_apply)

        def _read_cpu_usage_percent(self) -> int | None:
            try:
                with open("/proc/stat", "r", encoding="utf-8") as fh:
                    first = fh.readline().strip().split()
                if len(first) < 5 or first[0] != "cpu":
                    return None
                values = [int(v) for v in first[1:]]
                idle = values[3] + (values[4] if len(values) > 4 else 0)
                total = sum(values)
                prev_total = getattr(self, "_cpu_prev_total", None)
                prev_idle = getattr(self, "_cpu_prev_idle", None)
                self._cpu_prev_total = total
                self._cpu_prev_idle = idle
                if prev_total is None or prev_idle is None:
                    return None
                total_delta = total - prev_total
                idle_delta = idle - prev_idle
                if total_delta <= 0:
                    return None
                busy = max(0.0, (total_delta - idle_delta) / float(total_delta))
                return max(0, min(100, int(round(busy * 100))))
            except Exception:
                return None

        def _read_ram_usage_percent(self) -> int | None:
            try:
                totals = {}
                with open("/proc/meminfo", "r", encoding="utf-8") as fh:
                    for line in fh:
                        if ":" not in line:
                            continue
                        key, rest = line.split(":", 1)
                        parts = rest.strip().split()
                        if not parts:
                            continue
                        totals[key] = int(parts[0])
                total = totals.get("MemTotal")
                available = totals.get("MemAvailable")
                if not total or available is None or total <= 0:
                    return None
                used = max(0, total - available)
                return max(0, min(100, int(round((used / float(total)) * 100))))
            except Exception:
                return None

        def _read_disk_usage_percent(self) -> int | None:
            try:
                usage = shutil.disk_usage(Path.home())
                if usage.total <= 0:
                    return None
                return max(0, min(100, int(round((usage.used / float(usage.total)) * 100))))
            except Exception:
                return None

        @staticmethod
        def _format_device_name(devices_snapshot: dict[str, object]) -> str:
            raw = str(devices_snapshot.get("device_id") or "").strip()
            if raw:
                return raw
            return os.uname().nodename or "Local Device"

        def _wrap_page_scroll(self, gtk_module, child):
            scroller = gtk_module.ScrolledWindow()
            scroller.set_policy(gtk_module.PolicyType.AUTOMATIC, gtk_module.PolicyType.AUTOMATIC)
            scroller.set_vexpand(True)
            scroller.set_hexpand(True)
            scroller.set_child(child)
            return scroller

        def _placeholder_page(self, gtk_module, text_key: str):
            page = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=12)
            label = gtk_module.Label(label=self._t(text_key))
            label.set_wrap(True)
            label.set_xalign(0.0)
            self._i18n_labels.append((label, text_key))
            page.append(label)
            return page

        def _build_details_grid(self, gtk_module, rows: list[tuple[str, str]], store: dict[str, object]):
            grid = gtk_module.Grid(column_spacing=12, row_spacing=8)
            for idx, (label_key, key) in enumerate(rows):
                label_widget = gtk_module.Label(label=self._t(label_key))
                label_widget.set_xalign(0.0)
                value_widget = gtk_module.Label(label="-")
                value_widget.set_xalign(0.0)
                value_widget.set_wrap(True)
                store[key] = value_widget
                self._i18n_labels.append((label_widget, label_key))
                grid.attach(label_widget, 0, idx, 1, 1)
                grid.attach(value_widget, 1, idx, 1, 1)
            return grid

        def _build_devices_page(self, gtk_module):
            box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            box.append(self._placeholder_page(gtk_module, "devices.placeholder"))

            self._devices_auth_error_banner = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            self._devices_auth_error_banner.set_visible(False)
            self._devices_auth_error_label = gtk_module.Label(label="")
            self._devices_auth_error_label.set_xalign(0.0)
            self._devices_auth_error_label.set_wrap(True)
            self._devices_auth_error_hint = gtk_module.Label(label="")
            self._devices_auth_error_hint.set_xalign(0.0)
            self._devices_auth_error_hint.set_wrap(True)
            self._devices_auth_error_hint.add_css_class("dim-label")
            self._devices_auth_error_banner.append(self._devices_auth_error_label)
            self._devices_auth_error_banner.append(self._devices_auth_error_hint)
            box.append(self._devices_auth_error_banner)

            actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._devices_unpair_btn = gtk_module.Button(label=self._t("devices.unpair.button"))
            self._devices_unpair_btn.add_css_class("destructive-action")
            self._devices_unpair_btn.connect("clicked", self._on_devices_unpair_clicked)
            self._devices_unpair_btn.set_visible(False)
            actions.append(self._devices_unpair_btn)

            self._devices_repair_btn = gtk_module.Button(label=self._t("devices.repair.button"))
            self._devices_repair_btn.connect("clicked", self._on_devices_repair_clicked)
            self._devices_repair_btn.set_visible(False)
            actions.append(self._devices_repair_btn)

            self._devices_open_security_btn = gtk_module.Button(label=self._t("devices.open_security.link"))
            self._devices_open_security_btn.connect("clicked", self._on_devices_open_security_clicked)
            actions.append(self._devices_open_security_btn)
            box.append(actions)

            self._devices_revoke_distinct_hint = gtk_module.Label(label=self._t("devices.revoke_all.distinct.hint"))
            self._devices_revoke_distinct_hint.set_xalign(0.0)
            self._devices_revoke_distinct_hint.set_wrap(True)
            self._devices_revoke_distinct_hint.add_css_class("dim-label")
            box.append(self._devices_revoke_distinct_hint)

            self._devices_labels = {}
            rows = [
                ("label.paired", "paired"),
                ("label.device_id", "device_id"),
                ("label.device_state", "device_state"),
                ("label.trust_state", "trust_state"),
                ("label.runtime_state", "runtime_state"),
                ("label.last_heartbeat_at", "last_heartbeat_at"),
                ("label.pairing_timestamp", "pairing_timestamp"),
                ("label.backend_base_url", "backend_base_url"),
                ("label.admission_mode", "admission_mode"),
                ("label.allowlisted_command_channel", "allowlisted_command_channel"),
                ("label.hidden_actions", "hidden_actions"),
                ("label.pre_pair_actions", "pre_pair_actions"),
            ]
            box.append(self._build_details_grid(gtk_module, rows, self._devices_labels))
            return box

        def _build_diagnostics_page(self, gtk_module):
            box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)

            hero = gtk_module.Frame()
            hero_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            hero_box.set_margin_top(10)
            hero_box.set_margin_bottom(10)
            hero_box.set_margin_start(10)
            hero_box.set_margin_end(10)
            hero.set_child(hero_box)

            self._diagnostics_header_title = gtk_module.Label(label=self._t("diagnostics.page.title"))
            self._diagnostics_header_title.set_xalign(0.0)
            self._diagnostics_header_title.add_css_class("page-title")
            hero_box.append(self._diagnostics_header_title)

            self._diagnostics_header_subtitle = gtk_module.Label(label=self._t("diagnostics.page.subtitle"))
            self._diagnostics_header_subtitle.set_xalign(0.0)
            self._diagnostics_header_subtitle.set_wrap(True)
            hero_box.append(self._diagnostics_header_subtitle)
            box.append(hero)

            self._diagnostics_refresh_scan_btn = gtk_module.Button(
                label=self._t("diagnostics.action.refresh_scan")
            )
            self._diagnostics_refresh_scan_btn.connect("clicked", self._on_diagnostics_refresh_scan_clicked)
            self._i18n_labels.append((self._diagnostics_refresh_scan_btn, "diagnostics.action.refresh_scan"))
            hero_box.append(self._diagnostics_refresh_scan_btn)

            self._diagnostics_scan_status_label = gtk_module.Label(label="")
            self._diagnostics_scan_status_label.set_xalign(0.0)
            self._diagnostics_scan_status_label.set_wrap(True)
            self._diagnostics_scan_status_label.set_visible(False)
            hero_box.append(self._diagnostics_scan_status_label)

            self._diagnostics_labels = {}
            self._diagnostics_health_values = {}

            health_card = gtk_module.Frame()
            health_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            health_box.set_margin_top(10)
            health_box.set_margin_bottom(10)
            health_box.set_margin_start(10)
            health_box.set_margin_end(10)
            health_card.set_child(health_box)
            health_title = gtk_module.Label(label=self._t("diagnostics.health.title"))
            health_title.set_xalign(0.0)
            health_title.add_css_class("heading")
            health_box.append(health_title)

            def _health_row(title_key: str, value_key: str):
                row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                title = gtk_module.Label(label=self._t(title_key))
                title.set_xalign(0.0)
                title.set_hexpand(True)
                value = gtk_module.Label(label="-")
                value.set_xalign(1.0)
                row.append(title)
                row.append(value)
                health_box.append(row)
                self._diagnostics_health_values[value_key] = value

            _health_row("diagnostics.health.connection", "connection")
            _health_row("diagnostics.health.trust", "trust")
            _health_row("diagnostics.health.heartbeat", "heartbeat")
            _health_row("diagnostics.health.logs", "logs")
            box.append(health_card)

            self._diagnostics_details_expander = gtk_module.Expander() if hasattr(gtk_module, "Expander") else None
            details_card = gtk_module.Frame()
            details_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            details_box.set_margin_top(10)
            details_box.set_margin_bottom(10)
            details_box.set_margin_start(10)
            details_box.set_margin_end(10)
            details_card.set_child(details_box)
            details_title = gtk_module.Label(label=self._t("diagnostics.advanced.title"))
            details_title.set_xalign(0.0)
            details_title.add_css_class("heading")
            details_box.append(details_title)

            rows = [
                ("label.service_status", "service_status"),
                ("label.runtime_state", "runtime_state"),
                ("label.heartbeat_age_sec", "heartbeat_age_sec"),
                ("label.failure_count", "failure_count"),
                ("label.last_error", "last_error"),
                ("label.logs_path", "logs_path"),
                ("label.storage_path", "storage_path"),
                ("label.status_source_version", "status_source_version"),
                ("label.signed_bootstrap_required", "signed_bootstrap_required"),
            ]
            details_box.append(self._build_details_grid(gtk_module, rows, self._diagnostics_labels))
            box.append(details_card)

            box.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))
            box.append(self._build_command_log_panel(gtk_module))
            return box

        def _build_security_page(self, gtk_module):
            self._security_labels = {}

            scroll = gtk_module.ScrolledWindow()
            scroll.set_policy(gtk_module.PolicyType.AUTOMATIC, gtk_module.PolicyType.AUTOMATIC)
            scroll.set_vexpand(True)
            scroll.set_hexpand(True)

            page = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=12)
            page.set_margin_top(8)
            page.set_margin_bottom(12)
            page.set_margin_start(4)
            page.set_margin_end(4)
            scroll.set_child(page)

            page.append(self._build_security_status_cards(gtk_module))
            page.append(self._build_recommended_safe_mode_bar(gtk_module))
            page.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))
            page.append(self._build_cap_page_header(gtk_module))
            page.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))

            self._cap_selection_pending: dict[str, bool] = {key: False for key in UI_CAPABILITY_KEYS}
            self._cap_selection_dirty = False
            self._selected_profile_lifetime = "app_exit"
            page.append(self._build_security_profile_cards(gtk_module))
            page.append(self._build_profile_summary_panel(gtk_module))

            page.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))
            page.append(self._build_action_sessions_panel(gtk_module))
            page.append(self._build_cap_bottom_bar(gtk_module))
            page.append(self._build_security_advanced_expander(gtk_module))
            page.append(self._build_security_additional_expander(gtk_module))
            return scroll

        def _build_security_status_cards(self, gtk_module):
            outer = gtk_module.FlowBox()
            outer.set_selection_mode(gtk_module.SelectionMode.NONE)
            outer.set_min_children_per_line(2)
            outer.set_max_children_per_line(3)
            outer.set_column_spacing(8)
            outer.set_row_spacing(8)
            outer.set_hexpand(True)
            self._security_status_cards: dict[str, object] = {}

            def _card(title_key: str, value_key: str) -> None:
                frame = gtk_module.Frame()
                body = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
                body.set_margin_top(10)
                body.set_margin_bottom(10)
                body.set_margin_start(10)
                body.set_margin_end(10)
                title = gtk_module.Label(label=self._t(title_key))
                title.set_xalign(0.0)
                title.add_css_class("dim-label")
                value = gtk_module.Label(label="—")
                value.set_xalign(0.0)
                value.set_wrap(True)
                value.add_css_class("heading")
                body.append(title)
                body.append(value)
                frame.set_child(body)
                outer.append(frame)
                self._security_status_cards[value_key] = value

            _card("security.status.card.this_device", "this_device")
            _card("security.status.card.connected", "connected")
            _card("security.status.card.trusted", "trusted_device")
            _card("security.status.card.permissions", "current_permissions")
            _card("security.status.card.sessions", "active_sessions")
            _card("security.status.card.time_remaining", "time_remaining")

            revoke_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            revoke_row.set_margin_top(4)
            self._security_revoke_all_btn = gtk_module.Button(
                label=self._t("security.status.revoke_all")
            )
            self._security_revoke_all_btn.add_css_class("destructive-action")
            self._security_revoke_all_btn.connect("clicked", self._on_cap_revoke_all_clicked)
            revoke_row.append(self._security_revoke_all_btn)

            wrapper = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            wrapper.append(outer)
            wrapper.append(revoke_row)
            return wrapper

        def _build_recommended_safe_mode_bar(self, gtk_module):
            row = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            hint = gtk_module.Label(label=self._t("security.recommended_safe.hint"))
            hint.set_xalign(0.0)
            hint.set_wrap(True)
            hint.add_css_class("dim-label")
            self._recommended_safe_hint = hint
            row.append(hint)
            self._recommended_safe_btn = gtk_module.Button(
                label=self._t("security.recommended_safe.button")
            )
            self._recommended_safe_btn.add_css_class("suggested-action")
            self._recommended_safe_btn.set_halign(gtk_module.Align.START)
            self._recommended_safe_btn.connect("clicked", self._on_recommended_safe_mode_clicked)
            row.append(self._recommended_safe_btn)
            return row

        def _on_recommended_safe_mode_clicked(self, _btn=None) -> None:
            self._present_profile_safety_dialog(
                RECOMMENDED_SAFE_PROFILE_KEY,
                lifetime=RECOMMENDED_SAFE_LIFETIME_KEY,
            )

        def _present_profile_safety_dialog(self, preset_key: str, *, lifetime: str | None = None) -> None:
            resolved_lifetime = lifetime or self._selected_profile_lifetime_key()
            title = self._t("security.safety.summary.title")
            body = build_safety_summary_body(
                profile_key=preset_key,
                lifetime=resolved_lifetime,
                translate_fn=self._t,
            )
            dialog = adw_module.MessageDialog(transient_for=self, heading=title, body=body)
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.grant_selection.apply"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_d, response):
                if response == "confirm":
                    blocked = self._adapter.read_plan_blocked_ui_keys()
                    pending = apply_permission_preset_selection(preset_key, blocked_keys=blocked)
                    for cap_key, selected in pending.items():
                        self._cap_selection_pending[cap_key] = selected
                    self._adapter.apply_permission_profile(preset_key, lifetime=resolved_lifetime)
                    self._cap_selection_dirty = False
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _build_security_profile_cards(self, gtk_module):
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            title = gtk_module.Label(label=self._t("cap.grants.profile.title"))
            title.set_xalign(0.0)
            title.add_css_class("heading")
            self._cap_grants_permanent_title = title
            outer.append(title)
            subtitle = gtk_module.Label(label=self._t("cap.grants.profile.subtitle"))
            subtitle.set_xalign(0.0)
            subtitle.set_wrap(True)
            subtitle.add_css_class("dim-label")
            self._cap_grants_permanent_subtitle = subtitle
            outer.append(subtitle)

            cards = gtk_module.FlowBox()
            cards.set_selection_mode(gtk_module.SelectionMode.NONE)
            cards.set_min_children_per_line(1)
            cards.set_max_children_per_line(2)
            cards.set_column_spacing(8)
            cards.set_row_spacing(8)
            cards.set_hexpand(True)
            cards.set_homogeneous(True)

            self._security_profile_cards: dict[str, dict[str, object]] = {}
            for preset_key in PROFILE_PRESET_KEYS:
                card_btn = gtk_module.Button()
                card_btn.add_css_class("card")
                card_btn.set_halign(gtk_module.Align.FILL)
                card_btn.set_hexpand(True)
                card_body = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
                card_body.set_margin_top(12)
                card_body.set_margin_bottom(12)
                card_body.set_margin_start(12)
                card_body.set_margin_end(12)
                name_lbl = gtk_module.Label(label=self._t(profile_display_i18n_key(preset_key)))
                name_lbl.set_xalign(0.0)
                name_lbl.add_css_class("heading")
                summary_lbl = gtk_module.Label(label=self._t(f"{profile_display_i18n_key(preset_key)}.summary"))
                summary_lbl.set_xalign(0.0)
                summary_lbl.set_wrap(True)
                card_body.append(name_lbl)
                card_body.append(summary_lbl)
                card_btn.set_child(card_body)
                card_btn.connect("clicked", lambda _b, pk=preset_key: self._on_security_profile_card_clicked(pk))
                cards.append(card_btn)
                self._security_profile_cards[preset_key] = {
                    "button": card_btn,
                    "name": name_lbl,
                    "summary": summary_lbl,
                }
            outer.append(cards)

            lifetime_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            lifetime_lbl = gtk_module.Label(label=self._t("profile.lifetime.label"))
            lifetime_lbl.set_xalign(0.0)
            self._profile_lifetime_label = lifetime_lbl
            lifetime_row.append(lifetime_lbl)
            self._profile_lifetime_drop = gtk_module.DropDown()
            self._profile_lifetime_drop.set_hexpand(True)
            lifetime_row.append(self._profile_lifetime_drop)
            outer.append(lifetime_row)

            action_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._cap_apply_btn = gtk_module.Button(label=self._t("cap.grant_selection.apply"))
            self._cap_apply_btn.add_css_class("suggested-action")
            self._cap_apply_btn.connect("clicked", self._on_cap_apply_permissions_clicked)
            action_row.append(self._cap_apply_btn)
            self._cap_selection_count_lbl = gtk_module.Label(label="")
            self._cap_selection_count_lbl.set_xalign(0.0)
            action_row.append(self._cap_selection_count_lbl)
            outer.append(action_row)
            self._refresh_profile_lifetime_dropdown()
            return outer

        def _refresh_profile_lifetime_dropdown(self) -> None:
            if not hasattr(self, "_profile_lifetime_drop"):
                return
            snap = self._adapter.read_dashboard_snapshot()
            sec = self._adapter.read_security_snapshot()
            trust = str(snap.get("trust_state") or sec.get("active_trust_state") or "")
            paired = bool(snap.get("paired", False))
            state = self._adapter._agent().pairing.storage.load_state()
            authenticated = bool(state and str(getattr(state, "device_auth_token", "") or "").strip())
            allowed = sorted(
                allowed_session_lifetimes(
                    trust_state=trust,
                    paired=paired,
                    authenticated=authenticated,
                ),
                key=lambda k: SESSION_LIFETIME_KEYS.index(k) if k in SESSION_LIFETIME_KEYS else 99,
            )
            labels = gtk_module.StringList.new([self._t(f"profile.lifetime.{k}") for k in allowed])
            self._profile_lifetime_drop.set_model(labels)
            current = getattr(self, "_selected_profile_lifetime", "app_exit")
            if current not in allowed:
                current = allowed[0] if allowed else "app_exit"
                self._selected_profile_lifetime = current
            try:
                self._profile_lifetime_drop.set_selected(allowed.index(current))
            except ValueError:
                self._profile_lifetime_drop.set_selected(0)

        def _selected_profile_lifetime_key(self) -> str:
            if not hasattr(self, "_profile_lifetime_drop"):
                return getattr(self, "_selected_profile_lifetime", "app_exit")
            snap = self._adapter.read_dashboard_snapshot()
            sec = self._adapter.read_security_snapshot()
            trust = str(snap.get("trust_state") or sec.get("active_trust_state") or "")
            paired = bool(snap.get("paired", False))
            state = self._adapter._agent().pairing.storage.load_state()
            authenticated = bool(state and str(getattr(state, "device_auth_token", "") or "").strip())
            allowed = sorted(
                allowed_session_lifetimes(
                    trust_state=trust,
                    paired=paired,
                    authenticated=authenticated,
                ),
                key=lambda k: SESSION_LIFETIME_KEYS.index(k) if k in SESSION_LIFETIME_KEYS else 99,
            )
            idx = int(self._profile_lifetime_drop.get_selected())
            if 0 <= idx < len(allowed):
                return allowed[idx]
            return "app_exit"

        def _build_profile_summary_panel(self, gtk_module):
            frame = gtk_module.Frame()
            frame.set_margin_top(4)
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            outer.set_margin_top(8)
            outer.set_margin_bottom(8)
            outer.set_margin_start(8)
            outer.set_margin_end(8)
            frame.set_child(outer)
            title = gtk_module.Label(label=self._t("profile.summary.title"))
            title.set_xalign(0.0)
            title.add_css_class("heading")
            self._profile_summary_title = title
            outer.append(title)
            self._profile_summary_rows: dict[str, object] = {}
            for key, label_key in (
                ("profile", "profile.summary.active"),
                ("capabilities", "profile.summary.capabilities"),
                ("lifetime", "profile.summary.lifetime"),
                ("risk", "profile.summary.risk"),
                ("audit", "profile.summary.audit"),
            ):
                row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                lbl = gtk_module.Label(label=self._t(label_key))
                lbl.set_xalign(0.0)
                lbl.set_width_chars(18)
                lbl.add_css_class("dim-label")
                val = gtk_module.Label(label="—")
                val.set_xalign(0.0)
                val.set_wrap(True)
                val.set_hexpand(True)
                row.append(lbl)
                row.append(val)
                outer.append(row)
                self._profile_summary_rows[key] = val
            return frame

        def _refresh_profile_summary_panel(self) -> None:
            if not hasattr(self, "_profile_summary_rows"):
                return
            try:
                summary = self._adapter.read_permission_profile_summary()
                profile_key = str(summary.get("profile_key") or "minimal")
                lifetime = str(summary.get("lifetime") or "app_exit")
                audit_status = str(summary.get("audit_status") or "none")
                rows = build_profile_summary(
                    profile_key=profile_key,
                    lifetime=lifetime,
                    audit_status=audit_status,
                    translate_fn=self._t,
                )
                for key, val_lbl in self._profile_summary_rows.items():
                    val_lbl.set_label(str(rows.get(key) or "—"))
            except Exception:
                pass

        def _on_security_profile_card_clicked(self, preset_key: str) -> None:
            blocked = self._adapter.read_plan_blocked_ui_keys()
            pending = apply_permission_preset_selection(preset_key, blocked_keys=blocked)
            for cap_key, selected in pending.items():
                self._cap_selection_pending[cap_key] = selected
                widgets = self._cap_widgets.get(cap_key, {})
                cb = widgets.get("checkbox")
                if cb is not None:
                    cb.set_active(selected)
            self._update_cap_selection_count_label()
            lifetime = self._selected_profile_lifetime_key()
            self._present_profile_safety_dialog(preset_key, lifetime=lifetime)

        def _build_security_advanced_expander(self, gtk_module):
            expander = gtk_module.Expander()
            expander.set_label(self._t("security.advanced.title"))
            expander.set_expanded(False)
            self._cap_manual_expander = expander

            inner = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            hint = gtk_module.Label(label=self._t("security.advanced.subtitle"))
            hint.set_xalign(0.0)
            hint.set_wrap(True)
            hint.add_css_class("dim-label")
            self._cap_grant_selection_hint = hint
            inner.append(hint)
            inner.append(self._build_cap_table_section(gtk_module))
            inner.append(self._build_active_panel(gtk_module))
            expander.set_child(inner)
            return expander

        def _build_security_additional_expander(self, gtk_module):
            expander = gtk_module.Expander()
            expander.set_label(self._t("security.additional.title"))
            expander.set_expanded(False)
            self._security_additional_expander = expander
            expander.set_child(self._build_ai_providers_panel(gtk_module))
            return expander

        def _refresh_security_status_cards(self) -> None:
            if not hasattr(self, "_security_status_cards"):
                return
            try:
                snap = self._adapter.read_dashboard_snapshot()
                sec = self._adapter.read_security_snapshot()
                summary = self._adapter.read_permission_profile_summary()
                sessions = dedupe_sessions_by_preset(self._adapter.list_active_action_sessions())
                devices = self._adapter.read_devices_snapshot()
                owner = self._adapter.read_owner_display_snapshot()
                profile = self._adapter.fetch_owner_profile() or {}
                values = build_security_status_snapshot(
                    paired=bool(snap.get("paired", False)),
                    trust_state=str(snap.get("trust_state") or sec.get("active_trust_state") or ""),
                    profile_key=str(summary.get("profile_key") or "minimal"),
                    active_session_count=len(sessions),
                    active_sessions=sessions,
                    translate_fn=self._t,
                    device_name=str(owner.get("device_name") or profile.get("name") or ""),
                    hostname=str(profile.get("hostname") or os.uname().nodename or ""),
                    device_id=str(devices.get("device_id") or profile.get("id") or ""),
                )
                for key, label in self._security_status_cards.items():
                    label.set_label(str(values.get(key) or "—"))
            except Exception:
                pass

        def _refresh_security_status_line(self) -> None:
            self._refresh_security_status_cards()

        def _build_pending_confirmation_banner(self, gtk_module):
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            outer.add_css_class("pending-confirmation-banner")
            outer.set_margin_top(4)
            outer.set_margin_bottom(4)
            outer.set_margin_start(6)
            outer.set_margin_end(6)
            self._pending_confirmation_title = gtk_module.Label(
                label=self._t("cap.pending_confirmation.title")
            )
            self._pending_confirmation_title.set_xalign(0.0)
            self._pending_confirmation_title.add_css_class("pending-confirmation-title")
            outer.append(self._pending_confirmation_title)
            self._pending_confirmation_body = gtk_module.Label(label="")
            self._pending_confirmation_body.set_xalign(0.0)
            self._pending_confirmation_body.set_wrap(True)
            outer.append(self._pending_confirmation_body)
            actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._pending_confirmation_open_btn = gtk_module.Button(
                label=self._t("cap.pending_confirmation.open_dialog")
            )
            self._pending_confirmation_open_btn.add_css_class("suggested-action")
            self._pending_confirmation_open_btn.connect(
                "clicked", self._on_pending_confirmation_open_dialog
            )
            actions.append(self._pending_confirmation_open_btn)
            self._pending_confirmation_decline_btn = gtk_module.Button(
                label=self._t("dialog.command.confirm.decline")
            )
            self._pending_confirmation_decline_btn.add_css_class("destructive-action")
            self._pending_confirmation_decline_btn.connect(
                "clicked", lambda _b: self._on_pending_confirmation_response(approve=False)
            )
            actions.append(self._pending_confirmation_decline_btn)
            outer.append(actions)
            outer.set_visible(False)
            return outer

        def _refresh_pending_confirmation_banner(self) -> None:
            if not hasattr(self, "_pending_confirmation_banner"):
                return
            pending = self._adapter.read_pending_command_confirmation()
            if not pending_requires_user_action(pending):
                self._pending_confirmation_banner.set_visible(False)
                self._pending_command_id = ""
                return
            command_id = str(pending.get("command_id") or pending.get("request_id") or "").strip()
            command_type = str(pending.get("command_type") or "?")
            self._pending_command_id = command_id
            response_submitted = self._adapter.has_command_confirmation_response(command_id)
            if response_submitted:
                self._pending_confirmation_banner.set_visible(False)
                self._pending_command_id = ""
                return
            else:
                self._pending_confirmation_body.set_label(
                    self._t("cap.pending_confirmation.body").format(
                        command=command_type,
                        command_id=command_id[:8],
                    )
                )
                self._pending_confirmation_open_btn.set_sensitive(True)
                self._pending_confirmation_decline_btn.set_sensitive(True)
            self._pending_confirmation_banner.set_visible(True)

        def _pending_needs_owner_decision(self, pending: dict | None) -> bool:
            if not isinstance(pending, dict):
                return False
            command_id = str(pending.get("command_id") or pending.get("request_id") or "").strip()
            response_submitted = bool(
                command_id and self._adapter.has_command_confirmation_response(command_id)
            )
            return pending_awaiting_owner_decision(
                pending,
                response_submitted=response_submitted,
            )

        def _dismiss_active_confirmation_dialog(self) -> None:
            if self._active_confirmation_dialog is not None:
                try:
                    self._active_confirmation_dialog.close()
                except Exception:
                    pass
            self._reset_confirmation_dialog_state()

        def _on_pending_confirmation_open_dialog(self, _btn=None) -> None:
            pending = self._adapter.read_pending_command_confirmation()
            if not self._pending_needs_owner_decision(pending):
                return
            self._dismiss_active_confirmation_dialog()
            self._present_pending_confirmation_for_banner(pending)

        def _submit_pending_confirmation_approve(self, command_id: str) -> None:
            if not command_id:
                return
            self._adapter.submit_command_confirmation_response(command_id, approve=True)
            self._refresh_pending_confirmation_banner()
            self._refresh_command_log_panel()

        def _present_pending_confirmation_for_banner(self, pending: dict) -> None:
            """Banner confirm: GTK AlertDialog first (KDE-safe), then direct approve."""
            command_id = str(pending.get("command_id") or pending.get("request_id") or "").strip()
            command_type = str(pending.get("command_type") or "?")
            if not command_id:
                return
            title = self._t("dialog.command.confirm.title")
            body = self._t("cap.pending_confirmation.fallback_body").format(command=command_type)
            confirm_label = self._t("dialog.command.confirm.execute")
            cancel_label = self._t("dialog.command.confirm.decline")

            def _approve() -> None:
                self._submit_pending_confirmation_approve(command_id)

            self._bring_window_to_front()
            outcome = present_confirm_alert(
                gtk_module,
                parent=self,
                title=title,
                body=body,
                confirm_label=confirm_label,
                cancel_label=cancel_label,
                on_approve=_approve,
                glib_idle_add=self._glib.idle_add,
            )
            if outcome == "unavailable":
                # Red banner already warned; Adw modals fail on KDE — approve via bridge file.
                _approve()

        def _bring_window_to_front(self) -> None:
            try:
                self.present()
            except Exception:
                pass
            try:
                self.set_visible(True)
            except Exception:
                pass
            try:
                self.grab_focus()
            except Exception:
                pass
            try:
                self.set_keep_above(True)
                self._glib.timeout_add(400, self._clear_keep_above_once)
            except Exception:
                pass

        def _clear_keep_above_once(self) -> bool:
            try:
                self.set_keep_above(False)
            except Exception:
                pass
            return False

        def _reset_confirmation_dialog_state(self) -> None:
            self._confirmation_dialog_active = False
            self._active_confirmation_dialog = None

        def _on_pending_confirmation_response(self, *, approve: bool) -> None:
            command_id = str(getattr(self, "_pending_command_id", "") or "").strip()
            if not command_id:
                return
            if self._adapter.has_command_confirmation_response(command_id):
                return
            self._adapter.submit_command_confirmation_response(command_id, approve=approve)
            self._refresh_pending_confirmation_banner()
            self._refresh_command_log_panel()

        _EXPIRY_PRESET_KEYS: list[str] = ["15m", "1h", "24h", "reboot", "one_shot", "permanent"]

        def _build_ai_providers_panel(self, gtk_module):
            from .ai_providers_panel import (
                format_provider_account_label,
                format_provider_online_label,
                open_official_url,
                provider_row_summary,
            )

            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            title = gtk_module.Label(label=self._t("ai_providers.title"))
            title.set_xalign(0.0)
            title.add_css_class("page-title")
            outer.append(title)
            subtitle = gtk_module.Label(label=self._t("ai_providers.subtitle"))
            subtitle.set_xalign(0.0)
            subtitle.set_wrap(True)
            outer.append(subtitle)
            self._i18n_labels.append((title, "ai_providers.title"))
            self._i18n_labels.append((subtitle, "ai_providers.subtitle"))

            toolbar = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._ai_providers_refresh_btn = gtk_module.Button(label=self._t("ai_providers.action.refresh"))
            self._ai_providers_refresh_btn.connect("clicked", lambda _b: self._refresh_ai_providers_panel())
            self._i18n_labels.append((self._ai_providers_refresh_btn, "ai_providers.action.refresh"))
            toolbar.append(self._ai_providers_refresh_btn)
            outer.append(toolbar)

            self._ai_providers_list_box = gtk_module.ListBox()
            self._ai_providers_list_box.set_selection_mode(gtk_module.SelectionMode.NONE)
            self._ai_providers_list_box.add_css_class("boxed-list")
            outer.append(self._ai_providers_list_box)

            self._ai_providers_status_label = gtk_module.Label(label="")
            self._ai_providers_status_label.set_xalign(0.0)
            self._ai_providers_status_label.set_wrap(True)
            outer.append(self._ai_providers_status_label)

            self._ai_provider_rows: dict[str, dict[str, object]] = {}
            self._glib.idle_add(self._refresh_ai_providers_panel)
            return outer

        def _refresh_ai_providers_panel(self) -> bool:
            self._begin_ai_providers_refresh_ui()

            def _worker() -> None:
                providers, error_key = self._adapter.fetch_avatar_providers()
                self._glib.idle_add(self._finish_apply_ai_providers_panel, providers, error_key)

            threading.Thread(target=_worker, daemon=True).start()
            return False

        def _begin_ai_providers_refresh_ui(self) -> None:
            if hasattr(self, "_ai_providers_refresh_btn"):
                self._ai_providers_refresh_btn.set_sensitive(False)
                self._ai_providers_refresh_btn.set_label(self._t("ai_providers.action.refreshing"))
            if hasattr(self, "_ai_providers_status_label"):
                self._ai_providers_status_label.set_label(self._t("ai_providers.loading"))

        def _finish_apply_ai_providers_panel(
            self,
            providers: list[dict[str, object]],
            error_key: str | None,
        ) -> bool:
            if hasattr(self, "_ai_providers_refresh_btn"):
                self._ai_providers_refresh_btn.set_sensitive(True)
                self._ai_providers_refresh_btn.set_label(self._t("ai_providers.action.refresh"))
            return self._apply_ai_providers_panel(providers, error_key)

        def _apply_ai_providers_panel(
            self,
            providers: list[dict[str, object]],
            error_key: str | None,
        ) -> bool:
            from .ai_providers_panel import (
                format_provider_account_label,
                format_provider_online_label,
                format_connection_hint_label,
                provider_connect_action_allowed,
                provider_complete_login_allowed,
                provider_row_summary,
            )

            if not hasattr(self, "_ai_providers_list_box"):
                return False

            while True:
                row = self._ai_providers_list_box.get_row_at_index(0)
                if row is None:
                    break
                self._ai_providers_list_box.remove(row)

            if error_key:
                self._ai_providers_status_label.set_label(self._t(error_key))
                return False

            if not providers:
                self._ai_providers_status_label.set_label(self._t("ai_providers.empty"))
                return False

            self._ai_providers_status_label.set_label(self._t("ai_providers.sync.ok"))
            self._ai_provider_rows = {}

            for provider in providers:
                provider_id = str(provider.get("provider_id") or "")
                name = str(provider.get("name") or provider_id)
                health = provider.get("health") if isinstance(provider.get("health"), dict) else {}
                availability = str(health.get("availability") or "offline")
                account_status = str(provider.get("account_status") or "not_connected")
                connected = bool(provider.get("account_connected"))
                is_local = bool(provider.get("is_local"))
                enabled = bool(provider.get("enabled", True))

                row_outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
                row_outer.set_margin_top(8)
                row_outer.set_margin_bottom(8)
                row_outer.set_margin_start(8)
                row_outer.set_margin_end(8)

                header = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                name_label = gtk_module.Label(label=name)
                name_label.set_xalign(0.0)
                name_label.set_hexpand(True)
                name_label.add_css_class("heading")
                header.append(name_label)
                status_label = gtk_module.Label(
                    label=format_provider_online_label(self._t, availability)
                )
                header.append(status_label)
                account_label = gtk_module.Label(
                    label=format_provider_account_label(self._t, account_status, connected)
                )
                header.append(account_label)
                row_outer.append(header)

                detail = gtk_module.Label(label=provider_row_summary(provider, t=self._t))
                detail.set_xalign(0.0)
                detail.set_wrap(True)
                row_outer.append(detail)

                hint = str(provider.get("connection_hint") or "").strip()
                if hint:
                    hint_label = gtk_module.Label(label=format_connection_hint_label(self._t, hint))
                    hint_label.set_xalign(0.0)
                    hint_label.set_wrap(True)
                    row_outer.append(hint_label)

                actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=6)
                if not is_local:
                    if provider_complete_login_allowed(provider):
                        confirm_btn = gtk_module.Button(label=self._t("ai_providers.action.confirm_login"))
                        confirm_btn.connect(
                            "clicked",
                            lambda _b, pid=provider_id, pname=name: self._prompt_ai_provider_complete(pid, pname),
                        )
                        actions.append(confirm_btn)
                    elif provider_connect_action_allowed(provider):
                        connect_btn = gtk_module.Button(label=self._t("ai_providers.action.connect"))
                        connect_btn.connect(
                            "clicked",
                            lambda _b, pid=provider_id, pdata=provider: self._on_ai_provider_connect(pid, pdata),
                        )
                        actions.append(connect_btn)
                    if not connected:
                        reg_url = str(provider.get("registration_url") or "")
                        if reg_url.startswith("https://"):
                            reg_btn = gtk_module.Button(label=self._t("ai_providers.action.register"))
                            reg_btn.connect(
                                "clicked",
                                lambda _b, url=reg_url: webbrowser.open(url),
                            )
                            actions.append(reg_btn)
                    else:
                        disconnect_btn = gtk_module.Button(label=self._t("ai_providers.action.disconnect"))
                        disconnect_btn.connect(
                            "clicked",
                            lambda _b, pid=provider_id, pname=name: self._on_ai_provider_disconnect(pid, pname),
                        )
                        actions.append(disconnect_btn)

                toggle_label = (
                    self._t("ai_providers.action.disable")
                    if enabled
                    else self._t("ai_providers.action.enable")
                )
                toggle_btn = gtk_module.Button(label=toggle_label)
                toggle_btn.connect(
                    "clicked",
                    lambda _b, pid=provider_id, en=enabled: self._on_ai_provider_toggle(pid, not en),
                )
                actions.append(toggle_btn)
                row_outer.append(actions)

                list_row = gtk_module.ListBoxRow()
                list_row.set_child(row_outer)
                self._ai_providers_list_box.append(list_row)
                self._ai_provider_rows[provider_id] = provider

            return False

        def _on_ai_provider_connect(self, provider_id: str, provider: dict) -> None:
            from .ai_providers_panel import open_official_url

            name = str(provider.get("name") or provider_id)
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("ai_providers.consent.title"),
                body=self._t("ai_providers.consent.body").format(provider=name),
            )
            dialog.add_response("cancel", self._t("ai_providers.consent.cancel"))
            dialog.add_response("confirm", self._t("ai_providers.consent.confirm"))
            dialog.set_default_response("confirm")
            dialog.set_close_response("cancel")

            def _respond(dlg, response: str) -> None:
                dlg.close()
                if response != "confirm":
                    return
                result = self._adapter.connect_avatar_provider(provider_id, complete=False)
                if not result.get("ok"):
                    return
                payload = result.get("result") if isinstance(result.get("result"), dict) else {}
                login_url = str(payload.get("login_url") or provider.get("login_url") or "")
                open_official_url(login_url)
                self._prompt_ai_provider_complete(provider_id, name)

            dialog.connect("response", _respond)
            dialog.present()

        def _prompt_ai_provider_complete(self, provider_id: str, name: str) -> None:
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("ai_providers.complete.title"),
                body=self._t("ai_providers.complete.body").format(provider=name),
            )
            dialog.add_response("cancel", self._t("ai_providers.consent.cancel"))
            dialog.add_response("confirm", self._t("ai_providers.complete.confirm"))
            dialog.set_default_response("confirm")
            dialog.set_close_response("cancel")

            def _respond(dlg, response: str) -> None:
                dlg.close()
                if response != "confirm":
                    return
                self._adapter.connect_avatar_provider(provider_id, complete=True)
                self._refresh_ai_providers_panel()

            dialog.connect("response", _respond)
            dialog.present()

        def _on_ai_provider_disconnect(self, provider_id: str, name: str) -> None:
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("ai_providers.disconnect.title"),
                body=self._t("ai_providers.disconnect.body").format(provider=name),
            )
            dialog.add_response("cancel", self._t("ai_providers.consent.cancel"))
            dialog.add_response("confirm", self._t("ai_providers.disconnect.confirm"))
            dialog.set_default_response("confirm")
            dialog.set_close_response("cancel")

            def _respond(dlg, response: str) -> None:
                dlg.close()
                if response != "confirm":
                    return
                self._adapter.disconnect_avatar_provider(provider_id)
                self._refresh_ai_providers_panel()

            dialog.connect("response", _respond)
            dialog.present()

        def _on_ai_provider_toggle(self, provider_id: str, enabled: bool) -> None:
            self._adapter.patch_avatar_provider(provider_id, enabled=enabled)
            self._refresh_ai_providers_panel()

        def _build_cap_page_header(self, gtk_module):
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=10)
            hbox = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=16)
            title_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            title_box.set_hexpand(True)
            self._cap_page_title = gtk_module.Label(label=self._t("cap.page.title"))
            self._cap_page_title.set_xalign(0.0)
            self._cap_page_title.add_css_class("page-title")
            title_box.append(self._cap_page_title)
            self._i18n_labels.append((self._cap_page_title, "cap.page.title"))
            self._cap_page_subtitle = gtk_module.Label(label=self._t("cap.page.subtitle"))
            self._cap_page_subtitle.set_xalign(0.0)
            self._cap_page_subtitle.set_wrap(True)
            self._cap_page_subtitle.set_max_width_chars(68)
            title_box.append(self._cap_page_subtitle)
            self._i18n_labels.append((self._cap_page_subtitle, "cap.page.subtitle"))
            hbox.append(title_box)
            outer.append(hbox)
            return outer

        def _build_bottom_status_bar(self, gtk_module):
            bar = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=12)
            bar.add_css_class("toolbar")

            def _slot(title_key: str):
                wrap = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=4)
                title = gtk_module.Label(label=f"{self._t(title_key)}:")
                title.set_sensitive(False)
                self._i18n_labels.append((title, title_key))
                value = gtk_module.Label(label="—")
                value.add_css_class("footer-value")
                wrap.append(title)
                wrap.append(value)
                bar.append(wrap)
                return value

            self._footer_connected_value = _slot("footer.connected")
            self._footer_trust_value = _slot("footer.trust")
            self._footer_heartbeat_value = _slot("footer.heartbeat")
            self._footer_backend_value = _slot("footer.backend")
            self._expires_in_label = _slot("footer.expires")
            return bar

        def _build_cap_status_bar(self, gtk_module):
            hbox = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._cap_status_widgets: dict[str, object] = {}

            def _add(key: str, label_key: str):
                if self._cap_status_widgets:
                    sep = gtk_module.Label(label=" | ")
                    sep.set_sensitive(False)
                    hbox.append(sep)
                pfx = gtk_module.Label(label=f"{self._t(label_key)}: ")
                pfx.set_sensitive(False)
                self._i18n_labels.append((pfx, label_key))
                hbox.append(pfx)
                val = gtk_module.Label(label="—")
                val.set_xalign(0.0)
                self._cap_status_widgets[key] = val
                hbox.append(val)

            _add("model", "cap.status_bar.model")
            _add("trust", "label.trust_state")
            _add("runtime", "label.runtime_state")
            _add("consent", "cap.status_bar.consent")
            _add("heartbeat", "cap.status_bar.heartbeat")
            return hbox

        def _build_cap_grant_selection_bar(self, gtk_module):
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            title = gtk_module.Label(label=self._t("cap.grant_selection.title"))
            title.set_xalign(0.0)
            title.add_css_class("heading")
            self._cap_grant_selection_title = title
            self._i18n_labels.append((title, "cap.grant_selection.title"))
            outer.append(title)
            hint = gtk_module.Label(label=self._t("cap.grant_selection.hint"))
            hint.set_xalign(0.0)
            hint.set_wrap(True)
            self._cap_grant_selection_hint = hint
            self._i18n_labels.append((hint, "cap.grant_selection.hint"))
            outer.append(hint)

            preset_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=6)
            self._cap_preset_buttons: dict[str, object] = {}
            for preset_key in ("minimal", "standard", "full", "expert"):
                btn = gtk_module.Button(label=self._t(f"cap.preset.{preset_key}"))
                btn.add_css_class("flat")
                btn.connect("clicked", lambda _b, pk=preset_key: self._on_cap_preset_clicked(pk))
                preset_row.append(btn)
                self._cap_preset_buttons[preset_key] = btn
                self._i18n_labels.append((btn, f"cap.preset.{preset_key}"))
            outer.append(preset_row)

            action_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._cap_apply_btn = gtk_module.Button(label=self._t("cap.grant_selection.apply"))
            self._cap_apply_btn.add_css_class("suggested-action")
            self._cap_apply_btn.connect("clicked", self._on_cap_apply_permissions_clicked)
            self._i18n_labels.append((self._cap_apply_btn, "cap.grant_selection.apply"))
            action_row.append(self._cap_apply_btn)
            self._cap_selection_count_lbl = gtk_module.Label(label="")
            self._cap_selection_count_lbl.set_xalign(0.0)
            action_row.append(self._cap_selection_count_lbl)
            spacer = gtk_module.Label(label="")
            spacer.set_hexpand(True)
            action_row.append(spacer)
            outer.append(action_row)
            return outer

        def _build_cap_table_section(self, gtk_module):
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            table_scroll = gtk_module.ScrolledWindow()
            table_scroll.set_policy(gtk_module.PolicyType.AUTOMATIC, gtk_module.PolicyType.NEVER)
            table_scroll.set_hexpand(True)
            table_scroll.set_vexpand(False)
            table_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            table_content.set_size_request(960, -1)
            # Table title + policy link
            title_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._cap_table_title_lbl = gtk_module.Label(label=self._t("cap.table.title"))
            self._cap_table_title_lbl.set_xalign(0.0)
            self._cap_table_title_lbl.set_hexpand(True)
            self._cap_table_title_lbl.add_css_class("heading")
            title_row.append(self._cap_table_title_lbl)
            self._i18n_labels.append((self._cap_table_title_lbl, "cap.table.title"))
            self._cap_policy_btn = gtk_module.Button(label=self._t("cap.policy.link"))
            self._cap_policy_btn.add_css_class("flat")
            self._cap_policy_btn.connect("clicked", lambda _b: None)
            self._i18n_labels.append((self._cap_policy_btn, "cap.policy.link"))
            title_row.append(self._cap_policy_btn)
            table_content.append(title_row)
            # Column headers
            col_row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            col_row.set_margin_top(4)
            spacer = gtk_module.Label(label="")
            spacer.set_size_request(30, -1)
            col_row.append(spacer)

            def _col_hdr(key, width=None, expand=False):
                lbl = gtk_module.Label(label=self._t(key))
                lbl.set_xalign(0.0)
                lbl.add_css_class("cap-col-header")
                if width:
                    lbl.set_size_request(width, -1)
                if expand:
                    lbl.set_hexpand(True)
                return lbl

            self._col_permission_lbl = _col_hdr("cap.col.permission", expand=True)
            col_row.append(self._col_permission_lbl)
            self._i18n_labels.append((self._col_permission_lbl, "cap.col.permission"))
            self._col_grant_lbl = _col_hdr("cap.col.grant", width=56)
            col_row.append(self._col_grant_lbl)
            self._i18n_labels.append((self._col_grant_lbl, "cap.col.grant"))
            self._col_risk_lbl = _col_hdr("cap.col.risk", width=90)
            col_row.append(self._col_risk_lbl)
            self._i18n_labels.append((self._col_risk_lbl, "cap.col.risk"))
            self._col_status_lbl = _col_hdr("cap.col.status", width=108)
            col_row.append(self._col_status_lbl)
            self._i18n_labels.append((self._col_status_lbl, "cap.col.status"))
            self._col_expiry_lbl = _col_hdr("cap.col.expiry", width=120)
            col_row.append(self._col_expiry_lbl)
            self._i18n_labels.append((self._col_expiry_lbl, "cap.col.expiry"))
            self._col_actions_lbl = _col_hdr("cap.col.actions", width=188)
            col_row.append(self._col_actions_lbl)
            self._i18n_labels.append((self._col_actions_lbl, "cap.col.actions"))
            table_content.append(col_row)
            table_content.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))
            # Capability rows
            self._cap_widgets: dict[str, dict[str, object]] = {}
            for cap_key, defn in CAPABILITY_DEFINITIONS.items():
                table_content.append(self._build_cap_row(gtk_module, cap_key, defn))
                table_content.append(gtk_module.Separator(orientation=gtk_module.Orientation.HORIZONTAL))
            table_scroll.set_child(table_content)
            outer.append(table_scroll)
            return outer

        def _build_cap_row(self, gtk_module, cap_key: str, defn: dict):
            risk = str(defn["risk_level"])
            row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            row.set_margin_top(4)
            row.set_margin_bottom(4)
            # Checkbox — user selects grants (explicit opt-in)
            checkbox = gtk_module.CheckButton()
            checkbox.set_active(False)
            checkbox.set_sensitive(True)
            checkbox.set_size_request(30, -1)
            checkbox.connect("toggled", lambda cb, k=cap_key: self._on_cap_selection_toggled(k, cb))
            row.append(checkbox)
            # Name + description
            nd_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=0)
            nd_box.set_hexpand(True)
            name_lbl = gtk_module.Label(label=self._t(f"cap.{cap_key}"))
            name_lbl.set_xalign(0.0)
            name_lbl.add_css_class("cap-name")
            nd_box.append(name_lbl)
            plain_key = capability_plain_i18n_key(cap_key)
            desc_lbl = gtk_module.Label(label=self._t(plain_key))
            desc_lbl.set_xalign(0.0)
            desc_lbl.add_css_class("cap-desc")
            nd_box.append(desc_lbl)
            row.append(nd_box)
            # Risk badge
            risk_lbl = gtk_module.Label(label=self._t(f"cap.risk.{risk}"))
            risk_lbl.set_xalign(0.5)
            risk_lbl.set_size_request(90, -1)
            risk_lbl.add_css_class("risk-badge")
            risk_lbl.add_css_class(f"risk-{risk}")
            row.append(risk_lbl)
            # Status
            status_lbl = gtk_module.Label(label=self._t("cap.status.disabled"))
            status_lbl.set_xalign(0.0)
            status_lbl.set_size_request(108, -1)
            status_lbl.add_css_class("status-disabled")
            row.append(status_lbl)
            # Expiry dropdown
            expiry_model = gtk_module.StringList.new(
                [self._t(f"cap.expiry.{k}") for k in self._EXPIRY_PRESET_KEYS]
            )
            expiry_drop = gtk_module.DropDown.new(expiry_model, None)
            expiry_drop.set_selected(len(self._EXPIRY_PRESET_KEYS) - 1)  # permanent default
            expiry_drop.set_size_request(120, -1)
            row.append(expiry_drop)
            # Grant
            grant_btn = gtk_module.Button(label=self._t("cap.grant"))
            grant_btn.set_size_request(90, -1)
            grant_btn.add_css_class("suggested-action")
            grant_btn.connect("clicked", lambda _b, k=cap_key, d=expiry_drop: self._on_cap_grant_clicked(k, d))
            row.append(grant_btn)
            # Revoke
            revoke_btn = gtk_module.Button(label=self._t("cap.revoke"))
            revoke_btn.set_size_request(90, -1)
            revoke_btn.add_css_class("destructive-action")
            revoke_btn.connect("clicked", lambda _b, k=cap_key: self._on_cap_revoke_clicked(k))
            revoke_btn.set_visible(False)
            row.append(revoke_btn)
            self._cap_widgets[cap_key] = {
                "checkbox": checkbox, "name_lbl": name_lbl, "desc_lbl": desc_lbl,
                "risk_lbl": risk_lbl, "status_lbl": status_lbl,
                "expiry_drop": expiry_drop, "grant_btn": grant_btn,
                "revoke_btn": revoke_btn, "risk": risk,
            }
            return row

        def _build_active_panel(self, gtk_module):
            frame = gtk_module.Frame()
            frame.set_margin_top(4)
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            outer.set_margin_top(8)
            outer.set_margin_bottom(8)
            outer.set_margin_start(8)
            outer.set_margin_end(8)
            frame.set_child(outer)
            self._active_panel_title = gtk_module.Label(label=self._t("cap.active_permissions.title"))
            self._active_panel_title.set_xalign(0.0)
            self._active_panel_title.add_css_class("heading")
            outer.append(self._active_panel_title)
            self._active_panel_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            no_lbl = gtk_module.Label(label=f"\u2713  {self._t('cap.no_active_permissions')}")
            no_lbl.set_xalign(0.0)
            self._active_panel_content.append(no_lbl)
            sub_lbl = gtk_module.Label(label=self._t("cap.no_active_permissions.sub"))
            sub_lbl.set_xalign(0.0)
            self._active_panel_content.append(sub_lbl)
            outer.append(self._active_panel_content)
            return frame

        def _build_action_sessions_panel(self, gtk_module):
            frame = gtk_module.Frame()
            frame.set_margin_top(4)
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            outer.set_margin_top(8)
            outer.set_margin_bottom(8)
            outer.set_margin_start(8)
            outer.set_margin_end(8)
            frame.set_child(outer)

            self._action_sessions_title = gtk_module.Label(label=self._t("cap.action_sessions.title"))
            self._action_sessions_title.set_xalign(0.0)
            self._action_sessions_title.add_css_class("heading")
            outer.append(self._action_sessions_title)

            self._action_sessions_subtitle = gtk_module.Label(label=self._t("cap.action_sessions.subtitle"))
            self._action_sessions_subtitle.set_xalign(0.0)
            self._action_sessions_subtitle.set_wrap(True)
            self._action_sessions_subtitle.add_css_class("dim-label")
            outer.append(self._action_sessions_subtitle)

            active_hdr = gtk_module.Label(label=self._t("cap.action_sessions.running.title"))
            active_hdr.set_xalign(0.0)
            active_hdr.add_css_class("heading")
            active_hdr.set_margin_top(4)
            self._action_sessions_active_header = active_hdr
            outer.append(active_hdr)

            self._action_sessions_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=2)
            outer.append(self._action_sessions_content)

            auto_note = gtk_module.Label(label=self._t("security.task_sessions.note"))
            auto_note.set_xalign(0.0)
            auto_note.set_wrap(True)
            auto_note.add_css_class("dim-label")
            self._action_sessions_auto_note = auto_note
            outer.append(auto_note)

            cards = gtk_module.FlowBox()
            cards.set_selection_mode(gtk_module.SelectionMode.NONE)
            cards.set_min_children_per_line(1)
            cards.set_max_children_per_line(2)
            cards.set_column_spacing(8)
            cards.set_row_spacing(8)
            cards.set_hexpand(True)
            cards.set_homogeneous(True)

            self._action_session_preset_cards: dict[str, object] = {}
            for card_key, preset_keys in SIMPLIFIED_TASK_SESSION_CARDS:
                card_btn = gtk_module.Button()
                card_btn.add_css_class("card")
                card_btn.set_halign(gtk_module.Align.FILL)
                card_btn.set_hexpand(True)
                card_body = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
                card_body.set_margin_top(10)
                card_body.set_margin_bottom(10)
                card_body.set_margin_start(10)
                card_body.set_margin_end(10)
                title_lbl = gtk_module.Label(
                    label=self._t(f"cap.action_sessions.card.{card_key}.user_title")
                )
                title_lbl.set_xalign(0.0)
                title_lbl.add_css_class("heading")
                duration_key = (
                    f"cap.action_sessions.card.{card_key}.duration_label"
                    if card_key == "full_assistance"
                    else f"cap.action_sessions.card.{card_key}.duration"
                )
                duration_lbl = gtk_module.Label(label=self._t(duration_key))
                duration_lbl.set_xalign(0.0)
                duration_lbl.add_css_class("dim-label")
                summary_lbl = gtk_module.Label(
                    label=self._t(f"cap.action_sessions.card.{card_key}.summary")
                )
                summary_lbl.set_xalign(0.0)
                summary_lbl.set_wrap(True)
                card_body.append(title_lbl)
                card_body.append(duration_lbl)
                card_body.append(summary_lbl)
                card_btn.set_child(card_body)
                card_btn.connect(
                    "clicked",
                    lambda _b, ck=card_key, presets=preset_keys: self._on_simplified_task_session_clicked(
                        ck, presets
                    ),
                )
                cards.append(card_btn)
                self._action_session_preset_cards[card_key] = {
                    "button": card_btn,
                    "title": title_lbl,
                    "duration": duration_lbl,
                    "summary": summary_lbl,
                }
            outer.append(cards)

            self._cloud_suggestions_title = gtk_module.Label(label=self._t("cap.action_sessions.cloud.title"))
            self._cloud_suggestions_title.set_xalign(0.0)
            self._cloud_suggestions_title.add_css_class("heading")
            self._cloud_suggestions_title.set_visible(False)
            outer.append(self._cloud_suggestions_title)
            self._cloud_suggestions_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=2)
            self._cloud_suggestions_content.set_visible(False)
            outer.append(self._cloud_suggestions_content)
            return frame

        def _on_simplified_task_session_clicked(
            self,
            card_key: str,
            preset_keys: tuple[str, ...] | None,
        ) -> None:
            if card_key == "full_assistance":
                self._on_full_assistance_session_clicked()
                return
            if preset_keys is None:
                return
            self._on_action_session_card_clicked(card_key, preset_keys)

        def _on_full_assistance_session_clicked(self) -> None:
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("cap.action_sessions.card.full_assistance.choose.title"),
                body=self._t("cap.action_sessions.card.full_assistance.choose.body"),
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            for minutes in FULL_ASSISTANCE_DURATION_MINUTES:
                dialog.add_response(
                    str(minutes),
                    self._t("cap.action_sessions.card.full_assistance.duration_option").format(minutes=minutes),
                )
            dialog.set_default_response(str(FULL_ASSISTANCE_DURATION_MINUTES[1]))

            def _on_response(_d, response: str) -> None:
                try:
                    minutes = int(response)
                except ValueError:
                    _d.close()
                    return
                if minutes not in FULL_ASSISTANCE_DURATION_MINUTES:
                    _d.close()
                    return
                title = self._t("cap.action_sessions.card.full_assistance.title").format(minutes=minutes)
                body = self._t("cap.action_sessions.card.full_assistance.body").format(minutes=minutes)
                confirm = adw_module.MessageDialog(transient_for=self, heading=title, body=body)
                confirm.add_response("cancel", self._t("dialog.cancel"))
                confirm.set_close_response("cancel")
                confirm.add_response("confirm", self._t("cap.action_sessions.start.confirm"))
                confirm.set_default_response("confirm")
                confirm.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)

                def _confirm(_c, resp: str) -> None:
                    if resp == "confirm":
                        spec = build_full_assistance_session_spec(minutes)
                        self._adapter.create_action_session(
                            title=self._t("cap.action_sessions.card.full_assistance.user_title"),
                            capabilities=list(spec.get("capabilities") or []),
                            scope=dict(spec.get("scope") or {}),
                            ttl_seconds=int(spec.get("ttl_seconds") or minutes * 60),
                            confirmation_mode=str(spec.get("confirmation_mode") or "first_only"),
                            replace_same_preset=True,
                        )
                        self._refresh_capability_center()
                    _c.close()

                confirm.connect("response", _confirm)
                confirm.present()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_action_session_card_clicked(self, card_key: str, preset_keys: tuple[str, ...]) -> None:
            if len(preset_keys) == 1:
                self._on_start_action_session(preset_keys[0])
                return
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t(f"cap.action_sessions.card.{card_key}.choose.title"),
                body=self._t(f"cap.action_sessions.card.{card_key}.choose.body"),
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            for preset_key in preset_keys:
                dialog.add_response(
                    preset_key,
                    self._t(f"cap.action_sessions.preset.{preset_key}.user_title"),
                )
            dialog.set_default_response(preset_keys[0])

            def _on_response(_d, response: str) -> None:
                if response in preset_keys:
                    self._on_start_action_session(response)
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _resolve_action_session_preset_key(self, session: dict) -> str:
            return normalize_session_preset_key(session)

        def _action_session_display_title(self, session: dict) -> str:
            preset_key = self._resolve_action_session_preset_key(session)
            if preset_key:
                key = f"cap.action_sessions.preset.{preset_key}.user_title"
                translated = self._t(key)
                if translated != key:
                    return translated
            return str(session.get("title") or session.get("session_id") or "?")

        def _on_start_action_session(self, preset: str) -> None:
            from ..active.action_session_store import ActionSessionStore

            try:
                spec = ActionSessionStore.get_preset(preset)
            except ValueError:
                return
            scope = dict(spec.get("scope") or {})
            preset_key = str(scope.get("preset_key") or preset)
            dialog_key = preset_key
            title = self._t(f"cap.action_sessions.preset.{dialog_key}.title")
            body = self._t(f"cap.action_sessions.preset.{dialog_key}.body")
            dialog = adw_module.MessageDialog(transient_for=self, heading=title, body=body)
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.action_sessions.start.confirm"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_d, response):
                if response == "confirm":
                    self._adapter.create_action_session(
                        title=self._t(f"cap.action_sessions.preset.{preset_key}.user_title"),
                        capabilities=list(spec.get("capabilities") or []),
                        scope=scope,
                        ttl_seconds=int(spec.get("ttl_seconds") or 3600),
                        confirmation_mode=str(spec.get("confirmation_mode") or "first_only"),
                        replace_same_preset=True,
                    )
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _refresh_action_sessions_panel(self) -> None:
            if not hasattr(self, "_action_sessions_content"):
                return
            import datetime as _dt

            child = self._action_sessions_content.get_first_child()
            while child:
                nxt = child.get_next_sibling()
                self._action_sessions_content.remove(child)
                child = nxt

            sessions = dedupe_sessions_by_preset(self._adapter.list_active_action_sessions())
            if not sessions:
                empty = gtk_module.Label(label=self._t("cap.action_sessions.empty"))
                empty.set_xalign(0.0)
                self._action_sessions_content.append(empty)
            else:
                for session in sessions:
                    row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                    sid = str(session.get("session_id") or "")
                    display_title = self._action_session_display_title(session)
                    expires_at = str(session.get("expires_at") or "")
                    ttl_label = expires_at[:19].replace("T", " ") if expires_at else "-"
                    try:
                        exp = _dt.datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
                        if exp.tzinfo is None:
                            exp = exp.replace(tzinfo=_dt.timezone.utc)
                        delta = exp - _dt.datetime.now(_dt.timezone.utc)
                        seconds = int(delta.total_seconds())
                        if seconds <= 0:
                            ttl_label = self._t("cap.expires_in.expired")
                        elif seconds < 3600:
                            ttl_label = self._t("cap.expires_in.minutes").format(m=max(1, seconds // 60))
                        else:
                            ttl_label = self._t("cap.expires_in.hours").format(h=max(1, seconds // 3600))
                    except Exception:
                        pass
                    name_lbl = gtk_module.Label(
                        label=self._t("cap.action_sessions.active.row").format(
                            title=display_title,
                            remaining=ttl_label,
                        )
                    )
                    name_lbl.set_xalign(0.0)
                    name_lbl.set_hexpand(True)
                    name_lbl.add_css_class("active-cap-name")
                    row.append(name_lbl)
                    revoke_btn = gtk_module.Button(label=self._t("cap.action_sessions.revoke"))
                    revoke_btn.add_css_class("destructive-action")
                    revoke_btn.connect(
                        "clicked",
                        lambda _b, session_id=sid: self._on_revoke_action_session(session_id),
                    )
                    row.append(revoke_btn)
                    self._action_sessions_content.append(row)

            if hasattr(self, "_cloud_suggestions_content"):
                child = self._cloud_suggestions_content.get_first_child()
                while child:
                    nxt = child.get_next_sibling()
                    self._cloud_suggestions_content.remove(child)
                    child = nxt
                suggestions = self._adapter.read_cloud_suggested_actions()
                has_suggestions = bool(suggestions)
                if hasattr(self, "_cloud_suggestions_title"):
                    self._cloud_suggestions_title.set_visible(has_suggestions)
                if hasattr(self, "_cloud_suggestions_content"):
                    self._cloud_suggestions_content.set_visible(has_suggestions)
                if not has_suggestions:
                    return
                for item in suggestions[:5]:
                        title = str(item.get("title") or item.get("command_type") or "?")
                        risk = str(item.get("risk_level") or "medium")
                        lbl = gtk_module.Label(label=f"• {title} [{risk}]")
                        lbl.set_xalign(0.0)
                        self._cloud_suggestions_content.append(lbl)

        def _on_revoke_action_session(self, session_id: str) -> None:
            self._adapter.revoke_action_session(session_id)
            self._refresh_capability_center()

        def _build_command_log_panel(self, gtk_module):
            frame = gtk_module.Frame()
            frame.set_margin_top(4)
            outer = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            outer.set_margin_top(8)
            outer.set_margin_bottom(8)
            outer.set_margin_start(8)
            outer.set_margin_end(8)
            frame.set_child(outer)

            header = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            self._command_log_title = gtk_module.Label(label=self._t("cap.command_log.title"))
            self._command_log_title.set_xalign(0.0)
            self._command_log_title.add_css_class("heading")
            self._command_log_title.set_hexpand(True)
            header.append(self._command_log_title)

            filter_model = gtk_module.StringList.new(
                [
                    self._t("cap.command_log.filter.all"),
                    self._t("cap.command_log.filter.allow"),
                    self._t("cap.command_log.filter.deny"),
                ]
            )
            self._command_log_filter_drop = gtk_module.DropDown.new(filter_model, None)
            self._command_log_filter_drop.connect(
                "notify::selected", self._on_command_log_filter_changed
            )
            header.append(self._command_log_filter_drop)
            outer.append(header)

            self._command_log_view = gtk_module.TextView()
            self._command_log_view.set_editable(False)
            self._command_log_view.set_cursor_visible(False)
            self._command_log_view.set_monospace(True)
            scroll = gtk_module.ScrolledWindow()
            scroll.set_policy(gtk_module.PolicyType.NEVER, gtk_module.PolicyType.AUTOMATIC)
            scroll.set_min_content_height(140)
            scroll.set_child(self._command_log_view)
            outer.append(scroll)
            return frame

        def _build_cap_bottom_bar(self, gtk_module):
            hbox = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            hbox.set_margin_top(4)
            self._audit_btn = gtk_module.Button(label=self._t("cap.audit.open"))
            self._audit_btn.connect("clicked", self._on_cap_audit_clicked)
            hbox.append(self._audit_btn)
            self._revoke_sessions_btn = gtk_module.Button(label=self._t("cap.revoke_all_sessions"))
            self._revoke_sessions_btn.add_css_class("destructive-action")
            self._revoke_sessions_btn.connect("clicked", self._on_cap_revoke_all_sessions_clicked)
            hbox.append(self._revoke_sessions_btn)
            self._revoke_all_btn = gtk_module.Button(label=self._t("cap.revoke_all_grants"))
            self._revoke_all_btn.add_css_class("destructive-action")
            self._revoke_all_btn.connect("clicked", self._on_cap_revoke_all_clicked)
            hbox.append(self._revoke_all_btn)
            spacer = gtk_module.Label(label="")
            spacer.set_hexpand(True)
            hbox.append(spacer)
            self._cap_refresh_btn = gtk_module.Button(label=self._t("cap.refresh"))
            self._cap_refresh_btn.connect("clicked", lambda _b: self._refresh_capability_center())
            hbox.append(self._cap_refresh_btn)
            return hbox

        def _cap_expiry_preset_from_drop(self, expiry_drop) -> str:
            idx = int(expiry_drop.get_selected())
            if 0 <= idx < len(self._EXPIRY_PRESET_KEYS):
                return self._EXPIRY_PRESET_KEYS[idx]
            return "permanent"

        def _on_cap_selection_toggled(self, cap_key: str, checkbox) -> None:
            if not hasattr(self, "_cap_selection_pending"):
                return
            availability = self._adapter.read_ui_capability_availability().get(cap_key, {})
            if bool(availability.get("blocked_by_plan")):
                checkbox.set_active(False)
                return
            self._cap_selection_pending[cap_key] = bool(checkbox.get_active())
            self._cap_selection_dirty = True
            self._update_cap_selection_count_label()

        def _update_cap_selection_count_label(self) -> None:
            if not hasattr(self, "_cap_selection_count_lbl"):
                return
            pending = getattr(self, "_cap_selection_pending", {})
            selected = sum(1 for key in UI_CAPABILITY_KEYS if pending.get(key))
            self._cap_selection_count_lbl.set_label(
                self._t("cap.grant_selection.selected").format(selected=selected, total=len(UI_CAPABILITY_KEYS))
            )

        def _on_cap_preset_clicked(self, preset_key: str) -> None:
            preset = PERMISSION_PRESETS.get(preset_key, frozenset())
            blocked = self._adapter.read_plan_blocked_ui_keys()
            for cap_key in UI_CAPABILITY_KEYS:
                should_select = cap_key in preset and cap_key not in blocked
                self._cap_selection_pending[cap_key] = should_select
                widgets = self._cap_widgets.get(cap_key, {})
                cb = widgets.get("checkbox")
                if cb is not None:
                    cb.set_active(should_select)
            self._update_cap_selection_count_label()
            title = self._t(f"cap.preset.{preset_key}")
            body = self._t("cap.preset.confirm.body").format(preset=title)
            dialog = adw_module.MessageDialog(transient_for=self, heading=title, body=body)
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.grant_selection.apply"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_d, response):
                if response == "confirm":
                    self._on_cap_apply_permissions_clicked(None)
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_cap_apply_permissions_clicked(self, _btn) -> None:
            pending = getattr(self, "_cap_selection_pending", {})
            selected = [key for key in UI_CAPABILITY_KEYS if pending.get(key)]
            if not selected:
                dialog = adw_module.MessageDialog(
                    transient_for=self,
                    heading=self._t("cap.grant_selection.empty.title"),
                    body=self._t("cap.grant_selection.empty.body"),
                )
                dialog.add_response("ok", self._t("dialog.ok"))
                dialog.set_close_response("ok")
                dialog.connect("response", lambda d, _r: d.close())
                dialog.present()
                return
            high_risk = [k for k in selected if k in HIGH_RISK_CAPABILITIES or k in CRITICAL_CAPABILITIES]
            body = self._t("cap.grant_selection.confirm.body").format(count=len(selected))
            if high_risk:
                names = ", ".join(self._t(f"cap.{k}") for k in high_risk)
                body = f"{body}\n\n{self._t('cap.grant_selection.confirm.high_risk').format(caps=names)}"
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("cap.grant_selection.confirm.title"),
                body=body,
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.grant_selection.apply"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_d, response):
                if response == "confirm":
                    from iuve_connect_agents.shared.permission_profiles import infer_profile_from_ui_keys

                    lifetime = self._selected_profile_lifetime_key()
                    selected_set = set(selected)
                    inferred = infer_profile_from_ui_keys(selected_set)
                    preset_keys = PERMISSION_PRESETS.get(inferred, frozenset())
                    if preset_keys == selected_set and inferred in PROFILE_PRESET_KEYS:
                        self._adapter.apply_permission_profile(inferred, lifetime=lifetime)
                    else:
                        from iuve_connect_agents.shared.permission_profiles import lifetime_to_expiry_preset

                        self._adapter.apply_capability_selection(
                            selected,
                            expiry_preset=lifetime_to_expiry_preset(lifetime),
                        )
                        self._adapter.mark_grant_setup_completed()
                    self._cap_selection_dirty = False
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _maybe_show_grant_setup_wizard(self) -> None:
            if getattr(self, "_grant_wizard_shown", False):
                return
            if not self._adapter.needs_initial_grant_setup():
                return
            self._grant_wizard_shown = True
            self._stack.set_visible_child_name("security")
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("cap.grant_wizard.title"),
                body=self._t("cap.grant_wizard.body"),
            )
            dialog.add_response("later", self._t("cap.grant_wizard.later"))
            dialog.set_close_response("later")
            dialog.add_response("open", self._t("cap.grant_wizard.open"))
            dialog.set_default_response("open")
            dialog.set_response_appearance("open", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_d, response):
                if response == "open":
                    self._stack.set_visible_child_name("security")
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_cap_grant_clicked(self, cap_key: str, expiry_drop):
            expiry_preset = self._cap_expiry_preset_from_drop(expiry_drop)
            expiry_display = self._t(f"cap.expiry.{expiry_preset}")
            cap_name = self._t(f"cap.{cap_key}")
            risk_level = CAPABILITY_DEFINITIONS.get(cap_key, {}).get("risk_level", "medium")
            risk_display = self._t(f"cap.risk.{risk_level}")
            if cap_key in CRITICAL_CAPABILITIES:
                title = self._t("dialog.cap.root.title")
                body = self._t("dialog.cap.root.body")
                appearance = adw_module.ResponseAppearance.DESTRUCTIVE
            else:
                title = self._t("dialog.cap.grant.title")
                body = self._t("dialog.cap.grant.body").format(
                    capability=cap_name, risk=risk_display, expiry=expiry_display
                )
                appearance = adw_module.ResponseAppearance.SUGGESTED
            dialog = adw_module.MessageDialog(transient_for=self, heading=title, body=body)
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            if cap_key in CRITICAL_CAPABILITIES:
                dialog.add_response("confirm", self._t("dialog.cap.root.confirm"))
            else:
                dialog.add_response("confirm", self._t("cap.grant"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", appearance)

            def _on_response(_d, response):
                if response == "confirm":
                    self._adapter.grant_capability(cap_key, expiry_preset=expiry_preset)
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_cap_revoke_clicked(self, cap_key: str):
            cap_name = self._t(f"cap.{cap_key}")
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("dialog.cap.revoke.title"),
                body=self._t("dialog.cap.revoke.body").format(capability=cap_name),
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.revoke"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.DESTRUCTIVE)

            def _on_response(_d, response):
                if response == "confirm":
                    self._adapter.revoke_capability(cap_key)
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_cap_audit_clicked(self, _btn):
            text = self._adapter.format_permissions_audit_text(lang=self._lang, max_lines=50)
            text_view = gtk_module.TextView()
            text_view.set_editable(False)
            text_view.set_cursor_visible(False)
            text_view.get_buffer().set_text(text)
            text_view.set_monospace(True)
            scroll_w = gtk_module.ScrolledWindow()
            scroll_w.set_child(text_view)
            scroll_w.set_size_request(560, 320)
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("cap.audit.dialog.title"),
                body="",
            )
            dialog.set_extra_child(scroll_w)
            dialog.add_response("close", self._t("dialog.ok"))
            dialog.set_close_response("close")
            dialog.connect("response", lambda d, _r: d.close())
            dialog.present()

        def _on_cap_revoke_all_sessions_clicked(self, _btn):
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("dialog.cap.revoke_all_sessions.title"),
                body=self._t("dialog.cap.revoke_all_sessions.body"),
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.revoke_all_sessions"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.DESTRUCTIVE)

            def _on_response(_d, response):
                if response == "confirm":
                    self._adapter.revoke_all_action_sessions()
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_cap_revoke_all_clicked(self, _btn):
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("dialog.cap.revoke_all_grants.title"),
                body=self._t("dialog.cap.revoke_all_grants.body"),
            )
            dialog.add_response("cancel", self._t("dialog.cancel"))
            dialog.set_close_response("cancel")
            dialog.add_response("confirm", self._t("cap.revoke_all_grants"))
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.DESTRUCTIVE)

            def _on_response(_d, response):
                if response == "confirm":
                    self._adapter.revoke_all_capabilities()
                    self._refresh_capability_center()
                _d.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_command_log_filter_changed(self, dropdown, _pspec):
            idx = int(dropdown.get_selected())
            mapping = ("all", "allow", "deny")
            if 0 <= idx < len(mapping):
                self._command_log_filter = mapping[idx]
                self._refresh_command_log_panel()

        def _handle_command_confirmation_request(self, envelope, decision) -> str:
            """Blocking confirmation hook invoked from pipeline background thread."""
            handler = wrap_confirmation_callback(
                lambda env, dec: self._present_command_confirmation_dialog(env, dec)
            )
            return handler(envelope, decision)

        def _command_confirmation_heading_body(self, envelope, decision) -> tuple[str, str]:
            payload = envelope.payload
            command_type = envelope.command_type
            risk = envelope.risk_level
            stop_zone = str(decision.stop_zone or payload.get("stop_zone") or "").strip()
            is_verify_step = int(payload.get("double_confirm_step") or 0) == 2
            if requires_double_confirm(decision):
                if is_verify_step:
                    return (
                        self._t("dialog.command.double_confirm.verify.title"),
                        self._t("dialog.command.double_confirm.verify.body").format(
                            command=command_type,
                            stop_zone=stop_zone or "high_risk",
                        ),
                    )
                return (
                    self._t("dialog.command.double_confirm.title"),
                    self._t("dialog.command.double_confirm.body").format(
                        stop_zone=stop_zone or "high_risk",
                        command=command_type,
                        risk=self._t(f"cap.risk.{risk}") if risk in {"low", "medium", "high", "critical"} else risk,
                    ),
                )
            source = str(
                payload.get("source_agent")
                or payload.get("model_source")
                or payload.get("requested_by")
                or "backend"
            )
            reasoning = str(
                payload.get("reasoning_summary")
                or payload.get("intended_action")
                or command_type
            )
            rollback = str(payload.get("rollback_hint") or self._t("cap.no_expiry"))
            return (
                self._t("dialog.command.confirm.title"),
                self._t("dialog.command.confirm.body").format(
                    command=command_type,
                    risk=self._t(f"cap.risk.{risk}") if risk in {"low", "medium", "high", "critical"} else risk,
                    source=source,
                    reasoning=reasoning,
                    rollback=rollback,
                ),
            )

        def _poll_pending_command_confirmation(self) -> bool:
            self._refresh_pending_confirmation_banner()
            self._refresh_agent_status_overlay()
            pending = self._adapter.read_pending_command_confirmation()
            if (
                pending_requires_user_action(pending)
                and self._pending_needs_owner_decision(pending)
                and not self._confirmation_dialog_active
            ):
                command_id = str(pending.get("command_id") or pending.get("request_id") or "").strip()
                command_type = str(pending.get("command_type") or "?")
                if command_id:
                    self._confirmation_dialog_active = True
                    self._bring_window_to_front()

                    def _on_modal_response(response: str) -> None:
                        self._confirmation_dialog_active = False
                        if response == "approve":
                            self._submit_pending_confirmation_approve(command_id)
                        elif response == "decline":
                            self._adapter.submit_command_confirmation_response(command_id, approve=False)
                            self._refresh_pending_confirmation_banner()
                            self._refresh_command_log_panel()

                    self._present_action_required_modal(
                        title=self._t("modal.agent_waiting.title"),
                        body=self._t("cap.pending_confirmation.body").format(
                            command=command_type,
                            command_id=command_id[:8],
                        ),
                        buttons=[
                            ("decline", self._t("dialog.command.confirm.decline")),
                            ("approve", self._t("dialog.command.confirm.execute")),
                        ],
                        default_button="approve",
                        on_response=_on_modal_response,
                    )
            return True

        def _present_pending_confirmation_dialog(
            self,
            pending: dict,
            *,
            force: bool = False,
        ) -> bool:
            if not self._pending_needs_owner_decision(pending):
                return True
            if self._confirmation_dialog_active and not force:
                return True
            command_id = str(pending.get("command_id") or pending.get("request_id") or "").strip()
            if not command_id:
                return False
            self._bring_window_to_front()
            envelope = self._adapter.pending_confirmation_envelope(pending)
            decision = self._adapter.pending_confirmation_decision(pending)

            def _on_done(
                result: str,
                *,
                always_allow_command_type: str = "",
            ) -> None:
                approve = result == "approve"
                self._adapter.submit_command_confirmation_response(
                    command_id,
                    approve=approve,
                    always_allow_command_type=always_allow_command_type,
                )
                self._reset_confirmation_dialog_state()
                self._refresh_command_log_panel()
                self._refresh_pending_confirmation_banner()

            if force:
                self._dismiss_active_confirmation_dialog()

            self._confirmation_dialog_active = True
            dialog = self._show_command_confirmation_dialog_async(envelope, decision, _on_done)
            if dialog is None:
                self._reset_confirmation_dialog_state()
                return False
            self._active_confirmation_dialog = dialog
            return True

        def _show_command_confirmation_dialog_async(self, envelope, decision, *, on_done):
            payload = envelope.payload
            command_type = envelope.command_type
            is_verify_step = int(payload.get("double_confirm_step") or 0) == 2
            heading, body = self._command_confirmation_heading_body(envelope, decision)
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=heading,
                body=body,
            )
            try:
                dialog.set_modal(True)
            except Exception:
                pass
            dialog.add_response("decline", self._t("dialog.command.confirm.decline"))
            if requires_double_confirm(decision) and not is_verify_step:
                dialog.add_response("details", self._t("dialog.command.confirm.details"))
                dialog.add_response("execute", self._t("dialog.command.double_confirm.continue"))
                dialog.set_default_response("execute")
                dialog.set_response_appearance("execute", adw_module.ResponseAppearance.SUGGESTED)
            elif requires_double_confirm(decision) and is_verify_step:
                dialog.add_response("execute", self._t("dialog.command.double_confirm.verify.execute"))
                dialog.set_default_response("execute")
                dialog.set_response_appearance("execute", adw_module.ResponseAppearance.SUGGESTED)
            else:
                dialog.add_response("details", self._t("dialog.command.confirm.details"))
                dialog.add_response("always_allow", self._t("dialog.command.confirm.always_allow"))
                dialog.add_response("execute", self._t("dialog.command.confirm.execute"))
                dialog.set_default_response("execute")
                dialog.set_response_appearance("execute", adw_module.ResponseAppearance.SUGGESTED)
            dialog.set_close_response("decline")
            dialog.set_response_appearance("decline", adw_module.ResponseAppearance.DESTRUCTIVE)

            def _on_response(_d, response):
                if response == "details":
                    self._show_command_payload_details(envelope)
                    return
                if response == "always_allow":
                    self._adapter.add_always_allow_command_type(command_type)
                    try:
                        on_done("approve", always_allow_command_type=command_type)
                    except TypeError:
                        on_done("approve")
                elif response == "execute":
                    on_done("approve")
                else:
                    on_done("decline")
                _d.close()

            def _on_destroy(_d):
                if self._active_confirmation_dialog is _d:
                    self._reset_confirmation_dialog_state()
                    self._refresh_pending_confirmation_banner()

            dialog.connect("response", _on_response)
            dialog.connect("destroy", _on_destroy)

            def _present_dialog() -> bool:
                try:
                    self._bring_window_to_front()
                    dialog.present()
                except Exception:
                    return False
                return False

            self._glib.idle_add(_present_dialog)
            return dialog

        def _present_command_confirmation_dialog(self, envelope, decision) -> str:
            payload = envelope.payload
            command_type = envelope.command_type
            is_verify_step = int(payload.get("double_confirm_step") or 0) == 2
            heading, body = self._command_confirmation_heading_body(envelope, decision)
            if requires_double_confirm(decision) and not is_verify_step:
                buttons = [
                    ("decline", self._t("dialog.command.confirm.decline")),
                    ("execute", self._t("dialog.command.double_confirm.continue")),
                ]
                default_button = "execute"
            elif requires_double_confirm(decision) and is_verify_step:
                buttons = [
                    ("decline", self._t("dialog.command.confirm.decline")),
                    ("execute", self._t("dialog.command.double_confirm.verify.execute")),
                ]
                default_button = "execute"
            else:
                buttons = [
                    ("decline", self._t("dialog.command.confirm.decline")),
                    ("execute", self._t("dialog.command.confirm.execute")),
                ]
                default_button = "execute"

            response = show_modal_action_required_blocking(
                self._gtk,
                self._adw,
                self._glib,
                parent=self,
                title=heading,
                body=body,
                buttons=buttons,
                default_button=default_button,
                timeout_seconds=300.0,
            )
            if response == "execute":
                return "approve"
            return "decline"

        def _show_command_payload_details(self, envelope) -> None:
            text_view = gtk_module.TextView()
            text_view.set_editable(False)
            text_view.set_cursor_visible(False)
            text_view.get_buffer().set_text(
                json.dumps(envelope.payload, ensure_ascii=True, indent=2, sort_keys=True)
            )
            text_view.set_monospace(True)
            scroll_w = gtk_module.ScrolledWindow()
            scroll_w.set_child(text_view)
            scroll_w.set_size_request(560, 320)
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("dialog.command.confirm.details.title"),
                body="",
            )
            dialog.set_extra_child(scroll_w)
            dialog.add_response("close", self._t("dialog.ok"))
            dialog.set_close_response("close")
            dialog.connect("response", lambda d, _r: d.close())
            dialog.present()

        def _refresh_command_log_panel(self):
            if not hasattr(self, "_command_log_view"):
                return
            try:
                records = self._adapter.read_command_audit_recent(50)
            except Exception:
                records = []
            lines: list[str] = []
            for record in records:
                event = str(record.get("event") or "")
                decision = str(record.get("policy_decision") or "")
                if self._command_log_filter == "allow":
                    if event not in {"command_attempt", "command_completed"}:
                        continue
                elif self._command_log_filter == "deny":
                    if event not in {"command_denied", "command_failed"}:
                        continue
                at = str(record.get("timestamp") or "?")[:19].replace("T", " ")
                command_type = str(record.get("command_type") or "?")
                capability = str(record.get("capability") or "")
                summary = str(record.get("result_summary") or decision or "")
                lines.append(
                    f"[{at}]  {command_type}  {event}  {capability}  {summary}".rstrip()
                )
            text = "\n".join(lines) if lines else self._t("cap.command_log.empty")
            self._command_log_view.get_buffer().set_text(text)

        def _refresh_capability_center(self):
            if not hasattr(self, "_cap_widgets"):
                return
            import datetime as _dt
            snapshot = self._adapter.read_capability_snapshot()
            availability = self._adapter.read_ui_capability_availability()
            enabled_count = 0
            active_rows: list[dict] = []
            nearest_expiry_dt = None
            nearest_expiry_type = "none"
            for cap_key, widgets in self._cap_widgets.items():
                state = snapshot.get(cap_key, {})
                avail = availability.get(cap_key, {})
                blocked_by_plan = bool(avail.get("blocked_by_plan"))
                session_active = bool(avail.get("session_active"))
                enabled = bool(avail.get("granted", False))
                if hasattr(self, "_cap_selection_pending") and not getattr(self, "_cap_selection_dirty", False):
                    self._cap_selection_pending[cap_key] = enabled
                expires_at = state.get("expires_at")
                expiry_type = str(state.get("expiry_type", "permanent"))
                expired = False
                if enabled and expires_at:
                    try:
                        exp = _dt.datetime.fromisoformat(str(expires_at))
                        if exp.tzinfo is None:
                            exp = exp.replace(tzinfo=_dt.timezone.utc)
                        expired = _dt.datetime.now(_dt.timezone.utc) > exp
                    except Exception:
                        pass
                if enabled and expiry_type == "one_shot":
                    remaining = state.get("one_shot_remaining")
                    suffix = f" ({self._t('cap.expiry.one_shot')})"
                    if remaining is not None:
                        suffix = f" ({self._t('cap.expiry.one_shot')}: {remaining})"
                    status_text = f"{self._t('cap.status.enabled')}{suffix}"
                    css_class = "status-enabled"
                elif enabled and expiry_type == "reboot":
                    status_text = f"{self._t('cap.status.enabled')} ({self._t('cap.expiry.reboot')})"
                    css_class = "status-enabled"
                elif enabled and expiry_type == "app_exit":
                    status_text = f"{self._t('cap.status.enabled')} ({self._t('profile.lifetime.app_exit')})"
                    css_class = "status-enabled"
                elif expired:
                    status_text = self._t("cap.status.expired")
                    css_class = "status-expired"
                    enabled = False
                elif blocked_by_plan:
                    status_text = self._t("cap.status.blocked_plan")
                    css_class = "status-expired"
                    enabled = False
                elif session_active:
                    status_text = self._t("cap.status.session")
                    css_class = "status-enabled"
                elif enabled:
                    expiry_info = ""
                    if expires_at:
                        try:
                            exp_dt = _dt.datetime.fromisoformat(str(expires_at))
                            expiry_info = f" \u2191{exp_dt.strftime('%H:%M')}"
                        except Exception:
                            pass
                    status_text = f"{self._t('cap.status.enabled')}{expiry_info}"
                    css_class = "status-enabled"
                else:
                    status_text = self._t("cap.status.not_granted")
                    css_class = "status-disabled"
                status_lbl = widgets["status_lbl"]
                for cls in ("status-enabled", "status-disabled", "status-expired"):
                    status_lbl.remove_css_class(cls)
                status_lbl.add_css_class(css_class)
                status_lbl.set_label(status_text)
                cb = widgets["checkbox"]
                cb.set_active(bool(self._cap_selection_pending.get(cap_key, enabled)))
                cb.set_sensitive(not blocked_by_plan and not enabled)
                widgets["grant_btn"].set_visible(not enabled and not blocked_by_plan)
                widgets["expiry_drop"].set_visible(not enabled and not blocked_by_plan)
                widgets["revoke_btn"].set_visible(bool(avail.get("locally_enabled", False)))
                if enabled:
                    enabled_count += 1
                    active_rows.append({"key": cap_key, "state": state})
                    if expiry_type == "timestamp" and expires_at:
                        try:
                            exp_dt = _dt.datetime.fromisoformat(str(expires_at))
                            if exp_dt.tzinfo is None:
                                exp_dt = exp_dt.replace(tzinfo=_dt.timezone.utc)
                            if nearest_expiry_dt is None or exp_dt < nearest_expiry_dt:
                                nearest_expiry_dt = exp_dt
                                nearest_expiry_type = "timestamp"
                        except Exception:
                            pass
                    elif expiry_type == "reboot" and nearest_expiry_type == "none":
                        nearest_expiry_type = "reboot"
                    elif expiry_type == "permanent" and nearest_expiry_type == "none":
                        nearest_expiry_type = "permanent"
            granted_ui, total_ui = self._adapter.read_active_ui_grant_counts()
            if hasattr(self, "_capabilities_count_label"):
                self._capabilities_count_label.set_label(
                    self._t("cap.capabilities_count").format(granted=granted_ui, total=total_ui)
                )
            if hasattr(self, "_expires_in_label"):
                expires_value = self._t("cap.expires_in.none")
                if nearest_expiry_type == "timestamp" and nearest_expiry_dt is not None:
                    delta = nearest_expiry_dt - _dt.datetime.now(_dt.timezone.utc)
                    seconds = int(delta.total_seconds())
                    if seconds <= 0:
                        expires_value = self._t("cap.expires_in.expired")
                    elif seconds < 3600:
                        expires_value = self._t("cap.expires_in.minutes").format(m=max(1, seconds // 60))
                    else:
                        expires_value = self._t("cap.expires_in.hours").format(h=max(1, seconds // 3600))
                elif nearest_expiry_type == "reboot":
                    expires_value = self._t("cap.expiry.reboot")
                elif nearest_expiry_type == "permanent":
                    expires_value = self._t("cap.expiry.permanent")
                self._expires_in_label.set_label(expires_value)
            self._refresh_security_status_line()
            self._refresh_cap_status_bar()
            self._refresh_profile_summary_panel()
            self._refresh_profile_lifetime_dropdown()
            self._rebuild_active_panel(active_rows)
            self._refresh_action_sessions_panel()
            self._refresh_command_log_panel()
            self._poll_pending_command_confirmation()
            self._update_cap_selection_count_label()
            self._maybe_show_grant_setup_wizard()

        def _refresh_trust_badge(self):
            self._refresh_security_status_line()

        def _refresh_cap_status_bar(self):
            if not hasattr(self, "_cap_status_widgets"):
                return
            try:
                snap = self._adapter.read_dashboard_snapshot()
                sec = self._adapter.read_security_snapshot()
                w = self._cap_status_widgets
                if "model" in w:
                    w["model"].set_label(str(sec.get("admission_model") or "\u2014"))
                if "trust" in w:
                    w["trust"].set_label(str(snap.get("trust_state") or "\u2014"))
                if "runtime" in w:
                    w["runtime"].set_label(str(snap.get("runtime_state") or "\u2014"))
                if "consent" in w:
                    w["consent"].set_label(self._t("cap.status_bar.consent_required"))
                if "heartbeat" in w:
                    hb = str(snap.get("last_heartbeat_at") or "")
                    w["heartbeat"].set_label(hb or self._t("cap.status_bar.never"))
            except Exception:
                pass

        def _rebuild_active_panel(self, active_rows: list[dict]):
            if not hasattr(self, "_active_panel_content"):
                return
            child = self._active_panel_content.get_first_child()
            while child:
                nxt = child.get_next_sibling()
                self._active_panel_content.remove(child)
                child = nxt
            if not active_rows:
                no_lbl = gtk_module.Label(label=f"\u2713  {self._t('cap.no_active_permissions')}")
                no_lbl.set_xalign(0.0)
                self._active_panel_content.append(no_lbl)
                sub_lbl = gtk_module.Label(label=self._t("cap.no_active_permissions.sub"))
                sub_lbl.set_xalign(0.0)
                self._active_panel_content.append(sub_lbl)
                return
            for row in active_rows:
                cap_key = str(row["key"])
                state = row["state"]
                row_box = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                name_lbl = gtk_module.Label(label=self._t(f"cap.{cap_key}"))
                name_lbl.set_xalign(0.0)
                name_lbl.add_css_class("active-cap-name")
                name_lbl.set_width_chars(22)
                row_box.append(name_lbl)
                granted_at = str(state.get("granted_at") or "")[:19].replace("T", " ")
                expires_at = str(state.get("expires_at") or "")[:19].replace("T", " ")
                granted_by = str(state.get("granted_by") or "local_user")
                risk_level = str(state.get("risk_level") or CAPABILITY_DEFINITIONS.get(cap_key, {}).get("risk_level", "medium"))
                risk_display = self._t(f"cap.risk.{risk_level}")
                meta = f"{self._t('cap.granted_at_label')}: {granted_at}"
                if expires_at:
                    meta += f"  |  {self._t('cap.expires_at_label')}: {expires_at}"
                else:
                    meta += f"  |  {self._t('cap.expires_at_label')}: {self._t('cap.expiry.permanent')}"
                meta += f"  |  {self._t('cap.granted_by_label')}: {granted_by}"
                meta += f"  |  {self._t('cap.risk_level_label')}: {risk_display}"
                meta_lbl = gtk_module.Label(label=meta)
                meta_lbl.set_xalign(0.0)
                row_box.append(meta_lbl)
                self._active_panel_content.append(row_box)



        def _build_updates_page(self, gtk_module):
            box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=12)

            hero = gtk_module.Frame()
            hero_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=6)
            hero_box.set_margin_top(10)
            hero_box.set_margin_bottom(10)
            hero_box.set_margin_start(10)
            hero_box.set_margin_end(10)
            hero.set_child(hero_box)
            self._updates_header_title = gtk_module.Label(label=self._t("updates.page.title"))
            self._updates_header_title.set_xalign(0.0)
            self._updates_header_title.add_css_class("page-title")
            hero_box.append(self._updates_header_title)
            self._updates_header_subtitle = gtk_module.Label(label=self._t("updates.page.subtitle"))
            self._updates_header_subtitle.set_xalign(0.0)
            self._updates_header_subtitle.set_wrap(True)
            hero_box.append(self._updates_header_subtitle)
            box.append(hero)

            status_card = gtk_module.Frame()
            status_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            status_content.set_margin_top(10)
            status_content.set_margin_bottom(10)
            status_content.set_margin_start(10)
            status_content.set_margin_end(10)
            status_card.set_child(status_content)
            self._updates_status_title = gtk_module.Label(label=self._t("updates.status.title"))
            self._updates_status_title.set_xalign(0.0)
            self._updates_status_title.add_css_class("heading")
            status_content.append(self._updates_status_title)

            self._updates_summary_values: dict[str, object] = {}
            for title_key, value_key in [
                ("updates.status.version", "current_version"),
                ("updates.status.pairing", "pairing_status"),
                ("updates.status.channel", "channel"),
            ]:
                row = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
                title = gtk_module.Label(label=self._t(title_key))
                title.set_xalign(0.0)
                title.set_hexpand(True)
                value = gtk_module.Label(label="-")
                value.set_xalign(1.0)
                row.append(title)
                row.append(value)
                status_content.append(row)
                self._updates_summary_values[value_key] = value

            self._updates_labels = {}
            rows = [
                ("label.update_channel", "update_channel"),
                ("label.app_version", "app_version"),
                ("label.remote_update_allowed", "remote_update_allowed"),
                ("label.policy", "policy"),
            ]
            status_content.append(self._build_details_grid(gtk_module, rows, self._updates_labels))
            box.append(status_card)

            actions_card = gtk_module.Frame()
            actions = gtk_module.Box(orientation=gtk_module.Orientation.HORIZONTAL, spacing=8)
            actions.set_margin_top(10)
            actions.set_margin_bottom(10)
            actions.set_margin_start(10)
            actions.set_margin_end(10)
            self._updates_actions_title = gtk_module.Label(label=self._t("updates.actions.title"))
            self._updates_actions_title.set_xalign(0.0)
            self._updates_actions_title.add_css_class("heading")
            actions.set_vexpand(False)
            actions.set_hexpand(True)
            actions_card_box = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=8)
            actions_card_box.set_margin_top(10)
            actions_card_box.set_margin_bottom(10)
            actions_card_box.set_margin_start(10)
            actions_card_box.set_margin_end(10)
            actions_card_box.append(self._updates_actions_title)
            self._check_update_btn = gtk_module.Button(label=self._t("action.check_update"))
            self._check_update_btn.connect("clicked", self._on_check_update_clicked)
            actions.append(self._check_update_btn)

            self._updates_pairing_hint = gtk_module.Label(label=self._t("updates.pairing.required.hint"))
            self._updates_pairing_hint.set_xalign(0.0)
            self._updates_pairing_hint.set_wrap(True)
            self._updates_pairing_hint.set_hexpand(True)
            actions.append(self._updates_pairing_hint)
            actions_card_box.append(actions)
            actions_card.set_child(actions_card_box)
            box.append(actions_card)

            note_card = gtk_module.Frame()
            note_content = gtk_module.Box(orientation=gtk_module.Orientation.VERTICAL, spacing=4)
            note_content.set_margin_top(10)
            note_content.set_margin_bottom(10)
            note_content.set_margin_start(10)
            note_content.set_margin_end(10)
            note_card.set_child(note_content)
            self._updates_note_title = gtk_module.Label(label=self._t("updates.troubleshooting.title"))
            self._updates_note_title.set_xalign(0.0)
            self._updates_note_title.add_css_class("heading")
            note_content.append(self._updates_note_title)
            self._updates_note_body = gtk_module.Label(
                label=self._t("updates.note.discover_troubleshooting").format(version="latest")
            )
            self._updates_note_body.set_xalign(0.0)
            self._updates_note_body.set_wrap(True)
            note_content.append(self._updates_note_body)
            box.append(note_card)
            return box

        def _on_check_update_clicked(self, _btn):
            snap = self._adapter.read_dashboard_snapshot()
            trust_state = str(snap.get("trust_state") or "").strip().lower()
            paired = bool(snap.get("paired", False))
            if (not paired) or trust_state == "unpaired":
                self._show_update_pairing_required_dialog()
                return

            self._check_update_btn.set_sensitive(False)

            def _worker() -> None:
                check = self._adapter.check_update(dry_run=True)
                glib_module.idle_add(self._handle_update_check_result, check)

            threading.Thread(target=_worker, daemon=True).start()

        def _handle_update_check_result(self, check: dict[str, object]) -> None:
            self._check_update_btn.set_sensitive(True)
            status = str(check.get("status") or "unknown")
            reason = str(check.get("reason") or "")

            if status == "blocked" and reason == "device_not_paired":
                self._show_update_pairing_required_dialog()
                return

            if bool(check.get("would_upgrade", False)):
                target_version = str(check.get("target_version") or check.get("available_version") or "unknown")
                dialog = self._build_confirm_dialog(
                    title=self._t("dialog.update.available.title"),
                    body=self._t("dialog.update.available.body").format(version=target_version),
                    confirm_label=self._t("dialog.update.install"),
                    cancel_label=self._t("dialog.cancel"),
                )

                def _on_response(_dialog, response):
                    if response == "confirm":
                        _dialog.close()
                        self._check_update_btn.set_sensitive(False)

                        def _install_worker() -> None:
                            result = self._adapter.check_update(dry_run=False)
                            glib_module.idle_add(self._handle_update_install_result, result)

                        threading.Thread(target=_install_worker, daemon=True).start()
                        return
                    _dialog.close()

                dialog.connect("response", _on_response)
                dialog.present()
                return

            dialog = self._build_confirm_dialog(
                title=self._t("dialog.update.check.title"),
                body=self._friendly_update_message(check),
                confirm_label=self._t("dialog.ok"),
                cancel_label=None,
            )
            dialog.connect("response", lambda d, _r: d.close())
            dialog.present()

        def _handle_update_install_result(self, result: dict[str, object]) -> None:
            self._check_update_btn.set_sensitive(True)
            result_dialog = self._build_confirm_dialog(
                title=self._t("dialog.update.result.title"),
                body=self._friendly_update_message(result),
                confirm_label=self._t("dialog.ok"),
                cancel_label=None,
            )
            result_dialog.connect("response", lambda d, _r: d.close())
            result_dialog.present()
            self._refresh_dashboard(force=True)

        def _friendly_update_message(self, payload: dict[str, object]) -> str:
            return resolve_update_check_message(payload, self._t)

        def _show_update_pairing_required_dialog(self):
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=self._t("updates.pairing.required.title"),
                body=self._t("updates.pairing.required.message"),
            )
            dialog.add_response("pair", self._t("updates.pairing.required.pair"))
            dialog.add_response("help", self._t("updates.pairing.required.help"))
            dialog.add_response("ok", self._t("dialog.ok"))
            dialog.set_default_response("pair")
            dialog.set_close_response("ok")
            dialog.set_response_appearance("pair", adw_module.ResponseAppearance.SUGGESTED)

            def _on_response(_dialog, response):
                if response == "pair":
                    self._show_pairing_dialog()
                elif response == "help":
                    webbrowser.open("https://www.iuve.eu/connect")
                _dialog.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_devices_open_security_clicked(self, _btn):
            self._stack.set_visible_child_name("security")

        def _on_devices_unpair_clicked(self, _btn):
            devices_snapshot = self._adapter.read_devices_snapshot()
            hint = str(devices_snapshot.get("auth_recovery_hint") or "").strip()
            if not hint:
                hint = str(devices_snapshot.get("storage_path") or "").strip()
            dialog = self._build_confirm_dialog(
                title=self._t("dialog.devices.unpair.title"),
                body=self._t("dialog.devices.unpair.body").format(hint=hint),
                confirm_label=self._t("devices.unpair.button"),
            )
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.DESTRUCTIVE)

            def _on_response(_dialog, response):
                if response == "confirm":
                    self._adapter.unpair_device(reason="user_revoke_devices_tab")
                    self._refresh_dashboard(force=True)
                _dialog.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_devices_repair_clicked(self, _btn):
            dialog = self._build_confirm_dialog(
                title=self._t("dialog.devices.repair.title"),
                body=self._t("dialog.devices.repair.body"),
                confirm_label=self._t("devices.repair.button"),
            )

            def _on_response(_dialog, response):
                if response == "confirm":
                    self._adapter.unpair_device(reason="user_repair_devices_tab")
                    self._refresh_dashboard(force=True)
                    self._show_pairing_dialog()
                _dialog.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _on_restart_clicked(self, _btn):
            dialog = self._build_confirm_dialog(
                title=self._t("dialog.restart.title"),
                body=self._t("dialog.restart.body"),
            )

            def _on_response(_dialog, response):
                if response == "confirm":
                    self._adapter.restart_agent()
                    self._refresh_dashboard(force=True)
                _dialog.close()

            dialog.connect("response", _on_response)
            dialog.present()

        def _show_pairing_dialog(self):
            show_pairing_dialog(
                parent=self,
                adw_module=adw_module,
                gtk_module=gtk_module,
                glib_module=glib_module,
                status_adapter=self._adapter,
                translate=self._t,
                on_complete=self._refresh_dashboard_forced,
            )

        def _update_avatar_billing_upgrade_cta(self, visible: bool) -> None:
            if hasattr(self, "_avatar_upgrade_tariff_btn"):
                self._avatar_upgrade_tariff_btn.set_visible(bool(visible))

        def _on_saas_upgrade_clicked(self, _btn):
            self._adapter.open_billing_upgrade()

        def _on_saas_refresh_clicked(self, _btn):
            def _worker() -> None:
                self._adapter.refresh_all_console_context()
                self._glib.idle_add(self._refresh_dashboard_forced)
            threading.Thread(target=_worker, daemon=True).start()

        def _on_export_clicked(self, _btn):
            path = self._adapter.export_diagnostics_placeholder()
            dialog = self._build_confirm_dialog(
                title=self._t("dialog.export.title"),
                body=self._t("dialog.export.body").format(path=path),
                confirm_label=self._t("dialog.ok"),
                cancel_label=None,
            )
            dialog.connect("response", lambda d, _r: d.close())
            dialog.present()

        def _build_confirm_dialog(self, *, title: str, body: str, confirm_label: str | None = None, cancel_label: str | None = None):
            if confirm_label is None:
                confirm_label = self._t("dialog.confirm")
            if cancel_label is None:
                cancel_label = self._t("dialog.cancel")
            dialog = adw_module.MessageDialog(
                transient_for=self,
                heading=title,
                body=body,
            )
            if cancel_label is not None:
                dialog.add_response("cancel", cancel_label)
                dialog.set_close_response("cancel")
            dialog.add_response("confirm", confirm_label)
            dialog.set_default_response("confirm")
            dialog.set_response_appearance("confirm", adw_module.ResponseAppearance.SUGGESTED)
            return dialog

        def _refresh_dashboard(self, *, force: bool = False):
            now = self._glib.get_monotonic_time() / 1_000_000
            if (
                not force
                and now - float(getattr(self, "_last_dashboard_refresh_at", 0.0))
                < DASHBOARD_REFRESH_MIN_INTERVAL_SEC
            ):
                return False
            self._last_dashboard_refresh_at = now
            snapshot = self._adapter.read_dashboard_snapshot()
            for key, label in self._dashboard_labels.items():
                label.set_label(str(snapshot.get(key, "-")))

            devices_snapshot = self._adapter.read_devices_snapshot()
            for key, label in getattr(self, "_devices_labels", {}).items():
                value = devices_snapshot.get(key, "-")
                label.set_label(str(value))
            if hasattr(self, "_devices_unpair_btn"):
                show_actions = bool(devices_snapshot.get("pairing_actions_visible", False))
                self._devices_unpair_btn.set_visible(show_actions)
                self._devices_repair_btn.set_visible(show_actions or not bool(devices_snapshot.get("paired", False)))
            if hasattr(self, "_devices_auth_error_banner"):
                auth_error = bool(devices_snapshot.get("invalid_auth_error", False))
                self._devices_auth_error_banner.set_visible(auth_error)
                if auth_error:
                    last_error = str(devices_snapshot.get("last_error") or "invalid_device_auth_token")
                    hint = str(devices_snapshot.get("auth_recovery_hint") or "")
                    self._devices_auth_error_label.set_label(
                        self._t("devices.auth_error.banner").format(error=last_error)
                    )
                    self._devices_auth_error_hint.set_label(
                        self._t("devices.auth_error.hint").format(hint=hint) if hint else ""
                    )

            diagnostics_snapshot = self._adapter.read_diagnostics_snapshot()
            for key, label in getattr(self, "_diagnostics_labels", {}).items():
                value = diagnostics_snapshot.get(key, "-")
                label.set_label(str(value))
            if hasattr(self, "_diagnostics_health_values"):
                health = self._diagnostics_health_values
                health["connection"].set_label(str(snapshot.get("runtime_state") or "-"))
                health["trust"].set_label(str(snapshot.get("trust_state") or "-"))
                health["heartbeat"].set_label(str(snapshot.get("heartbeat_age_sec") or "-"))
                health["logs"].set_label(str(diagnostics_snapshot.get("logs_path") or "-"))

            security_snapshot = self._adapter.read_security_snapshot()
            for key, label in getattr(self, "_security_labels", {}).items():
                value = security_snapshot.get(key, "-")
                if isinstance(value, dict):
                    formatted = ", ".join(f"{k}={'off' if bool(v) is False else 'on'}" for k, v in value.items())
                    label.set_label(formatted)
                elif isinstance(value, list):
                    label.set_label("; ".join(str(item) for item in value))
                else:
                    label.set_label(str(value))
            self._refresh_capability_center()

            updates_snapshot = self._adapter.read_updates_snapshot()
            for key, label in getattr(self, "_updates_labels", {}).items():
                value = updates_snapshot.get(key, "-")
                if isinstance(value, str) and value.startswith("i18n."):
                    value = self._t(value[5:])
                label.set_label(str(value))
            if hasattr(self, "_check_update_btn"):
                paired = bool(snapshot.get("paired", False))
                trust_state = str(snapshot.get("trust_state") or "").strip().lower()
                allow_update_check = paired and trust_state != "unpaired"
                self._check_update_btn.set_sensitive(allow_update_check)
                if hasattr(self, "_header_update_btn"):
                    self._header_update_btn.set_sensitive(allow_update_check)
                if hasattr(self, "_updates_pairing_hint"):
                    self._updates_pairing_hint.set_visible(not allow_update_check)
            if hasattr(self, "_updates_summary_values"):
                summary = self._updates_summary_values
                summary["current_version"].set_label(
                    str(updates_snapshot.get("current_version") or updates_snapshot.get("app_version") or "-")
                )
                pairing_key = str(updates_snapshot.get("pairing_status_label_key") or "label.pairing_required")
                summary["pairing_status"].set_label(self._t(pairing_key))
                summary["channel"].set_label(str(updates_snapshot.get("channel") or updates_snapshot.get("update_channel") or "-"))
            if hasattr(self, "_updates_note_body"):
                latest_version = str(updates_snapshot.get("latest_stable_version") or "").strip() or "latest"
                self._updates_note_body.set_label(
                    self._t("updates.note.discover_troubleshooting").format(version=latest_version)
                )

            if hasattr(self, "_footer_connected_value"):
                self._footer_connected_value.set_label(str(snapshot.get("runtime_state") or "-"))
            if hasattr(self, "_footer_trust_value"):
                self._footer_trust_value.set_label(str(snapshot.get("trust_state") or "-"))
            if hasattr(self, "_footer_heartbeat_value"):
                self._footer_heartbeat_value.set_label(str(snapshot.get("heartbeat_age_sec") or "-"))
            if hasattr(self, "_footer_backend_value"):
                self._footer_backend_value.set_label(str(snapshot.get("backend_base_url") or "-"))
            if hasattr(self, "_onboarding_banner"):
                trust_state = str(snapshot.get("trust_state") or "").strip().lower()
                runtime_state = str(snapshot.get("runtime_state") or "").strip().lower()
                paired = bool(snapshot.get("paired", False))
                needs_pairing = (not paired) or (trust_state == "unpaired" and runtime_state == "offline")
                self._onboarding_banner.set_visible(needs_pairing)

            if hasattr(self, "_saas_labels"):
                saas = self._adapter.read_saas_context_snapshot()
                if not bool(saas.get("available", False)):
                    unavailable = self._t("saas.unavailable")
                    for key in self._saas_labels:
                        self._saas_labels[key].set_label(unavailable if key == "plan" else "-")
                else:
                    plan = str(saas.get("effective_plan") or "-")
                    desc = str(saas.get("plan_description") or "")
                    self._saas_labels["plan"].set_label(f"{plan} — {desc}" if desc else plan)
                    self._saas_labels["subscription"].set_label(
                        str(saas.get("subscription_status") or "-")
                    )
                    caps = saas.get("capabilities") or []
                    cap_lines: list[str] = []
                    if isinstance(caps, list):
                        for row in caps:
                            if not isinstance(row, dict):
                                continue
                            cap_id = str(row.get("id") or "?")
                            allowed = bool(row.get("allowed"))
                            status = self._t(
                                "saas.capability.allowed" if allowed else "saas.capability.denied"
                            )
                            reason = str(row.get("reason") or "").strip()
                            line = self._t("saas.capability_row").format(id=cap_id, status=status)
                            if reason and not allowed:
                                line = f"{line} ({reason})"
                            cap_lines.append(line)
                    self._saas_labels["capabilities"].set_label("\n".join(cap_lines) or "-")
                    usage = saas.get("usage") or {}
                    usage_lines: list[str] = []
                    if isinstance(usage, dict):
                        for feature, row in usage.items():
                            if not isinstance(row, dict):
                                continue
                            usage_lines.append(
                                self._t("saas.usage_row").format(
                                    feature=feature,
                                    used=int(row.get("used") or 0),
                                    limit=int(row.get("limit") or 0),
                                )
                            )
                    self._saas_labels["usage"].set_label("\n".join(usage_lines) or "-")
                    allowed_providers = saas.get("allowed_providers") or []
                    blocked_providers = saas.get("blocked_providers") or []
                    provider_lines: list[str] = []
                    if isinstance(allowed_providers, list) and allowed_providers:
                        provider_lines.append(
                            f"{self._t('saas.allowed')}: {', '.join(str(p) for p in allowed_providers)}"
                        )
                    if isinstance(blocked_providers, list):
                        for row in blocked_providers:
                            if not isinstance(row, dict):
                                continue
                            provider_lines.append(
                                f"{self._t('saas.blocked')}: {row.get('provider_id', '?')} — {row.get('reason', '')}"
                            )
                    self._saas_labels["providers"].set_label("\n".join(provider_lines) or "-")
                if hasattr(self, "_saas_upgrade_btn"):
                    self._saas_upgrade_btn.set_visible(bool(saas.get("available")))

            self._refresh_header_status(snapshot, devices_snapshot)
            self._refresh_avatar_context(force=force)
            return True

        def _refresh_dashboard_forced(self) -> None:
            self._refresh_dashboard(force=True)

        def _refresh_header_status(self, snapshot: dict[str, object], devices_snapshot: dict[str, object]):
            if not hasattr(self, "_header_status_values"):
                return
            caps = self._adapter.read_capability_snapshot()
            granted_ui, _total_ui = self._adapter.read_active_ui_grant_counts()
            root_enabled = bool(caps.get("root_operations", {}).get("enabled", False))
            values = self._header_status_values
            values["device"].set_label(self._format_device_name(devices_snapshot))
            values["trust"].set_label(str(snapshot.get("trust_state") or "-"))
            values["avatar_mode"].set_label(
                self._t("cap.avatar.mode.enabled") if root_enabled else self._t("cap.avatar.mode.inactive")
            )
            values["active_permissions"].set_label(str(granted_ui))
            values["connection"].set_label(str(snapshot.get("runtime_state") or "-"))
            owner = self._adapter.read_owner_display_snapshot()
            owner_label = str(owner.get("owner_email") or owner.get("owner_name") or "-")
            if "owner" in values:
                values["owner"].set_label(owner_label)
            self._update_orchestra_status_ui(self._orchestra_status_snapshot)

        def _orchestra_status_label_key(self, state: OrchestraStatusState | None) -> str:
            mapping = {
                OrchestraStatusState.CLOUD_ACTIVE: "status.orchestra.cloud_active",
                OrchestraStatusState.LOCAL_FALLBACK: "status.orchestra.local_fallback",
                OrchestraStatusState.OFFLINE: "status.orchestra.offline",
            }
            if state is None:
                return "status.orchestra.offline"
            return mapping.get(state, "status.orchestra.offline")

        def _orchestra_status_css_class(self, state: OrchestraStatusState | None) -> str:
            mapping = {
                OrchestraStatusState.CLOUD_ACTIVE: "orchestra-cloud-active",
                OrchestraStatusState.LOCAL_FALLBACK: "orchestra-local-fallback",
                OrchestraStatusState.OFFLINE: "orchestra-offline",
            }
            if state is None:
                return "orchestra-offline"
            return mapping.get(state, "orchestra-offline")

        def _orchestra_status_modal_body(self, state: OrchestraStatusState | None) -> str:
            if state is None:
                state = OrchestraStatusState.OFFLINE
            prefix = f"status.orchestra.{state.value}.bullet"
            bullets = [self._t(f"{prefix}{idx}") for idx in (1, 2, 3)]
            body = "\n\n".join(f"• {line}" for line in bullets)
            snapshot = getattr(self, "_orchestra_status_snapshot", None)
            if snapshot is not None:
                reason = str(getattr(snapshot, "fallback_reason", "") or "").strip()
                if reason:
                    reason_key = f"status.orchestra.reason.{reason}"
                    reason_text = self._t(reason_key)
                    if reason_text != reason_key:
                        body = f"{body}\n\n{self._t('status.orchestra.reason.label')}: {reason_text}"
                detail = str(getattr(snapshot, "detail", "") or "").strip()
                if detail and detail not in {reason, ""}:
                    body = f"{body}\n\n{self._t('status.orchestra.detail.label')}: {detail}"
            return body

        def _run_orchestra_diagnostic(self) -> None:
            paired = bool(self._adapter.read_dashboard_snapshot().get("paired", False))
            creds = self._adapter.read_cloud_probe_credentials()
            backend_url = creds[0] if creds else None
            auth_token = creds[1] if creds else None
            try:
                snapshot = probe_orchestra_status(
                    device_paired=paired,
                    backend_url=backend_url,
                    auth_token=auth_token,
                    config_path=self._adapter._config_path,
                )
            except Exception:
                snapshot = OrchestraStatusSnapshot(state=OrchestraStatusState.OFFLINE)
            self._orchestra_status_snapshot = snapshot
            self._update_orchestra_status_ui(snapshot)
            state = snapshot.state
            show_modal_action_required(
                self._gtk,
                self._adw,
                parent=self,
                title=self._t("modal.orchestra_status.title"),
                body=f"{self._t(self._orchestra_status_label_key(state))}\n\n{self._orchestra_status_modal_body(state)}",
                buttons=[("close", self._t("modal.orchestra_status.done"))],
                on_response=lambda _response: None,
                default_button="close",
            )

        def _update_orchestra_status_ui(self, snapshot: OrchestraStatusSnapshot | None) -> None:
            if not hasattr(self, "_orchestra_status_pill"):
                return
            from ..orchestra_routing_tracker import last_routing_path, resolve_effective_orchestra_state

            state = (
                resolve_effective_orchestra_state(snapshot, last_path=last_routing_path())
                if snapshot is not None
                else OrchestraStatusState.OFFLINE
            )
            label_key = self._orchestra_status_label_key(state)
            label = self._t(label_key)
            pill = self._orchestra_status_pill
            for css_name in ("orchestra-cloud-active", "orchestra-local-fallback", "orchestra-offline"):
                pill.remove_css_class(css_name)
            pill.add_css_class(self._orchestra_status_css_class(state))
            pill.set_label(label)
            if hasattr(self, "_orchestra_status_btn"):
                self._orchestra_status_btn.set_tooltip_text(label)

        def _refresh_orchestra_status_async(self, *_args) -> bool:
            if getattr(self, "_orchestra_status_probe_in_flight", False):
                return False
            self._orchestra_status_probe_in_flight = True
            paired = bool(self._adapter.read_dashboard_snapshot().get("paired", False))

            def _worker() -> None:
                try:
                    creds = self._adapter.read_cloud_probe_credentials()
                    backend_url = creds[0] if creds else None
                    auth_token = creds[1] if creds else None
                    snapshot = probe_orchestra_status(
                        device_paired=paired,
                        backend_url=backend_url,
                        auth_token=auth_token,
                        config_path=self._adapter._config_path,
                    )
                except Exception:
                    snapshot = OrchestraStatusSnapshot(state=OrchestraStatusState.OFFLINE)

                def _apply() -> bool:
                    self._orchestra_status_probe_in_flight = False
                    self._orchestra_status_snapshot = snapshot
                    self._update_orchestra_status_ui(snapshot)
                    return False

                self._glib.idle_add(_apply)

            threading.Thread(target=_worker, daemon=True).start()
            return False

        def _orchestra_status_refresh_timer(self):
            self._refresh_orchestra_status_async()
            return True

        def _on_orchestra_status_clicked(self, _btn) -> None:
            snapshot = self._orchestra_status_snapshot
            state = snapshot.state if snapshot is not None else OrchestraStatusState.OFFLINE
            buttons: list[tuple[str, str]] = [("diagnose", self._t("modal.orchestra_status.check"))]
            buttons.append(("close", self._t("modal.orchestra_status.done")))

            def _on_modal_response(response: str) -> None:
                if response == "diagnose":
                    self._run_orchestra_diagnostic()

            show_modal_action_required(
                self._gtk,
                self._adw,
                parent=self,
                title=self._t("modal.orchestra_status.title"),
                body=f"{self._t(self._orchestra_status_label_key(state))}\n\n{self._orchestra_status_modal_body(state)}",
                buttons=buttons,
                on_response=_on_modal_response,
                default_button="close",
            )

        def _on_language_changed(self, _dropdown, _pspec):
            idx = int(self._lang_switch.get_selected())
            if idx < 0 or idx >= len(self._lang_codes):
                return
            self._lang = self._lang_codes[idx]
            self._adapter.update_owner_interface_language(self._lang)
            self._apply_locale()
            self._reset_avatar_welcome()
            self._refresh_dashboard(force=True)

        def _sync_owner_interface_language_on_startup(self) -> bool:
            self._sync_owner_interface_language(force_apply=True)
            return False

        def _sync_owner_interface_language(self, *, force_apply: bool = False) -> None:
            def _worker() -> None:
                profile_lang = self._adapter.fetch_owner_interface_language()

                def _apply() -> bool:
                    if not profile_lang:
                        return False
                    resolved = resolve_ui_language(
                        profile_lang=profile_lang,
                        cached_lang=self._adapter.load_cached_ui_language(),
                        env_override=os.getenv("IUVE_AGENT_UI_LANG", ""),
                    )
                    if resolved == self._lang and not force_apply:
                        return False
                    self._lang = resolved
                    self._adapter.save_cached_ui_language(resolved)
                    self._set_lang_dropdown_selection()
                    self._apply_locale()
                    self._reset_avatar_welcome()
                    self._refresh_dashboard(force=True)
                    return False

                self._glib.idle_add(_apply)

            threading.Thread(target=_worker, daemon=True).start()

        def _set_lang_dropdown_selection(self) -> None:
            if not hasattr(self, "_lang_switch"):
                return
            try:
                idx = self._lang_codes.index(self._lang)
            except ValueError:
                idx = 0
            self._lang_switch.set_selected(idx)

        def _reset_avatar_welcome(self) -> None:
            if not hasattr(self, "_avatar_conversation_buffer"):
                return
            self._avatar_conversation_buffer.set_text("")
            self._avatar_append_message(
                self._t("avatar.chat.avatar"),
                self._t("avatar.chat.welcome"),
                role="avatar",
            )

        def _apply_locale(self):
            self.set_title(self._t("app.title"))
            self._header_title_label.set_label(self._t("app.title"))
            try:
                self._agent_status_overlay().set_locale(self._lang)
            except Exception:
                pass

            self._open_connect_btn.set_label(self._t("action.open_connect"))
            self._open_logs_btn.set_label(self._t("action.open_logs"))
            self._restart_btn.set_label(self._t("action.restart_agent"))
            self._export_btn.set_label(self._t("action.export_diagnostics"))
            self._header_refresh_btn.set_label(self._t("header.refresh"))
            if hasattr(self, "_header_update_btn"):
                self._header_update_btn.set_label(self._t("action.check_update"))
            self._header_settings_btn.set_label(self._t("header.settings"))
            self._header_profile_btn.set_label(self._t("header.profile"))
            self._header_fullscreen_btn.set_label(
                self._t("header.fullscreen.exit") if self._is_fullscreen else self._t("header.fullscreen.enter")
            )
            if hasattr(self, "_pair_device_btn"):
                self._pair_device_btn.set_label(self._t("onboarding.pair.button"))
            if hasattr(self, "_devices_unpair_btn"):
                self._devices_unpair_btn.set_label(self._t("devices.unpair.button"))
            if hasattr(self, "_devices_repair_btn"):
                self._devices_repair_btn.set_label(self._t("devices.repair.button"))
            if hasattr(self, "_devices_open_security_btn"):
                self._devices_open_security_btn.set_label(self._t("devices.open_security.link"))
            if hasattr(self, "_devices_revoke_distinct_hint"):
                self._devices_revoke_distinct_hint.set_label(self._t("devices.revoke_all.distinct.hint"))
            if hasattr(self, "_check_update_btn"):
                self._check_update_btn.set_label(self._t("action.check_update"))
            if hasattr(self, "_updates_header_title"):
                self._updates_header_title.set_label(self._t("updates.page.title"))
            if hasattr(self, "_updates_header_subtitle"):
                self._updates_header_subtitle.set_label(self._t("updates.page.subtitle"))
            if hasattr(self, "_updates_status_title"):
                self._updates_status_title.set_label(self._t("updates.status.title"))
            if hasattr(self, "_updates_pairing_hint"):
                self._updates_pairing_hint.set_label(self._t("updates.pairing.required.hint"))
            if hasattr(self, "_updates_note_title"):
                self._updates_note_title.set_label(self._t("updates.troubleshooting.title"))
            if hasattr(self, "_updates_note_body"):
                self._updates_note_body.set_label(self._t("updates.note.discover_troubleshooting"))
            if hasattr(self, "_diagnostics_header_title"):
                self._diagnostics_header_title.set_label(self._t("diagnostics.page.title"))
            if hasattr(self, "_diagnostics_header_subtitle"):
                self._diagnostics_header_subtitle.set_label(self._t("diagnostics.page.subtitle"))
            if hasattr(self, "_diagnostics_refresh_scan_btn"):
                scanning = bool(getattr(self, "_diagnostics_device_scan_active", False))
                self._diagnostics_refresh_scan_btn.set_label(
                    self._t("diagnostics.action.refresh_scan.scanning")
                    if scanning
                    else self._t("diagnostics.action.refresh_scan")
                )
            if hasattr(self, "_diagnostics_scan_status_label") and getattr(
                self, "_diagnostics_device_scan_active", False
            ):
                self._diagnostics_scan_status_label.set_label(self._t("diagnostics.scan.status.scanning"))
            if hasattr(self, "_avatar_mic_btn"):
                self._avatar_mic_btn.set_tooltip_text(self._t("avatar.input.voice"))
            if hasattr(self, "_avatar_chat_input_bar"):
                self._refresh_avatar_chat_input_bar()
            if hasattr(self, "_avatar_session_start_btn"):
                self._avatar_session_start_btn.set_label(self._t("avatar.action.session_start"))
            if hasattr(self, "_avatar_session_skip_btn"):
                self._avatar_session_skip_btn.set_label(self._t("avatar.action.session_skip"))
            if hasattr(self, "_avatar_open_permissions_btn"):
                self._avatar_open_permissions_btn.set_label(self._t("avatar.action.open_permissions"))
            if hasattr(self, "_avatar_open_diagnostics_btn"):
                self._avatar_open_diagnostics_btn.set_label(self._t("avatar.action.open_diagnostics"))
            if hasattr(self, "_avatar_upgrade_tariff_btn"):
                self._avatar_upgrade_tariff_btn.set_label(self._t("saas.upgrade"))
            if hasattr(self, "_avatar_cancel_btn"):
                self._avatar_cancel_btn.set_label(self._t("avatar.action.cancel"))
            if hasattr(self, "_avatar_execute_btn"):
                pending_plan = getattr(self, "_avatar_pending_plan", None)
                self._avatar_execute_btn.set_label(
                    self._t("avatar.action.execute_plan")
                    if pending_plan
                    else self._t("avatar.action.execute")
                )
            if hasattr(self, "_avatar_conversation_title"):
                self._avatar_conversation_title.set_label(self._t("avatar.conversation.title"))
            if hasattr(self, "_avatar_status_header"):
                self._avatar_status_header.set_label(self._t("avatar.title"))
            if hasattr(self, "_avatar_intro_label"):
                self._avatar_intro_label.set_label(self._t("avatar.intro").format(device=self._format_device_name(self._adapter.read_devices_snapshot())))
            if hasattr(self, "_avatar_context_title"):
                self._avatar_context_title.set_label(self._t("avatar.device_context.title"))
            if hasattr(self, "_avatar_accuracy_disclaimer"):
                self._avatar_accuracy_disclaimer.set_label(self._t("avatar.chat.accuracy_disclaimer"))
            if hasattr(self, "_avatar_advisory_note"):
                self._avatar_advisory_note.set_label(self._t("avatar.advisory.only"))
            if hasattr(self, "_avatar_onboarding_label"):
                self._avatar_onboarding_label.set_label(self._t("avatar.unpaired.banner"))
            if hasattr(self, "_avatar_pair_device_btn"):
                self._avatar_pair_device_btn.set_label(self._t("avatar.unpaired.pair"))
            if hasattr(self, "_avatar_onboarding_permissions_btn"):
                self._avatar_onboarding_permissions_btn.set_label(self._t("avatar.unpaired.permissions"))
            if hasattr(self, "_avatar_onboarding_diagnostics_btn"):
                self._avatar_onboarding_diagnostics_btn.set_label(self._t("avatar.unpaired.diagnostics"))

            if hasattr(self, "_header_status_prefixes"):
                self._header_status_prefixes["device"].set_label(f"{self._t('header.device')}:")
                self._header_status_prefixes["trust"].set_label(f"{self._t('header.trust')}:")
                self._header_status_prefixes["avatar_mode"].set_label(f"{self._t('header.avatar_mode')}:")
                self._header_status_prefixes["active_permissions"].set_label(f"{self._t('header.active_permissions')}:")
                self._header_status_prefixes["connection"].set_label(f"{self._t('header.connection')}:")
                if "orchestra" in self._header_status_prefixes:
                    self._header_status_prefixes["orchestra"].set_label(f"{self._t('header.orchestra')}:")
            self._update_orchestra_status_ui(self._orchestra_status_snapshot)

            if hasattr(self, "_cap_page_title"):
                self._cap_page_title.set_label(self._t("cap.page.title"))
            if hasattr(self, "_cap_page_subtitle"):
                self._cap_page_subtitle.set_label(self._t("cap.page.subtitle"))
            if hasattr(self, "_cap_grants_permanent_title"):
                self._cap_grants_permanent_title.set_label(self._t("cap.grants.profile.title"))
            if hasattr(self, "_cap_grants_permanent_subtitle"):
                self._cap_grants_permanent_subtitle.set_label(self._t("cap.grants.profile.subtitle"))
            if hasattr(self, "_profile_summary_title"):
                self._profile_summary_title.set_label(self._t("profile.summary.title"))
            if hasattr(self, "_profile_lifetime_label"):
                self._profile_lifetime_label.set_label(self._t("profile.lifetime.label"))
            if hasattr(self, "_action_sessions_auto_note"):
                self._action_sessions_auto_note.set_label(self._t("security.task_sessions.note"))
            if hasattr(self, "_recommended_safe_btn"):
                self._recommended_safe_btn.set_label(self._t("security.recommended_safe.button"))
            if hasattr(self, "_recommended_safe_hint"):
                self._recommended_safe_hint.set_label(self._t("security.recommended_safe.hint"))
            if hasattr(self, "_security_revoke_all_btn"):
                self._security_revoke_all_btn.set_label(self._t("security.status.revoke_all"))
            if hasattr(self, "_security_profile_cards"):
                for preset_key, widgets in self._security_profile_cards.items():
                    widgets["name"].set_label(self._t(profile_display_i18n_key(preset_key)))
                    widgets["summary"].set_label(self._t(f"{profile_display_i18n_key(preset_key)}.summary"))
            if hasattr(self, "_cap_manual_expander"):
                self._cap_manual_expander.set_label(self._t("security.advanced.title"))
            if hasattr(self, "_security_additional_expander"):
                self._security_additional_expander.set_label(self._t("security.additional.title"))
            self._refresh_security_status_line()
            if hasattr(self, "_cap_table_title_lbl"):
                self._cap_table_title_lbl.set_label(self._t("cap.table.title"))
            if hasattr(self, "_cap_policy_btn"):
                self._cap_policy_btn.set_label(self._t("cap.policy.link"))
            if hasattr(self, "_col_permission_lbl"):
                self._col_permission_lbl.set_label(self._t("cap.col.permission"))
            if hasattr(self, "_col_grant_lbl"):
                self._col_grant_lbl.set_label(self._t("cap.col.grant"))
            if hasattr(self, "_col_risk_lbl"):
                self._col_risk_lbl.set_label(self._t("cap.col.risk"))
            if hasattr(self, "_col_status_lbl"):
                self._col_status_lbl.set_label(self._t("cap.col.status"))
            if hasattr(self, "_col_expiry_lbl"):
                self._col_expiry_lbl.set_label(self._t("cap.col.expiry"))
            if hasattr(self, "_col_actions_lbl"):
                self._col_actions_lbl.set_label(self._t("cap.col.actions"))
            if hasattr(self, "_cap_grant_selection_hint"):
                self._cap_grant_selection_hint.set_label(self._t("cap.grant_selection.hint"))
            if hasattr(self, "_cap_apply_btn"):
                self._cap_apply_btn.set_label(self._t("cap.grant_selection.apply"))
            if hasattr(self, "_ai_providers_refresh_btn"):
                self._ai_providers_refresh_btn.set_label(self._t("ai_providers.action.refresh"))
            if hasattr(self, "_cap_selection_count_lbl"):
                self._update_cap_selection_count_label()
            if hasattr(self, "_active_panel_title"):
                self._active_panel_title.set_label(self._t("cap.active_permissions.title"))
            if hasattr(self, "_action_sessions_title"):
                self._action_sessions_title.set_label(self._t("cap.action_sessions.title"))
            if hasattr(self, "_action_sessions_subtitle"):
                self._action_sessions_subtitle.set_label(self._t("cap.action_sessions.subtitle"))
            if hasattr(self, "_action_sessions_active_header"):
                self._action_sessions_active_header.set_label(self._t("cap.action_sessions.running.title"))
            if hasattr(self, "_action_session_preset_cards"):
                for card_key, widgets in self._action_session_preset_cards.items():
                    widgets["title"].set_label(self._t(f"cap.action_sessions.card.{card_key}.user_title"))
                    duration_key = (
                        f"cap.action_sessions.card.{card_key}.duration_label"
                        if card_key == "full_assistance"
                        else f"cap.action_sessions.card.{card_key}.duration"
                    )
                    widgets["duration"].set_label(self._t(duration_key))
                    widgets["summary"].set_label(self._t(f"cap.action_sessions.card.{card_key}.summary"))
            if hasattr(self, "_cloud_suggestions_title"):
                self._cloud_suggestions_title.set_label(self._t("cap.action_sessions.cloud.title"))
            if hasattr(self, "_action_sessions_content"):
                self._refresh_action_sessions_panel()
            if hasattr(self, "_audit_btn"):
                self._audit_btn.set_label(self._t("cap.audit.open"))
            if hasattr(self, "_revoke_sessions_btn"):
                self._revoke_sessions_btn.set_label(self._t("cap.revoke_all_sessions"))
            if hasattr(self, "_revoke_all_btn"):
                self._revoke_all_btn.set_label(self._t("cap.revoke_all_grants"))
            if hasattr(self, "_command_log_title"):
                self._command_log_title.set_label(self._t("cap.command_log.title"))
            if hasattr(self, "_command_log_filter_drop"):
                selected = int(self._command_log_filter_drop.get_selected())
                filter_model = gtk_module.StringList.new(
                    [
                        self._t("cap.command_log.filter.all"),
                        self._t("cap.command_log.filter.allow"),
                        self._t("cap.command_log.filter.deny"),
                    ]
                )
                self._command_log_filter_drop.set_model(filter_model)
                if selected >= 0:
                    self._command_log_filter_drop.set_selected(selected)
            if hasattr(self, "_cap_refresh_btn"):
                self._cap_refresh_btn.set_label(self._t("cap.refresh"))
            if hasattr(self, "_saas_refresh_btn"):
                self._saas_refresh_btn.set_label(self._t("saas.refresh"))
            if hasattr(self, "_saas_upgrade_btn"):
                self._saas_upgrade_btn.set_label(self._t("saas.upgrade"))
            if hasattr(self, "_cap_widgets"):
                for cap_key, widgets in self._cap_widgets.items():
                    risk = str(widgets.get("risk", "medium"))
                    if "name_lbl" in widgets:
                        widgets["name_lbl"].set_label(self._t(f"cap.{cap_key}"))
                    if "desc_lbl" in widgets:
                        widgets["desc_lbl"].set_label(self._t(f"cap.description.{cap_key}"))
                    if "risk_lbl" in widgets:
                        widgets["risk_lbl"].set_label(self._t(f"cap.risk.{risk}"))
                    if "grant_btn" in widgets:
                        widgets["grant_btn"].set_label(self._t("cap.grant"))
                    if "revoke_btn" in widgets:
                        widgets["revoke_btn"].set_label(self._t("cap.revoke"))
                    if "expiry_drop" in widgets:
                        expiry_drop = widgets["expiry_drop"]
                        expiry_model = gtk_module.StringList.new(
                            [self._t(f"cap.expiry.{k}") for k in self._EXPIRY_PRESET_KEYS]
                        )
                        expiry_drop.set_model(expiry_model)

            for widget, key in self._i18n_labels:
                widget.set_label(self._t(key))

            page_titles = {
                "avatar": "nav.avatar",
                "dashboard": "nav.dashboard",
                "devices": "nav.devices",
                "diagnostics": "nav.diagnostics",
                "security": "nav.security",
                "updates": "nav.updates",
            }
            for name, key in page_titles.items():
                child = self._stack_pages.get(name)
                if child is None:
                    continue
                page = self._stack.get_page(child)
                page.set_title(self._t(key))

        def _refresh_timer(self):
            self._refresh_dashboard(force=False)
            self._poll_pending_command_confirmation()
            return True

    window = MainWindow()
    try:
        from iuve_connect_agents.desktop.voice.ptt_hotkey import bind_ptt_hotkey
        from iuve_connect_agents.desktop.voice.voice_presence_service import VoicePresenceService

        # Window-scoped service; noop when VOICE_PRESENCE_ENABLED=false (production default).
        window._voice_presence_service = VoicePresenceService(locale=getattr(window, "_lang", "") or "")

        def _on_ptt() -> None:
            try:
                window._voice_presence_service.start_capture(trigger="push_to_talk")
            except Exception:
                pass

        bind_ptt_hotkey(window=window, gtk_module=gtk_module, on_ptt=_on_ptt)
    except Exception:
        pass
    return window
