Skip to main content

Loc.ai:Link Changelog

All notable changes migrating from locai-link-old to locai-link-new.

Loc.ai:Link has evolved into a single-process, pipeline-based architecture that replaces the older monolithic system, improving efficiency and maintainability. It now features a plugin-driven design with typed configuration, enabling flexible integration of AI models and components. With OTA updates, session recovery, and service deployment, it is a fully modular, production-ready agent framework.

Releases

The latest tagged releases of Locai:Link. The migration narrative from locai-link-old is preserved further down as a historical record.

Link is now a standalone app on macOS and Linux

Link ships with a GUI installer, a Setup Assistant, and a menu-bar companion on macOS and Linux as of 1.0.19. See Using the Link App for the install and usage guide. A Windows GUI installer is on the roadmap but not shipping yet; Windows users should follow the source-install path. The HTTP surface that integrators consume is unchanged; see Your Own Software.

1.2.0 · 2026-07-24

Workspace access from the menu bar, model removal from the device, and a self-cleaning uninstall. The companion can now open your Locai Workspace directly, remove individual models without opening Control, and uninstalling Link now tidies up after itself on the Control dashboard.

Added

  • Open a Workspace from the menu bar. A new tray item opens workspace.locai.co.uk in your default browser, taking you straight to Locai Workspace.
  • Remove a model from the device. Deployed models in the companion now have a per-model Remove action that deletes the model from the device and frees the disk space. Removing a serving model stops it before deletion, the Control dashboard updates to reflect the change, and the model stays available to re-download later. See Removing a model.

Changed

  • Uninstalling now deregisters the device. Uninstalling the companion deregisters the device from Control, removing the stale offline dashboard row and freeing the device-limit slot. Per-user Setup Assistant data is also fully removed on uninstall. See Deregistering a Device.
  • macOS installer advertises Apple Silicon only. Intel Macs are no longer offered an incompatible build.
  • Hardened archive extraction. Archive extraction during router and plugin provisioning now uses the same path-traversal-guarded extractor as the updater. Service manager commands also no longer run through a shell, and the source-install service label now uses the same reverse-DNS namespace as the packaged app (internal hardening, no user-facing behaviour change).

Fixed

  • Version visible everywhere. The Preferences window now shows the installed version string on all install layouts.
  • No more duplicate tray icons. Launching the companion when it is already running now focuses the existing window and exits cleanly.

1.1.0 · 2026-07-16

Whole-app over-the-air updates and in-app model downloads. Updating a device now refreshes the menu-bar companion and Setup Assistant alongside the agent runtime, macOS gets a working automatic-update path for the first time, and the companion can pull new models on demand without reopening the Setup Assistant.

Added

  • Automatic updates now cover the whole app. Updating a device refreshes the menu-bar companion and Setup Assistant, not just the agent runtime. Only the parts that actually changed are reinstalled, and the companion restarts itself so the new version takes effect straight away.
  • Automatic updates on macOS. macOS devices can now update over the air for the first time. Update checks run through Control, so they are safe to use across a fleet, and each release ships a signed, notarised update package so an updated app stays trusted by macOS.
  • Download models from the companion. The menu-bar companion can now request and download extra models straight from its Available Models list, with live progress, without reopening the Setup Assistant.

Changed

  • Smarter model picker in the Setup Assistant. The model list now highlights a recommended default and only shows the models this device can actually run, so it is easier to pick one that will work.

1.0.19 · 2026-07-10

The first end-to-end graphical install path for macOS and Linux. A signed .pkg (macOS) or tarball (Linux) installs the runtime together with two apps: a Setup Assistant that signs you in, registers the device with Control, and deploys the models you pick, and a menu-bar companion that shows agent health, deployed models with live download progress, and a Preferences window. See Using the Link App for the full guide.

Added

  • Setup Assistant. A first-run onboarding wizard: sign in with your Locai account (browser sign-in flow), name the device, choose whether Link starts at login, register with Control, and optionally queue models for download at the end. Launching it again later detects the existing install and offers Continue, Re-register, or Uninstall.
  • Menu-bar companion. A tray app that shows whether the agent is online, lists in-flight downloads with live progress and deployed models with serve and stop toggles, opens the Control dashboard, and hosts a Preferences window with device identity, agent status and version, network status, per-model controls, and log access.
  • Cancel a model download from the device. In-flight downloads show a Cancel button in the companion's Preferences. Cancelling stops the download immediately and cleans up the partial file.
  • macOS .pkg installer. A signed, notarised installer that installs system-wide, launches the Setup Assistant automatically when it finishes, and ships a matching uninstaller that removes the runtime, both apps, and their data.
  • Linux tarball installer. An unprivileged install.sh that installs to your home directory, registers systemd user services, adds app-launcher entries and icons, starts the agent and companion, and launches the Setup Assistant. A matching uninstall.sh reverses all of it.
  • Version reporting from first contact. Registration now includes the installed agent version, so a new device no longer shows "Version unknown" in Control between registering and its first heartbeat.
  • Local health API extensions. /healthz now carries in-flight deployment progress alongside the models list and transport state, and the local API gains per-model serve, stop, and cancel-deploy actions (used by the companion).
  • Release-channel label. Pre-release builds show their channel next to the version, for example 1.0.19 · Alpha, in the Setup Assistant, the companion tray, and Preferences. Production builds hide the label.

Changed

  • Local health API port. The agent-state endpoints (/healthz, GET /models) now listen on 127.0.0.1:20505 instead of 8101. Integrators probing the old port should update; see Your Own Software for the current surface.
  • Stale queued deployments expire. A deployment that sits in "Queued" for more than 5 minutes (for example when the deploy command never reached the device) is automatically marked failed instead of spinning forever. Cancel and redeploy to retry.
  • Safer uninstall scripts. The Linux uninstaller refuses to run against a folder that is not a real Locai install root, and the macOS uninstaller now matches only Locai's own apps when stopping processes.
  • More reliable model downloads. Cancelling now interrupts the download connection immediately rather than waiting for a timeout, truncated downloads are detected and reported as failed instead of leaving a short file behind, and late failures during install always report a final status.
  • Release artifacts. macOS releases now publish the .pkg (with its checksum) as the install path, replacing the runtime-only archive. Linux releases now contain the full installer layout (runtime, both apps, services, and install scripts). Windows remains a runtime archive.

Fixed

  • Faster, smoother Preferences. The Preferences window no longer blocks on slow status checks, opens with a single local round trip, and refreshes every 2 seconds.
  • Setup Assistant stays responsive. Signing in and waiting for the agent to come online no longer freeze the setup window.

1.0.18 · 2026-07-07

Groundwork for the graphical install path: the first beta of the menu-bar companion, and parallel, cancellable model deployments in the runtime.

Added

  • Menu-bar companion (beta). A tray-only app for macOS that reflects whether the agent is up, lists models with per-model serve and stop toggles, and can start at login.
  • Parallel model deployments. Deploying a model no longer blocks other commands; each deployment runs on its own worker, so several models can download at once.
  • Cancellable deployments. A new cancel command lets an in-flight model download stop cleanly, with progress and cancellation reflected in telemetry from start to finish.

Removed

  • The tui subcommand and --tui setup flag. The text-based UI was a developer utility, was never distributed as a user feature, and has been removed. The graphical Setup Assistant and menu-bar companion supersede it.

1.0.17 · 2026-07-01

Bundled over-the-air updates land end-to-end, GET /models gives integrators per-pipeline serving state, and a launcher-based bootstrap makes first-launch installs work without pre-baking a bundle into your host installer.

Added

  • GET /models on port 8101. Returns per-pipeline serving state (pipeline id, alias, port, host, and is_serving), with one entry per configured servable-model pipeline. The response is snapshotted lazily per request, so it is always current. See Your Own Software for the recommended probe pattern.
  • Bundled OTA update path, functional end-to-end. Frozen .pkg and .msi deployments now self-update from GitHub Releases. Downloads are SHA256-verified against a sidecar file before anything is written to disk, two versions are kept on disk at a time (current and previous), and a small launcher binary auto-rolls back if a freshly installed version exits non-zero within 120 seconds of the swap. A pre-swap self-check boots config, transport, and plugins before committing, discarding the new version if it fails. Source (git) installs are unaffected and still update via git pull.
  • First-launch bootstrap from a launcher and boot.json. Partner installers no longer need to ship a pre-baked agent bundle. The launcher reads boot.json, fetches the latest matching release, verifies it, and starts the agent, so users on first run always get the current release regardless of how old the host installer is. Hosts that prefer to pre-seed a bundle can still do so.
  • Launcher configurability for GUI installers. The service manager gains scope and label_prefix options so a .pkg installer can register a system-wide service under uk.co.locai.link.* labels without disturbing existing developer flows.

Changed

  • Browser handoff on device-flow sign-in. When onboarding runs detached from a terminal (for example a .pkg postinstall or a service-mode boot), the sign-in verification URL now opens in the system browser automatically. The terminal banner stays for shell invocations, so there is no change if you are at a prompt.

Deprecated

  • /healthz.currently_serving and /healthz.model_id. Both fields stay in the response for backwards compatibility, but they only reflect pipelines started via an explicit START_SERVING command and cannot represent multi-model serving. New integrations should read GET /models. Integrations that only need "is any model serving?" can migrate to models.some(m => m.is_serving) without an API change.

1.0.16 · 2026-06-25

Restores model-state reporting after an unclean restart, tightens input validation and the request-handling security perimeter, makes orphaned-process cleanup safer, and updates the bundled model server.

Added

  • Status reporting after auto-resume. When a device restarts and automatically resumes a model that was serving (or running inference), it now re-announces that state to Control. Previously the dashboard could stay stuck on "not serving" until you issued a fresh command, even though the model was already back up.

Changed

  • Updated model server. The bundled llama.cpp build moved from b9222 to b9789, with the Windows CUDA prebuilt following the upstream cuda-13.3 rename. macOS and Linux download patterns are unchanged.

Fixed

  • Safer model removal and deployment. Model file names are now validated before they are used on disk, closing a path-traversal vector from malformed input. Deploy and update commands also reject payloads whose pipeline identifier does not match their configuration, preventing state from being saved under the wrong key.
  • Tighter telemetry hygiene. Rejected command details are no longer echoed into telemetry. A generic "Invalid command payload" is reported instead, and the full diagnostic stays in local logs only. Incoming request origins are also stripped of stray control characters at the boundary.
  • Safer orphan cleanup. After an unclean shutdown, Link now reclaims only its own previous model-server process and refuses to touch an unrelated process that happens to have reused the same process ID or port. A serving health check now correctly reports unhealthy when no server process is running.
  • More robust serving. The serving proxy now accepts both common streaming event formats, fails fast and loudly when its public port is already in use instead of appearing to start silently, and no longer drops a command from its duplicate-protection window when its handler fails, so legitimate retries can proceed.
  • More reproducible builds. Bundle manifests are now written in a stable, canonical order, so two builds of the same set of components produce identical output.

1.0.15 · 2026-06-18

Moves inference observability onto HTTP-response interception, lands the bundling subsystem (signed and notarised macOS builds with drag-to-extract packaging), hardens orphaned-process cleanup, and ships a typed command contract.

Added

  • Inference telemetry through a serving proxy. A reverse proxy now always sits in front of the model server, capturing per-inference token usage directly from chat completion responses (both streaming and non-streaming). This replaces the older log-parsing approach and reports usage reliably in production serving.
  • Multi-model attribution. When several models are served on the same port, each model now gets its own telemetry stream, so inferences are attributed to the correct model.
  • Bundling subsystem. A new build pipeline produces installable bundles per platform (Linux, Windows, macOS). On macOS, every binary is code-signed with a Developer ID, runs under a hardened runtime, is notarised by Apple, and ships as a drag-to-extract .dmg. Windows bundles are prepared but not yet signed.
  • Consolidated release workflow. Building, signing, notarising, and uploading release artifacts now happen in a single workflow run.
  • Typed command contract. New UNINSTALL_MODEL and UPDATE_PIPELINE commands, with golden-fixture round-trip tests covering every command type to keep the device and backend in sync.

Changed

  • The CORS shim is now a full serving proxy. CORS headers and inference telemetry are two independently configurable features of the same proxy, which now always fronts the model server. The model server listens on a loopback-only internal port while the proxy owns the public port.
  • Stricter command validation. Every incoming command is validated against the typed command contract before it runs. Commands that fail validation are reported as failed rather than being silently dropped.
  • Backend-defined pipelines. Devices now apply ready-made pipeline definitions sent by the backend instead of deriving them locally.
  • Quieter plugin installs. Plugin install scripts return early without log noise when a binary is already installed at the pinned version.

Removed

  • The legacy REMOVE_MODEL command alias. Use UNINSTALL_MODEL instead.
  • The old per-partner bundle "profiles". Bundles are now described by an explicit plugin list.
  • The separate release token requirement. The consolidated release workflow uses the default CI token.

Fixed

  • Orphaned model-server processes surviving an unclean shutdown. Link now reliably reclaims only its own previous instance on the next start, and refuses to touch unrelated processes that may have reused the port.
  • Inference telemetry going dark in production serving. Telemetry is now captured through the serving proxy end to end, closing a gap where no telemetry was emitted when serving via the swap manager.
  • Chats being attributed to the wrong model when two models shared a server on the same port.

1.0.9 · 2026-05-08

Adds deployment-progress reporting, broadens transport and installer options, and improves cross-platform temperature monitoring.

Added

  • Incremental deployment progress. Model deployments now report downloading, configuring, and completed events with byte counts, throttled to 5% steps.
  • More transport options. The Zenoh client now accepts TLS root CA, username, and password settings; tls_root_ca: "auto" resolves to the bundled certificate store at runtime, so no certificate file needs to live on disk.
  • Consistent installer flags. The Linux/macOS and Windows CMD installers now honor --branch and --repo-url arguments, matching the PowerShell installer. Environment variables are still respected, with CLI flags taking precedence.
  • Broader temperature monitoring. Windows now reads CPU temperature without administrator rights via a performance counter (with an admin-only fallback), and macOS can optionally report temperature via the osx-cpu-temp Homebrew binary.
  • End-to-end installer tests across bash, PowerShell, and CMD.

Changed

  • TLS is now applied based on the endpoint scheme rather than the connection mode, so peer-of-router setups also verify outbound TLS.
  • In pure client mode, the agent no longer provisions a local router binary it does not need.
  • Plugin install scripts return early and silently when a binary is already at the pinned version, removing repeated banner logs on every start.
  • The PowerShell installer now translates its idiomatic parameters into the underlying command arguments and propagates the real exit code, so installer crashes no longer report success.

Removed

  • The Windows-only wmi dependency, whose fragile install and noisy teardown messages have been replaced by a direct PowerShell shell-out.

Fixed

  • A Windows CMD installer bug where argument shifting inside a conditional block caused the installer to invoke Python incorrectly, fail silently, and still report success.

Architecture

A modular, pipeline-based runtime. The control plane (lifecycle, configuration) is separated from the data plane (inference, telemetry) for performance and resilience.

Changed

  • Single-process model: The previous two-process split (manager.py supervisor + agent.py worker) has been consolidated into a single main.py. OTA updates now use os.execv() to replace the process in place rather than relying on a parent supervisor loop — the PID is preserved across updates.

  • Pipeline-based runtime: The monolithic agent.py (1,496 lines), which handled command polling, inference dispatch, serving, and metrics, has been replaced by AgentRuntime + Pipeline threads. Each pipeline is a Source → Sink pair running on its own thread, composable from config.

  • Component registry: Components (HTTP sources, Zenoh sinks, system monitors, command handlers) self-register via a @ComponentRegistry.register("name") decorator and are instantiated from declarative config.

  • Pydantic config models: Ad-hoc JSON config handling has been replaced with typed AgentConfig, PipelineConfig, TransportConfig, and GenericConfig Pydantic models. Schema version is pinned at 2.1.

  • Session state persistence: A new StateManager writes timestamped configs/session_*.json files for crash recovery. The agent automatically resumes the latest session on restart, and any running pipelines are re-started accordingly.

Removed

  • manager.py (1,080 lines) subcommands have been folded into main.py.
  • src/link/serving/ LLMServer, WhisperServer, and BaseServer moved into plugins.
  • src/link/inference/ dispatcher.py and TFLite runners (language_model_gguf.py, image_detection_cpy_tflite.py, audio_classification_yamnet_tflite.py) replaced by plugin adapters.
  • src/link/logger/ custom module replaced by src/link/utils/logger.py with structured async handlers.
  • src/link/analytics.py analytics now flow through the generic reporting handler system.
  • src/link/components/buffers.py unused LocalBuffer stub removed.

Registration & Onboarding

Registration & Onboarding now supports flexible identity resolution and simplified device activation using keys. Authentication is updated to JWT-based login with email/token, and passwords are handled securely via prompt input.

Added

  • Four-tier identity resolution in main.py run explicit --config, auto-resume of the latest session, just-in-time onboarding with --registration-key, or factory defaults.
  • activate_device() for re-activating existing devices using only --device-id and --registration-key.

Changed

  • The register_device() function previously accepted --username and sent it in the request body. It now accepts --email (or a pre-obtained --token) and uses login_and_get_token() to obtain a JWT, which is sent as Authorization: Bearer on the /devices/register-with-key request matching the backend's expected auth flow.
  • Passwords are prompted securely via getpass when omitted from the command line.

Installation

Installation is streamlined with one-liner scripts for Linux, macOS, and Windows, enabling quick setup across platforms. The new main.py install command automates the full flow clone, setup, register, and run in a single step. Setup is now managed via main.py setup, replacing the old manager-based approach.

Added

  • One-liner install scripts for Linux/macOS (install.sh), Windows PowerShell (install.ps1), and Windows CMD (install.cmd). Each script bootstraps uv, detects local vs. remote main.py, and hands off to the new install subcommand.
  • Main.py install subcommand orchestrates the full flow clone/update repo → setup → register → run, in a single command.

Changed

  • Setup is now handled via main.py setup (with --dev and --tui extras) instead of manager.py setup --extras.

Plugins

The system now uses a plugin-based architecture, where each capability (LLM, audio, vision) is packaged as an independent, installable module. Core plugins like language_model, audio_transcriber, and classifiers enable flexible AI workloads with support for CUDA, ARM64, and optimized builds.

Enhancements like caching, improved loading, and smarter configuration make plugins more efficient, scalable, and easier to manage.

Added

  • Plugin architecture: Plugins are standalone installable packages that register via the locai.plugins entry point. Each plugin has its own pyproject.toml, adapter.py, and install.py.
  • language_model: Plugin ports the LLM server logic (llama-server lifecycle). Pinned to llama.cpp b8808.
  • audio_transcriber: Plugin ports the Whisper server logic (whisper-server lifecycle). Pinned to whisper.cpp v1.8.4.
  • image_classifier: Plugin-vision inference via TFLite.
  • audio_classifier: Plugin-audio tagging via TFLite.
  • CUDA build-from-source fallback: On Linux when the CUDA toolkit (nvcc) is detected — enables -DGGML_CUDA=ON for optimal GPU performance.
  • Tag-based caching: Each plugin install uses a tag file to skip re-download/rebuild when already at the pinned version. Re-running install.py after an OTA update is lightweight.
  • ARM64 Linux support: For llama.cpp prebuilts (the old code was x64-only).
  • macOS quarantine stripping: xattr -dr com.apple.quarantine is applied after extraction to prevent Gatekeeper from blocking binaries.
  • Symlink preservation: When extracting tarballs versioned shared library names (e.g., libmtmd.0.dylib) now resolve correctly.

Changed

  • Removed hardcoded --chat-template chatml the language_model plugin now only passes --chat-template when explicitly configured, allowing llama.cpp to auto-detect from the model's metadata.
  • Health-check timeout raised from 30s to 120s for plugin servers — large models on CPU can take longer to load.
  • LD_LIBRARY_PATH now walks subdirectories to locate ggml*.so / whisper*.so files, as CUDA shared libs may reside in nested folders.

Configuration

Configuration is now fully declarative and flexible, supporting pipelines defined via config with dynamic source sink mapping. It introduces Zenoh as an alternative transport alongside HTTP, enabling more scalable communication. Logging and reporting are config-driven with template-based customization, allowing reusable and device-specific configurations.

Added

  • Zenoh transport as an alternative to HTTP for the control plane and pipelines. Set TransportConfig.type = "zenoh" or "http".
  • Declarative pipelines in config pipelines: [{id, source, sink, active}]. Sources and sinks are instantiated by name from the component registry.
  • Config-driven logging and reporting handlers LoggingConfig.handlers and ReportingConfig.handlers accept a list of typed handler configs (console, http, zenoh).
  • Template substitution in handler args ${identity.device_id}, {cid}, and {mid} are resolved at runtime, allowing a single config to serve multiple devices.

Pipelines

Pipelines now treat empty inputs as successful no-ops, preventing false failure warnings during idle polling. This improves stability by ensuring the pipeline loop handles inactive states correctly without unnecessary alerts.

Changed

  • AgentCommand sink now returns True on empty input (previously returned None). The pipeline loop treats non-truthy sink results as failures and warns accordingly. Idle poll ticks (http_poll returning [] when no commands are pending) previously triggered a spurious "Sink is returning False" warning. An empty dispatch is now treated as a successful no-op.

Transport, Logging & Reporting

Transport, Logging & Reporting now use a structured, async system with LinkReporter and non-blocking handlers for HTTP and Zenoh. Error handling is improved with retry logic, clear classification (retryable vs fatal), and standardized log formats, ensuring reliable and scalable observability.

Added

  • LinkReporter a custom logger class exposing report_lifecycle(status), report_command(cmd_id, status, output), and report_model(...) for structured status reporting.

  • AsyncHandler base class with a worker thread and queue — all handlers are non-blocking. Subclasses include:

    • AsyncHTTPHandler routes events to HTTP endpoints via template lookup (PUT for lifecycle_status, POST for everything else).
    • AsyncZenohHandler publishes to Zenoh topics.
  • HttpError exception class with status, reason, and retryable fields allows callers to distinguish transient failures (timeout, 5xx, connection refused) from non-retryable ones (401, 403, 404). The previous HttpClient silently swallowed all errors as None/False.

  • agent_version in lifecycle status payload report_lifecycle() now includes the agent's installed version (read from importlib.metadata.version("locai-link")), satisfying the backend's AgentStatusUpdate.agent_version semver requirement.

Changed

  • HttpClient.get() / post() now classifies errors: timeouts, 5xx responses, and connection errors return None/False (retryable); 4xx auth/client errors raise HttpError. HttpPoller and HttpPublisher catch HttpError and log with actionable context before re-raising.

  • HTTP log payload shape now matches the backend's LogCreate schema: {message, severity, category}. Severity is lowercase (DEBUG maps to "info"); category defaults to "other" and can be set via logger.info("msg", extra={"category": "security"}). This replaces the previous {timestamp, level, message, logger} shape.

  • AsyncHTTPHandler now retries on timeouts, connection errors, and 5xx responses with exponential backoff (0.5s, 1.5s, capped at 2 retries). 4xx responses remain fatal and are not retried. Timeout is configurable per handler via args.timeout (default 10s), split into (connect=3s, read=timeout) fast-fail on unreachable hosts, tolerant on slow responses.

  • HttpClient.get() timeout demoted from WARNING to DEBUG polling is self-healing (the next tick retries), and a flaky network was generating excessive console noise at warning level.

Service Deployment

Service Deployment now includes a cross-platform service manager, enabling the agent to run as a native service on Linux, macOS, and Windows. It supports production mode execution and graceful shutdown, simplifying deployment and lifecycle management.

Added

  • Cross-platform service manager (src/link/infra/service.py) — the ServiceManager factory selects the appropriate backend:
    • Linux → systemd user service at ~/.config/systemd/user/locai-link.service
    • macOS → LaunchAgent plist at ~/Library/LaunchAgents/io.locai.locai-link.plist
    • Windows → Windows Service via sc.exe (requires admin privileges)
  • main.py run --prod installs and starts the agent as an OS service.
  • main.py stop gracefully stops the agent (and zenohd, if installed).

OTA Updates

OTA Updates now support seamless, zero-downtime upgrades using in-place restarts via os.execv(), preserving the running process. Updates are branch-aware, stash-safe, and intelligently refresh only the plugins actually in use.

The new updater system ensures clean pipeline shutdown, automated installs, and efficient version management without needing an external supervisor.

Added

  • UPDATE_AGENT command handled by AgentRuntime: On receipt reports completion, cleanly shuts down pipelines, and signals main.py to update.
  • src/link/app/updater.py: Provides pull_and_update() (git fetch/stash/pull/pop + uv pip install -e .), reinstall_plugin_binaries() (config-driven; see Changed below), get_current_branch(), and get_local_version().
  • In-place restart via os.execv(): The process image is replaced while preserving the PID. systemd/launchd see a continuously running process with no downtime gap.
  • Branch-aware updates: Dev branches pull from origin/<current-branch>, not origin/main.
  • Stash-safe updates: Dirty working trees are stashed and reapplied around the pull.
  • -DGGML_NATIVE=OFF on macOS: For both llama.cpp and whisper.cpp builds avoids ggml's -mcpu=native fallback, which AppleClang rejects on arm64. Metal + Accelerate handle performance-critical paths on Apple Silicon with no throughput regression. Linux and Windows are unchanged.
  • Silenced detached-HEAD git advisory: Tagged clones now pass -c advice.detachedHead=false inline to suppress cosmetic noise from OTA build logs.

Changed

  • Reinstall_plugin_binaries() is now config-driven. Previously, every plugin under plugins/ had its install.py re-run on every OTA — so a device running only language_model would still attempt to build whisper.cpp, TFLite, etc. The updater now walks the active
  • AgentConfig.pipelines[*].source/sink.type, maps each type to its owning plugin via that plugin's [project.entry-points."locai.plugins"] in pyproject.toml, and only refreshes plugins that are actually referenced. Unused plugins are silently skipped.

Removed

  • The old EXIT_CODE_UPDATE = 42 + subprocess-loop supervisor in manager.py. The new architecture requires no external supervisor.

Testing & CI

Testing & CI now includes 77+ unit tests and full integration tests across plugins, ensuring reliability of core features and model workflows. It also introduces a multi-OS CI pipeline with version checks, improving code quality, consistency, and release control.

Added

  • 77 unit tests: Covering HTTP client error classification, onboarding auth flow, state manager version handling, OTA updater logic, runtime command handling, service manager across all three oses, zenoh router, config loading, and platform detection.
  • ci pytest marker:For tests requiring external binaries or network access skipped locally by default, enabled in CI via -m "" override.
  • Integration tests:In each plugin directory download real models, spawn real server binaries, and verify full transcription/completion flows.
  • Multi-OS CI matrix: (Ubuntu, macOS, Windows) for both unit and integration jobs.
  • Version-bump gate on PRs: Fails if the pyproject.toml version has not been incremented.
  • audio_transcriber wired into the integration-test job alongside the other three plugins.

CLI Reference

The CLI has been simplified by replacing manager.py with a unified main.py, consolidating all commands into a single entry point. It now supports setup, install, run, stop, and plugin management, making the workflow cleaner and easier to use.

Before (old manager.py)

manager.py install        # Full installation wizard
manager.py setup # Configure venv and deps
manager.py reset # Clean up artifacts
manager.py register # Register device
manager.py activate # Activate a pre-registered device
manager.py update # Pull latest code
manager.py run # Run agent (supervisor loop)
manager.py install-deps # Install llama/whisper server binaries

After (new main.py)

main.py setup             # Install Python dependencies (--dev, --tui)
main.py install # Full installation wizard
main.py run # Run agent (in-process, handles OTA via execv)
main.py stop # Stop all services
main.py reset # Clean up environment (--hard)
main.py install-plugin # Install a plugin by name
main.py tui # Launch text UI (optional)

Breaking Changes

Breaking Changes introduce updates like --email replacing --username, a new config schema (v2.1), and removal of manager.py in favor of main.py. Plugins must now be installed separately, and logging/reporting formats have been standardized through the new LinkReporter system.

info

Review these carefully before migrating.

  • Registration argument is --email (not --username).
  • Config schema version is 2.1. Earlier state files are rejected and will not be loaded.
  • manager.py no longer exists; all commands must be run via main.py.
  • Plugins must be installed separately as editable packages (uv pip install -e "plugins/<name>"). They are not bundled with the core agent.
  • Agent status, command status, and model status payloads now flow through the new LinkReporter handler system direct requests.post calls to /agent/{device_id}/status have been removed.
  • HTTP log payload shape changed from {timestamp, level, message, logger} to {message, severity, category} to match the backend's LogCreate schema. Backends consuming /logs must accept the new shape; the old field names are no longer emitted.

You can refer to the locai-link Github repositary.