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 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
.pkginstaller. 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.shthat 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 matchinguninstall.shreverses 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.
/healthznow 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 on127.0.0.1:20505instead of8101. 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
tuisubcommand and--tuisetup 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 /modelson port8101. Returns per-pipeline serving state (pipeline id, alias, port, host, andis_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
.pkgand.msideployments 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 (currentandprevious), 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 viagit pull. - First-launch bootstrap from a launcher and
boot.json. Partner installers no longer need to ship a pre-baked agent bundle. The launcher readsboot.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
scopeandlabel_prefixoptions so a.pkginstaller can register a system-wide service underuk.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
.pkgpostinstall 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_servingand/healthz.model_id. Both fields stay in the response for backwards compatibility, but they only reflect pipelines started via an explicitSTART_SERVINGcommand and cannot represent multi-model serving. New integrations should readGET /models. Integrations that only need "is any model serving?" can migrate tomodels.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.cppbuild moved fromb9222tob9789, with the Windows CUDA prebuilt following the upstreamcuda-13.3rename. 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_MODELandUPDATE_PIPELINEcommands, 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
failedrather 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_MODELcommand alias. UseUNINSTALL_MODELinstead. - 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, andcompletedevents 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
--branchand--repo-urlarguments, 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-tempHomebrew 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
wmidependency, 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.pysupervisor +agent.pyworker) has been consolidated into a singlemain.py. OTA updates now useos.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 byAgentRuntime+ Pipeline threads. Each pipeline is aSource → Sinkpair 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, andGenericConfigPydantic models. Schema version is pinned at2.1. -
Session state persistence: A new
StateManagerwrites timestampedconfigs/session_*.jsonfiles 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 intomain.py.src/link/serving/LLMServer,WhisperServer, andBaseServermoved into plugins.src/link/inference/dispatcher.pyand 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 bysrc/link/utils/logger.pywith structured async handlers.src/link/analytics.pyanalytics now flow through the generic reporting handler system.src/link/components/buffers.pyunusedLocalBufferstub 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 runexplicit--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-idand--registration-key.
Changed
- The
register_device()function previously accepted--usernameand sent it in the request body. It now accepts--email(or a pre-obtained--token) and useslogin_and_get_token()to obtain a JWT, which is sent asAuthorization: Beareron the/devices/register-with-keyrequest matching the backend's expected auth flow. - Passwords are prompted securely via
getpasswhen 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 bootstrapsuv, detects local vs. remotemain.py, and hands off to the newinstallsubcommand. Main.py installsubcommand orchestrates the full flow clone/update repo → setup → register → run, in a single command.
Changed
- Setup is now handled via
main.py setup(with--devand--tuiextras) instead ofmanager.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.pluginsentry point. Each plugin has its ownpyproject.toml,adapter.py, andinstall.py. language_model: Plugin ports the LLM server logic (llama-serverlifecycle). Pinned tollama.cpp b8808.audio_transcriber: Plugin ports the Whisper server logic (whisper-serverlifecycle). Pinned towhisper.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=ONfor 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.pyafter an OTA update is lightweight. - ARM64 Linux support: For
llama.cppprebuilts (the old code was x64-only). - macOS quarantine stripping:
xattr -dr com.apple.quarantineis 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 chatmlthelanguage_modelplugin now only passes--chat-templatewhen explicitly configured, allowingllama.cppto auto-detect from the model's metadata. - Health-check timeout raised from
30sto120sfor plugin servers — large models on CPU can take longer to load. LD_LIBRARY_PATHnow walks subdirectories to locateggml*.so/whisper*.sofiles, 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.handlersandReportingConfig.handlersaccept 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
AgentCommandsink now returnsTrueon empty input (previously returnedNone). The pipeline loop treats non-truthy sink results as failures and warns accordingly. Idle poll ticks (http_pollreturning[]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
-
LinkReportera custom logger class exposingreport_lifecycle(status),report_command(cmd_id, status, output), andreport_model(...)for structured status reporting. -
AsyncHandlerbase class with a worker thread and queue — all handlers are non-blocking. Subclasses include:AsyncHTTPHandlerroutes events to HTTP endpoints via template lookup (PUTforlifecycle_status,POSTfor everything else).AsyncZenohHandlerpublishes to Zenoh topics.
-
HttpErrorexception class withstatus,reason, andretryablefields allows callers to distinguish transient failures (timeout, 5xx, connection refused) from non-retryable ones (401, 403, 404). The previousHttpClientsilently swallowed all errors asNone/False. -
agent_versionin lifecycle status payloadreport_lifecycle()now includes the agent's installed version (read fromimportlib.metadata.version("locai-link")), satisfying the backend'sAgentStatusUpdate.agent_versionsemver requirement.
Changed
-
HttpClient.get()/post()now classifies errors: timeouts, 5xx responses, and connection errors returnNone/False(retryable); 4xx auth/client errors raiseHttpError.HttpPollerandHttpPublishercatchHttpErrorand log with actionable context before re-raising. -
HTTP log payload shape now matches the backend's
LogCreateschema:{message, severity, category}. Severity is lowercase (DEBUGmaps to"info"); category defaults to"other"and can be set vialogger.info("msg", extra={"category": "security"}). This replaces the previous{timestamp, level, message, logger}shape. -
AsyncHTTPHandlernow 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 viaargs.timeout(default10s), split into(connect=3s, read=timeout)fast-fail on unreachable hosts, tolerant on slow responses. -
HttpClient.get()timeout demoted fromWARNINGtoDEBUGpolling 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) — theServiceManagerfactory 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)
- Linux → systemd user service at
main.py run --prodinstalls and starts the agent as an OS service.main.py stopgracefully stops the agent (andzenohd, 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_AGENTcommand handled byAgentRuntime: On receipt reports completion, cleanly shuts down pipelines, and signalsmain.pyto update.src/link/app/updater.py: Providespull_and_update()(git fetch/stash/pull/pop+uv pip install -e .),reinstall_plugin_binaries()(config-driven; see Changed below),get_current_branch(), andget_local_version().- In-place restart via
os.execv(): The process image is replaced while preserving the PID.systemd/launchdsee a continuously running process with no downtime gap. - Branch-aware updates: Dev branches pull from
origin/<current-branch>, notorigin/main. - Stash-safe updates: Dirty working trees are stashed and reapplied around the pull.
-DGGML_NATIVE=OFFon macOS: For bothllama.cppandwhisper.cppbuilds avoidsggml's-mcpu=nativefallback, 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=falseinline to suppress cosmetic noise from OTA build logs.
Changed
Reinstall_plugin_binaries()is now config-driven. Previously, every plugin underplugins/had itsinstall.pyre-run on every OTA — so a device running onlylanguage_modelwould still attempt to buildwhisper.cpp, TFLite, etc. The updater now walks the activeAgentConfig.pipelines[*].source/sink.type, maps each type to its owning plugin via that plugin's[project.entry-points."locai.plugins"]inpyproject.toml, and only refreshes plugins that are actually referenced. Unused plugins are silently skipped.
Removed
- The old
EXIT_CODE_UPDATE = 42+ subprocess-loop supervisor inmanager.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.
cipytest 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.tomlversion has not been incremented. audio_transcriberwired 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.
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.pyno longer exists; all commands must be run viamain.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
LinkReporterhandler system directrequests.postcalls to/agent/{device_id}/statushave been removed. - HTTP log payload shape changed from
{timestamp, level, message, logger}to{message, severity, category}to match the backend'sLogCreateschema. Backends consuming/logsmust accept the new shape; the old field names are no longer emitted.
You can refer to the locai-link Github repositary.