Discovery-side enablement for making the Assistant useful on CLOUD models.
Today, sensitive resources route to cloud as a terse redacted summary, so
the Assistant never receives discovery's access context and gives generic
answers — invisibly broken for the majority of users who run cloud AI.
FormatCloudSafeContext returns the operational context the Assistant needs
(service identity, access pattern, config/data/log paths, port numbers)
while omitting PII by construction (no hostname, IP, bind addresses). The
chat sanitizer (Codex's ai/chat + unifiedresources policy lane) can include
this in cloud-routed summaries behind an opt-in, instead of withholding
everything. Local routing keeps using FormatForAIContext (full context).
Tested: includes service/access/paths/ports; rejects hostname + IP.
Cached discoveries from before the surface/fast-path/nested fixes still
counted as 'fresh' by the time-based window, so the panel showed worse
pre-fix data (what 'surely this isnt done?' surfaced on esphome).
Add servicediscovery.DiscoveryEngineVersion (currently 1), stamped onto
every freshly built discovery (LXC/VM and Docker build sites). Unlike
CLIAccessVersion it is NOT auto-upgraded on read, so a missing/older value
reliably means the result predates the current engine. The per-guest panel
shows an amber 're-run for improved results' nudge when engine version is
below current (CURRENT_DISCOVERY_ENGINE_VERSION, kept in sync).
Contract-neutral re: unified-resources (DiscoveryTab is a consumer; this is
a UI nudge + discovery field, no consumption-contract delta) — landed via
PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT. Go package + version-stamp test pass;
type-check + eslint clean.
The per-guest Discovery panel dropped the raw cli_access string into a
copyable code row — but cli_access is guidance written for the Assistant
('Use pulse_control with target_host ...'), which is meaningless to a
person and reads as a fake command.
Derive a concrete, human-runnable command from the resource coordinates
(deriveCliCommand): 'pct exec 101 -- bash' for an LXC, 'docker exec <name>
bash' for Docker, and the layered 'pct exec 101 -- docker exec
homeassistant bash' for a service in a nested container. Show that
(copyable) with a 'run from the Proxmox/Docker host' hint. For types with
no clean human command (VM, k8s, agent) fall back to the cli_access text,
now clearly labelled as how the Assistant reaches it. The cli_access field
itself (Assistant context) is unchanged.
Unit-tested (deriveCliCommand); type-check + eslint clean.
The name fast-path identifies workloads named after their service, but a
workload with a generic name (ct101, db1) still fell through to the model —
which, with a slow reasoning model configured, times out rather than just
being slow. Many such workloads are still identifiable instantly by a
DISTINCTIVE listening port.
Add a Ports field to the identity table (only single-service ports: 8123
HA, 32400 Plex, 5432 postgres, 6379 redis, 1883/8883 mosquitto, 3306
mariadb, 8086 influxdb, 8096 jellyfin, 9090 prometheus, 6052 esphome) and
a second fast-path inferSurfaceIdentityFromPorts that runs after the name
path and before the model. Ambiguous ports (80, 443, 8080, 3000, 5000) are
deliberately excluded — a bad port guess is worse than asking the model.
Parser validated against real ss -tlnp output from LXC 101 (extracts 8123,
ignores ephemeral ports / PIDs / docker-proxy noise → home-assistant).
Unit tests cover distinctive-port matches and ambiguous-only non-matches.
The fast-path nailed identity but emitted the bare-guest access path, so
for a service running in Docker inside an LXC/VM (e.g. Home Assistant
Container) it told the Assistant to 'pct exec' into the guest shell —
wrong: the service is one layer deeper.
Access topology is index-level 'how to reach it' (only a probe can know a
service runs in a nested container), so re-add a LIGHT nested-container
probe (docker ps names+images, not the deep enumeration) to the LXC/VM
surface sets. When a nested container matches the identified service,
layer cli_access: '... docker exec <container> <command>'. Identity stays
instant from the name; the access path is corrected by one cheap command
when the agent is connected, and falls back to bare-guest guidance when not.
Verified live: HA LXC 101 (homeassistant container under Docker, alongside
watchtower) now yields cli_access including 'docker exec homeassistant',
in ~1s. Tests cover the match (HA by name, postgres by image), non-matches
(watchtower, unrelated service, no docker), and the cli_access layering.
Discovery should be instant for the obvious case: a workload named after
its service ('home-assistant', 'frigate', 'mqtt'…) is identified from the
name alone, with no model call and no command scan needed. This is the
'surface index' — identity + how-to-reach — with depth left to the
Assistant's own knowledge and on-demand commands.
Expand knownServiceIdentities from one entry (esphome) to the common
homelab set, and run inferSurfaceIdentity BEFORE the model: on a
name match, build the identity result and skip the (slow reasoning-model)
analysis entirely. Conservative — name signals only, never broad
command-output guesses — so the model is skipped only on an obvious match;
ambiguous workloads still fall through to full analysis.
Verified live on real infra: HA LXC now identifies in ~0s as Home
Assistant (0.9), no model call, even with the agent disconnected —
previously it timed out at 45s on the reasoning model.
Tests: new inferSurfaceIdentity coverage; updated three tests whose
fixtures were named after known services (they now take the fast-path) —
abstention test uses a generic name, repair test expects 0 model calls,
cached test uses a complete identity. Full package green.
Discovery is the index, not the encyclopedia: it needs to quickly answer
'what is this and how do I reach it', then the Assistant supplies
standard-service knowledge and runs commands on demand for specifics.
Trim the guest command sets to surface identity signals only (OS,
hostname, running services, listening ports, top processes for
LXC/VM; OS, processes, ports, env for Docker). Drop the deep
enumeration — installed_packages, config_files, docker_mounts,
hardware/GPU, disk, cron, nested docker_check — which bloated the
evidence payload (and the AI analysis) for no benefit the Assistant
can't get live. Remove the now-unused dockerMountsCommand const and
retire its test; add TestGuestCommandSetsAreSurfaceOnly to pin the
surface intent (verified live: HA LXC went from 13 commands to 5).
Note: full speed also needs a fast identification path (the configured
reasoning model still exceeds the 45s analysis timeout on its own) —
that's the follow-up.
Completes the resource-type matrix in the scenario corpus: LXC, Docker,
VM, and now k8s. A redis-pod cache-loss/restart question that needs
kubectl exec access, redis.conf, the rollout-restart command, and the
memory-limit fact — verified through both chat and remediation packs.
Test-only coverage; full servicediscovery package green.
The corpus covered LXC and Docker workloads but not VMs, which the agent
reaches via the QEMU guest agent (qm guest exec) rather than pct/docker
exec. Add a Plex-on-VM cell — a transcode-failure question that needs the
guest-exec access, the GPU decoder (hardware fact), and the restart
command — verified through both the chat and remediation packs. Test-only
coverage; full servicediscovery package green.
The Discovery tab capped Key Facts at the first 8 in raw analyzer order
(facts.slice(0, 8)), but the analyzer can emit up to 12. So a late
service-control fact (how to restart the workload — the most actionable
one) could be dropped from the human view, even though it was captured.
Add a small pure helper that stable-sorts facts by actionability
(service > security > dependency > hardware > version > storage),
mirroring the backend context-pack ranking, and apply it before the cap.
New unit test pins the ordering and that a trailing service-control fact
survives the cap. Discovery 21 tests green; type-check + lint + prettier
clean (the 2 unrelated guardrail failures pre-exist).
FormatForRemediation surfaced config and log paths but not data paths,
while FormatForAIContext (chat) does. For remediation those matter —
backup targets, disk-full triage, restore points (e.g. a database data
dir or HA's /config/.storage). Add a Data Directories section, matching
the chat pack. Extends the remediation test to assert a data path
reaches it; teeth-checked. Full servicediscovery package green.
Add two common-service cells to the context oracle. Beyond documenting
nginx and MQTT, they pin two code paths the existing cells did not cover:
the read-only bind-mount marker (nginx config mounted read-only) and
security-category fact surfacing (mosquitto auth). Both pass against the
current formatters (no production gap) — regression protection for the
iter4/iter5 mount + fact-filter work. Corpus now 6 cells (HA-LXC,
HA-Docker, postgres, frigate, nginx, mosquitto). Full package green.