fix(hub): platform-aware artifact selection for binary hub agents#2086
Conversation
…alls (#2084) The hub installer only reads the manifest's legacy singular artifact field (always the first-published macOS binary), so Windows/Linux hosts get fed a macOS executable into uv pip install. These tests pin the fix: platform-aware selection from versions[v].artifacts[], no wheel fallback for binary agents, and health/rollback/uninstall/router handling for the new binary artifact kind. All new tests fail against current code (missing install(platform_key=...), compatibility.current_platform_key(), and the sentinel artifact_kind field), confirming they exercise the not-yet-implemented fix rather than passing vacuously.
The installer only read the manifest's legacy singular artifact field, which is always the first-published macOS binary — so Windows and Linux hosts got fed a macOS executable into uv pip install and the install failed every time (#2084). installer.py now selects the matching entry from versions[v].artifacts[] by platform key (win32-x64/darwin-arm64/darwin-x64/linux-x64), writes it as a binary rather than routing it through pip, and never falls back to a wheel even if one happens to be present. compatibility.py gains current_platform_key() to resolve the host's key, mirroring the email sidecar's own mapping so both agree on the platform namespace.
…ts[] cases (#2084) The hub worker writes artifacts: [artifact] for every publish, so a modern wheel-only agent carries a single-element .whl artifacts[]. Presence of artifacts[] must not imply a binary install: a wheel/sdist-only list routes through pip regardless of platform_key, and a cpp manifest (classified first) extracts its singular zip exactly like the legacy route. Both new tests are red against the current increment, which over-eagerly treats any non-empty artifacts[] as platform-keyed binary selection.
The hub worker writes artifacts: [artifact] on every publish, so a modern wheel-only agent carries an artifacts[] list too — treating the list's mere presence as "binary agent" would loudly fail every wheel install the current worker publishes. Classify the set instead: cpp first (byte-identical legacy route), then platform-match among the binary-like entries only, and a wheel/sdist-only list takes the pip route regardless of platform. Also generalizes the .exe suffix match to any platform key (win32-arm64 binaries are planned, #1898) and names the version in the no-match error.
… mutating its dir A correct binary install of the email agent would read "broken" forever: health/status demanded a gaia.agent entry point, which binary agents don't ship by design. health_check now probes binary-kind installs by executable presence (+x on POSIX) via the executable name the sentinel records, leaving wheel agents' entry-point probe untouched. The email agent's install dir doubles as the sidecar's own binary cache, and the warm manager holds the exe open — so install/uninstall/ rollback of email now shut a running sidecar down first (router-level hook; gaia.hub still never imports gaia.ui). Absent or stopped manager is a no-op.
|
Verdict: Approve — clean, well-tested, scope-tight. This fixes the Email Triage agent failing to install from the hub on every platform: the installer read only the manifest's legacy singular 🔍 Technical detailsStrengths
Minor observations (non-blocking, no change requested)
No doc-sync obligation here: this is core installer internals, not a hub-agent package doc surface, and no |
|
Linux evidence added — the fix is now verified on all three OS targets, plus the earlier Windows CI failure is resolved. Ubuntu 24.04 (linux-x64), cold state — fresh POST /api/agents/install {"id":"email"} → {"id":"email","status":"queued"}
install-status → {"id":"email","status":"completed","phase":"completed","percent":100,"version":"0.4.0","error":null}Ran in a container because both physical Linux test boxes were unavailable at the time (one reserved by a long eval run, the other is the dual-boot machine currently booted into Windows); the install path is hardware-independent and the installed binary executes. Per-OS summary: Windows 11 (physical, Radeon — the reporter's environment class): before→after + locked-file + update-in-place, SHA-verified ✅ · macOS (darwin-arm64, physical): SHA + exec bit + health ✅ · Ubuntu 24.04 (linux-x64, container): SHA + exec bit + health ✅. CI: the "Test GAIA CLI on Windows (Full Integration)" failure was a runner loss (job killed at 11m mid-step, log blob never uploaded; workflow green on the last 5 main commits) — rerun passed, all checks green. |
…holder lock (amd#2100) Installing the email agent from the Agent Hub currently produces a binary the UI refuses to run: amd#2086 puts a checksum-verified `email-agent` exactly where `EmailSidecarManager` looks, but the user-mode spawn path gates on `binaries.lock.json`'s placeholder SHAs *before* checking what's installed, so the first email chat dies with a "placeholder sha256" error. Now `fetch_binary` short-circuits to the Hub-installed binary first — re-verified against the SHA-256 its install sentinel recorded from the hub manifest — so Hub install → email chat just works in user mode, no env overrides. With no installed binary, the placeholder lock still fails loudly naming the lock, and a sentinel/file mismatch raises `IntegrityError` with a reinstall remedy instead of silently falling back. Fixes amd#2095 ## Test plan - [ ] `python -m pytest tests/unit/test_email_sidecar_fetch.py tests/unit/test_email_sidecar_manager.py tests/unit/test_hub_installer.py -q` — includes new tests pinning the ordering (installed-binary short-circuit before the lock is consulted), the loud placeholder failure without an install, the tampered-install `IntegrityError`, and the manager-level spawn with the placeholder lock - [ ] `python util/lint.py --all` - [ ] Manual golden path: fresh `~/.gaia/agents/email`, install the email agent from the Agent Hub panel, open an email chat — the sidecar spawns the installed binary (log: `using hub-installed verified binary`)
Closes #2084
Installing the Email Triage agent from the Agent UI hub failed on every platform: the hub manifest's legacy singular
artifactfield always points at the first-published platform binary (email-agent-darwin-arm64), the installer read only that field, andlanguage: pythonrouted the macOS executable intouv pip install— the exit-2 error in #2084. The installer now selects the right entry fromversions[v].artifacts[]for the host platform and installs a binary as a binary (checksum-verified, atomic,chmod +x), so the only agent in the live hub catalog actually installs — verified end-to-end on a Windows 11 + Radeon machine (the reporter's environment class) and on macOS.Also in the change, because the email install dir doubles as the running sidecar's own binary cache: the UI shuts a running sidecar down before install/uninstall/rollback, a Windows locked-file failure becomes an actionable error instead of a stack trace, updates replace the binary in place (no dir-level backup that would yank a live process's directory), and health/status report a correct binary install as healthy instead of demanding the
gaia.agententry point binaries don't ship. Wheel- and C++-agent behavior is byte-identical (regression-tested). Deferred follow-ups (binary-agent trust gate, wheel-publishing hazards, small install hardening) are tracked in #2085 with the full analysis.Test plan
python -m pytest tests/unit/test_hub_installer.py tests/unit/test_hub_router.py tests/unit/test_hub_lifecycle.py tests/unit/test_hub_compatibility.py -q→ 121 passed (includes the new T1–T14 contract: platform selection, wheel/cpp regression guards, loud no-match errors, sentinel back-compat, locked-file/update paths, sidecar-shutdown hook, kind-aware health, platform-key parity with the sidecar's own mapping)tests/unit/test_hub_catalog.py tests/unit/test_agent_hub_api.py→ 21 passed, 4 skippedpython util/lint.py --all→ ALL QUALITY CHECKS PASSEDEvidence (real hub, cold state —
~/.gaia/agents/emaildeleted before each phase)Before — main (
a02ae714), Windows 11:POST /api/agents/install {"id":"email"}→ install-status:{"id":"email","status":"failed","error":"'uv pip install' failed (exit 2): error: Failed to parse: `...\\gaia-hub-k1pdt_rr\\email-agent-darwin-arm64` ... Expected path to end in a supported file extension: `.whl`, ..."}After — this branch (
924c8449), same box, cold: same request →{"id":"email","status":"completed","phase":"completed","percent":100,"version":"0.4.0","error":null}email-agent.exeSHA-256 =206a9c12…93f8— exact match with the live manifest'swin32-x64artifact;GET /api/agents/email/health→{"state":"healthy"}.🔍 More evidence: locked-file error, update-in-place, macOS
Windows, install while the agent binary is running (holds the exe open) — actionable error instead of a stack trace, and no
.backup/dir is created:{"status":"failed","error":"Could not write email-agent.exe to C:\\Users\\...\\agents\\email: [WinError 5] Access is denied ... The agent appears to be running — close it and retry."}After closing the process, the same request completes and replaces the binary in place (SHA re-verified).
macOS (darwin-arm64), cold: same install →
completed,~/.gaia/agents/email/email-agentwritten with-rwx--x--x, SHA-256 =26bcf05b…bf(exact manifest match), healthhealthy,./email-agent --helpruns.Tested by driving the real
POST /api/agents/install/install-status/healthendpoints ofgaia.ui.serveragainst the live hub on both machines.