# How `openlogi-agent-core` Orchestrates Agent Functionality: Inside the Orchestrator

> Discover how the openlogi-agent-core orchestrator centralizes configuration and synchronizes state across agent components via atomic rebuilds and shared memory.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-27

---

**The `Orchestrator` in `openlogi-agent-core` acts as the central coordinator that owns user configuration, device inventory, and shared runtime structures, synchronizing state across OS hooks, HID++ watchers, and GUI components through atomic rebuilds and Arc-based shared memory.**

The OpenLogi agent relies on a central `Orchestrator` to translate static configuration and live device enumeration into coherent runtime behavior. Located in [`crates/openlogi-agent-core/src/orchestrator.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/orchestrator.rs), this component serves as the single source of truth for how the system orchestrates agent functionality across background workers and foreground GUI interactions.

## The Three Phases of Orchestration

The `Orchestrator` manages agent lifecycle through three distinct phases, each handling specific responsibilities for state consistency.

### Initialization Phase

During startup, the `Orchestrator::new` function (lines 96-133) creates a fresh `SharedRuntime` populated with empty `Arc`s and seeds them from the loaded `Config`. This phase establishes the foundational runtime structures that background threads will consume.

The initialization sequence:
- Instantiates `RwLock`s and `Arc` containers for hook maps, capture plans, and DPI cycles
- Seeds observable state with the launch-at-login flag
- Triggers the first `rebuild()` to publish initial hook maps and DPI state

### State Synchronization Phase

The orchestrator continuously refreshes device inventory through `refresh_inventory`, `publish_device_runtime`, `rebuild`, and `publish_inventory` (lines 158-230). This phase reconciles the physical device state with the agent's internal representation.

When the inventory watcher detects changes, the orchestrator:
- Updates the `InventoryState` enum (`Pending`, `Ready`, or `Unavailable`)
- Recomputes per-device facts into `AgentDevice` snapshots
- Determines if volatile settings must be reapplied after system wake events
- Triggers full rebuilds only when device routes or selection changes

### Runtime Mutation Phase

User-driven changes flow through dedicated mutation methods: `set_current_app`, `reload_config`, `set_camera_active`, and `set_manual_light_power` (lines 270-340). Each mutation repopulates relevant `Arc`s and notifies the observable IPC cell to maintain GUI consistency.

## Core Data Structures

Four primary structures enable the orchestrator to coordinate thread-safe access across the agent.

### AgentDevice

The `AgentDevice` struct (lines 43-64) represents a minimal per-device snapshot containing the config key, HID++ route, capabilities, and online flag. Background workers use these snapshots to build capture plans and DPI cycles without blocking on the main orchestrator lock.

### SharedRuntime

`SharedRuntime` (lines 66-107) holds all mutable runtime state as cheap-to-clone `Arc`s, including `hook_maps`, `capture_plans`, `dpi_cycle`, and channel pools. Background threads read these structures without contention while the orchestrator atomically rewrites them during rebuilds.

### ObservableState

Defined in [`crates/openlogi-agent-core/src/observable.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/observable.rs), this IPC-visible cell exposes public fields like `inventory()`, `status()`, and `camera_active` that the GUI polls. The orchestrator updates this state whenever public-facing fields change, ensuring the GUI reflects the current device reality.

### InventoryState

The `InventoryState` enum (lines 174-186) tracks the enumeration lifecycle, storing the latest `DeviceInventory` snapshots and signaling whether the inventory is `Pending`, `Ready`, or `Unavailable` for downstream consumers.

## How the Orchestrator Coordinates Runtime Components

### Device Discovery and Inventory Management

The `refresh_inventory` method processes updates from the inventory watcher each tick. It updates `inventory` and `hid_open_failures` counters, then calls the static helper `build_devices` to transform raw HID++ inventory into `AgentDevice` instances.

If device routes changed or the selected device switched, the orchestrator updates `self.devices`, selects a new current device, and triggers a full `rebuild()`. If only online flags changed, it republishes minimal updates to avoid unnecessary computation.

### Publishing Runtime Views

The `publish_device_runtime` method computes capture plans via `capture_plans_for`, generating one plan per online device reflecting current config and foreground app context. It then rebuilds per-device DPI cycles through `rebuild_dpi_cycles` and updates global keyboard bindings and host-switch links.

### Hook Map Generation

The `hook_maps_for` method generates a `HookMaps` struct that merges button bindings and OS-hook gestures for a specific device key and foreground application. This result is written atomically into `shared.hook_maps`, ensuring the OS-hook never observes a half-updated state that could cause missed inputs or race conditions.

### Handling Dynamic Mutations

**Foreground app changes** invoke `set_current_app`, which swaps app-scoped hook maps and republishes capture plans immediately.

**Configuration reloads** through `reload_config` swap the config, republish scroll preferences, update the launch-at-login flag, and retain manual light overrides when still applicable. This method also reapplies native wheel modes, Fn-locks, and light settings across all connected devices.

**Camera-linked lighting** uses `set_camera_active` to toggle lights across capable devices while clearing manual overrides, while `set_manual_light_power` records transient overrides for specific light routes that persist only while the device remains camera-linked.

### Volatile Settings Re-application

When detecting system wake events, `reapply_volatile_on_next_refresh` sets an internal flag. During the subsequent `refresh_inventory` call, the orchestrator invokes `reapply_volatile_settings` for every online device, writing DPI, SmartShift, wheel mode, lighting, and Fn-lock values in background threads to restore hardware state lost during sleep.

## Working with the Orchestrator

Use the `Orchestrator` type to bridge configuration, device discovery, and GUI state:

```rust
use openlogi_core::config::Config;
use openlogi_agent_core::{Orchestrator, ObservableState};
use std::sync::Arc;

// 1️⃣ Initialise the orchestrator from a loaded config.
let config: Config = /* load Config from toml */;
let observable = Arc::new(ObservableState::new());
let mut orch = Orchestrator::new(config, observable.clone());

// 2️⃣ Periodically refresh the device inventory (called by the inventory watcher).
fn on_inventory_tick(
    inventories: &[DeviceInventory],
    standalone: &[StandaloneDevice],
    hid_open_failures: bool,
) {
    orch.refresh_inventory(inventories, standalone, hid_open_failures);
}

// 3️⃣ React to a foreground‑app change from the GUI.
fn foreground_changed(app: Option<ForegroundApp>) {
    orch.set_current_app(app);
}

// 4️⃣ Reload the configuration after the user edits `config.toml`.
fn reload(new_cfg: Config) {
    orch.reload_config(new_cfg);
}

// 5️⃣ Query the IPC‑exposed state (e.g., for the GUI).
let current_inventory = orch.inventory();       // Vec<DeviceInventory>
let health = orch.inventory_health();           // InventoryHealth enum
let ring = orch.action_ring_session(None);     // Option<ActionRingSessionSpec>

```

## Summary

- The `Orchestrator` in `openlogi-agent-core` serves as the single source of truth for device state, configuration, and runtime mappings.
- Three operational phases—initialization, state synchronization, and runtime mutation—ensure consistent behavior across system events and user interactions.
- `SharedRuntime` uses `Arc`-based structures to provide lock-free reads for background workers while maintaining atomic updates from the main thread.
- Device inventory changes trigger selective updates, with full rebuilds occurring only when hardware routes or selected devices change.
- The observable state pattern enables reactive GUI updates without polling the hardware directly.

## Frequently Asked Questions

### How does `SharedRuntime` enable thread-safe coordination?

`SharedRuntime` stores all mutable state—including hook maps, capture plans, and DPI cycles—as `std::sync::Arc` pointers that can be cloned cheaply. Background threads like the OS-hook and HID++ gesture watcher hold clones of these `Arc`s and read them without acquiring locks. When the orchestrator updates state, it creates new `Arc` allocations and atomically swaps the pointers, ensuring readers never observe partial writes or block on the update path.

### What triggers a full `rebuild()` versus a partial update?

A full rebuild occurs when `refresh_inventory` detects changes to device routes, the selected device, or the set of connected hardware. In [`crates/openlogi-agent-core/src/orchestrator.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/orchestrator.rs) lines 158-230, the orchestrator checks `reapply_targets` and `plan_reapply` flags to determine if hardware capabilities changed. If only online flags toggle without structural changes, the orchestrator calls `publish_inventory` to update availability status without recomputing hook maps or capture plans.

### How does the orchestrator handle configuration reloads at runtime?

The `reload_config` method (lines 270-340) accepts a new `Config` object and performs a coordinated swap. It updates scroll preferences in `SharedRuntime`, recomputes the selected device based on new configuration rules, and conditionally retains manual light overrides if they remain applicable. The method then triggers a full `rebuild()` to regenerate hook mappings and capture plans that reflect the new settings, ensuring zero-downtime configuration updates.

### What is the purpose of the `AgentDevice` snapshot?

`AgentDevice` (lines 43-64) provides a minimal, serializable view of device capabilities and routing information that the orchestrator uses to build execution plans. Unlike the full HID++ device objects, these snapshots contain only the config key, route, capabilities bitmask, and online status. This abstraction allows the `Orchestrator` to compute capture plans and DPI cycles without holding references to low-level hardware handles that could block or fail during enumeration.