Commit Graph

3083 Commits

Author SHA1 Message Date
rcourtman f70c0214db Align Assistant tool context and progress visibility 2026-06-06 04:02:07 +01:00
rcourtman a7854bf341 Bound Assistant stream chunk stalls 2026-06-06 03:41:05 +01:00
rcourtman ce4dc88ab7 Show Assistant model reasoning activity 2026-06-06 03:34:08 +01:00
rcourtman ab99112ad2 Show Assistant tool starts while streaming 2026-06-06 03:12:25 +01:00
rcourtman 7c583c0547 Answer Assistant inventory counts locally 2026-06-06 01:47:05 +01:00
rcourtman 1f4477c27c Show live Assistant tool progress 2026-06-06 01:12:01 +01:00
rcourtman e1a9b69862 Bound provider stream startup waits 2026-06-06 00:34:45 +01:00
rcourtman 26bdfe27df Add same-model OpenRouter chat fallback 2026-06-06 00:26:02 +01:00
rcourtman 73b0635c21 Improve Assistant inventory and activity flow 2026-06-06 00:17:44 +01:00
rcourtman 2ab0a60e99 Speed up Assistant stream startup 2026-06-05 22:54:44 +01:00
rcourtman 0d2f2a3cc8 Anchor Assistant stream and OpenRouter default 2026-06-05 22:02:47 +01:00
rcourtman 5ef50f712d Expose Assistant fallback model route 2026-06-05 21:29:41 +01:00
rcourtman 17c4c1c172 Scope Assistant fast inventory turns 2026-06-05 21:17:16 +01:00
rcourtman 3d8a926acc Keep Assistant tool-call leaks out of chat output 2026-06-05 20:29:24 +01:00
rcourtman 7067a51680 Make Assistant provider fallback resilient 2026-06-05 20:15:55 +01:00
rcourtman c2dd431052 Prefer chat-suitable Assistant model routes 2026-06-05 19:36:16 +01:00
rcourtman a648afaff0 Avoid duplicate Assistant stream errors 2026-06-05 19:29:07 +01:00
rcourtman 774d49e2a4 Fix Assistant current-resource tool routing 2026-06-05 19:10:46 +01:00
rcourtman dae5daf6ac Fix Assistant provider route validation 2026-06-05 18:48:57 +01:00
rcourtman 9c06cda3ea Bound Assistant stream provider startup 2026-06-05 18:14:14 +01:00
rcourtman 5ea51b4b25 Harden Assistant stream output and OpenRouter token budget 2026-06-05 16:30:07 +01:00
rcourtman 064cd4bc80 Hide Assistant reasoning from message history 2026-06-05 15:43:06 +01:00
rcourtman 05d00a65a6 Hide raw Assistant tool-call output 2026-06-05 14:05:38 +01:00
rcourtman c5bee4a785 Surface Assistant provider readiness 2026-06-05 12:50:02 +01:00
rcourtman 6ba21c7a2b Clean up Assistant provider error messages 2026-06-05 12:25:56 +01:00
rcourtman 479ac6db2c Stream Assistant cold sessions directly 2026-06-05 11:54:01 +01:00
rcourtman 2b24c375e0 Abstain instead of fabricating discovery when commands can't run
When a deep scan was attempted for a workload (container/VM) but produced no
command output — e.g. the host agent rejects exec — the AI analyzer was still
asked to identify the service from metadata alone. It confabulated confident,
false identities: an ESPHome LXC and an influxdb-telegraf LXC were both
"identified" as Pi-hole at 0.95 confidence, with invented facts carrying
fabricated command sources (source: "pihole -v" for a command that never ran)
and a docker exec CLI for an LXC.

When a command scan was attempted for a command-dependent resource type but
yielded nothing, abstain: skip the analyzer entirely and return a not-determined
result (no service, no facts, zero confidence) with guidance to enable Pulse
Commands. Host agents are unaffected (identified from their own metadata), and
deployments without command scanning keep the metadata-only path. Abstaining
also avoids a wasted model call.

Verified live: re-running discovery on the esphome LXC now returns an empty,
zero-confidence result with the correct container CLI instead of fabricated
Pi-hole facts. Adds a regression test.
2026-06-05 09:57:57 +01:00
rcourtman 6bbbf185bb Make workload discovery repair known service results 2026-06-05 09:21:53 +01:00
rcourtman 71b1762d53 Sanitize provider stream errors shown in the Assistant chat
On a provider/transport failure the chat rendered the raw upstream error. An
OpenRouter 402, for example, dumped the provider's billing JSON and a
workspace-key dashboard URL straight into the message: unreadable, and a leak
of provider routing plus a key fragment.

Classify known failures (billing/402, auth, rate-limit, timeout, cancel) into
clean actionable messages, and for anything else strip the embedded JSON body
and any URL so raw provider payloads never reach the user. Applied to both the
in-stream error-event path and the transport-failure fallback. Adds tests,
including the exact OpenRouter 402 string.

Verified live: the 402 now reads "The AI provider rejected the request for
billing or quota reasons..." instead of the raw JSON+URL dump.
2026-06-05 09:00:50 +01:00
rcourtman 91528f5a07 Fix Assistant resource context and model route labels 2026-06-04 23:45:15 +01:00
rcourtman 80b20cb1ff Stream OpenRouter reasoning tokens in the OpenAI client
OpenRouter and other OpenAI-compatible gateways normalize chain-of-thought
into a "reasoning" delta field, while DeepSeek's direct API uses
"reasoning_content". The OpenAI-compatible client only read
"reasoning_content", so reasoning models routed via OpenRouter (e.g.
deepseek/deepseek-v4-pro, the configured Assistant default) had every
thinking token dropped. The Assistant showed a long dead pause and then the
answer instead of a live thinking stream, which read as "DeepSeek doesn't
stream".

Parse "reasoning" on both the streaming delta and the non-streaming message,
and surface it as a thinking event alongside the existing reasoning_content
path. Direct DeepSeek (reasoning_content) is unchanged. Adds a regression
test feeding an OpenRouter-style reasoning stream.
2026-06-04 23:25:50 +01:00
rcourtman a43f7cbe7f Add discovery readiness to Assistant context 2026-06-04 21:56:36 +01:00
rcourtman 29ac5945bc Fix resource-context Assistant tool targeting 2026-06-04 19:18:08 +01:00
rcourtman d305d602a6 Add resource-context Assistant eval 2026-06-04 18:44:47 +01:00
rcourtman 3834bc1a78 Add resource-context safety tests 2026-06-04 17:05:57 +01:00
rcourtman 3f8525a7c2 Implement resource-aware Assistant context 2026-06-04 16:36:04 +01:00
rcourtman bd6f77e093 Prepare v6.0.0 release candidate
Tighten v5-to-v6 upgrade safety, release installability, provider MSP mode handling, AI cost accounting, metrics flushing, and frontend guardrails for the v6.0.0 GA candidate.
2026-06-04 14:07:14 +01:00
rcourtman efb6976069 Diagnostics: handle blank-name instances; add report-token router test
Two follow-ups caught by independent re-review:
- #29 (8855b78c0): GetConnectionStatuses keys a name-less instance by host,
  but the diagnostics lookup used 'pve-'/'pbs-'+Name only, so the
  monitor-state merge was a silent no-op for unnamed instances. Fall back
  to host to match.
- #22 (d9da9b18f, #1254): add a full-router test asserting an UNBOUND
  agent:report token gets 404 (not 200) on config fetch — continuously
  verifying the token-binding security boundary that the scope change
  relies on.
2026-06-04 11:49:17 +01:00
rcourtman ec74894982 Don't clear Ceph pool alerts in the stale-storage sweep
Follow-up to a85ec40a4 (#19), caught by independent re-review. Ceph pool
storage ("<instance>-ceph-pool-<name>") is raised/cleared by the separate
Ceph poll path (checkCephPoolStorage) and is NOT in the allStorage
inventory passed to SyncStorageAlertsForInstance — which runs before
pollCephCluster. Without a guard the sweep cleared the ceph pool usage
alert every storage poll and pollCephCluster re-raised it every cycle
(flapping: notification spam + StartTime reset). Skip any alert whose
ResourceID contains '-ceph-pool-'. Adds a regression test.
2026-06-04 11:43:03 +01:00
rcourtman d4edd1264d Fix TestRestart_StartIfServiceMissing expectation for single config load
#23 (a6b773a85) made Restart load the AI config once and pass it to
startWithConfig, so the start path no longer re-loads it. The test still
expected LoadAIConfig().Twice(); updated to .Once() to match the fixed
single-load behavior. (Caught by running the full internal/api suite,
which the per-fix targeted runs had missed.)
2026-06-04 11:23:05 +01:00
rcourtman fb91fbe9fe Revert #38 QuickAnalysis change (introduced a patrol regression)
Reverts b39cfc3a0. That port of v5 fix #1360 made QuickAnalysis build a
per-model provider via NewForModel, but v6 deliberately uses the configured
s.provider (it already has createPatrolProviderForModel for the per-model
case elsewhere), and NewForModel resolves unknown prefixes to a real Ollama
client rather than erroring — so the fallback never fired. This broke the
existing TestPatrolService_AskAIAboutAlert tests (panic via an empty
response) and changed v6's intended behavior. Restored to v6's original
QuickAnalysis. #1360 needs re-evaluation against v6's provider model before
any re-attempt.
2026-06-04 11:15:43 +01:00
rcourtman 1713662379 Stamp AI models with their provider for correct grouping (#1320)
Back-port the core of v5 fix 1de1392c9 to v6 (minimal, not the full
dedup refactor). The model list API now sets ModelInfo.Provider (stamped
in prefixProviderModels and passed through HandleListModels), and the
frontend groupModelsByProvider prefers the server-supplied provider over
splitting the model id, so models with opaque ids (e.g. Ollama-hosted
llama3-8b) group under the correct provider. Adds a grouping regression
test.
2026-06-04 10:47:40 +01:00
rcourtman 2db82fc302 Reject Docker host match on conflicting physical identity (#1366)
Back-port v5 fix 333e66a8e to v6, adapted to v6's DockerHostView accessors.
findMatchingDockerHost now skips an agentID/token match when
dockerHostIdentityConflicts detects a different MachineID, Swarm NodeID, or
(absent stronger IDs) hostname, so two physically different Docker nodes
that share a token+agentID are not collapsed into one host record. Adds a
regression test.
2026-06-04 10:35:35 +01:00
rcourtman a6b773a85f Reuse the loaded AI config when restarting the service
Back-port v5 fix 716340dcd to v6. Extracts startWithConfig from Start so
Restart passes the config it already loaded down to the start path, instead
of Start re-reading loadAIConfig a second time. Avoids starting the AI
service with a stale/inconsistent config if it changed between Restart's
load and the start. Both Restart start paths now use startWithConfig.
2026-06-04 10:30:45 +01:00
rcourtman 3928b19803 Attach ZFS pool status to dir storages on ZFS dataset paths
Back-port v5 fix ae6b663e9 to v6. Removes the 'only fetch ZFS pools when a
zfspool/zfs/local-zfs storage exists' gate (you cannot tell whether a dir
storage sits on a ZFS dataset without the pool list; fetch is gated by
PULSE_DISABLE_ZFS_MONITORING and empty-fast on non-ZFS nodes) and the
type guard before matchZFSPoolForStorage, so a dir storage whose path
resolves to a pool (e.g. /rpool/data) now gets ZFS health attached. The
guardrail-pinned 'if pool := matchZFSPoolForStorage(...)' call form is
preserved. Adds a poll integration test.
2026-06-04 10:27:59 +01:00
rcourtman b8a60db6fa Fall back to argv0 for agent self-update path on FreeBSD (#1457)
Back-port the agentupdate half of v5 fix 8600706da to v6 (the install.sh
FreeBSD rc.d supervisor-pidfile change is already present in substance).
performUpdate now resolves the executable via resolveExecutablePath, which
falls back to an absolute, existing os.Args[0] when os.Executable() fails
or returns empty — the condition that breaks agent self-update on
FreeBSD/OPNsense. Adds an injectable osArgsFn and a unit test.
2026-06-04 10:19:45 +01:00
rcourtman 67a8c4188b Use node-local task counts for node-scoped swarm services
Back-port v5 fix 48bdfdc30 to v6. In node scope, collectSwarmDataFromManager
now derives each service's Desired/Running/Completed task counts from the
node-local task list and drops services with no tasks on this node, instead
of reusing the cluster-wide ServiceStatus and a 'keep all when none match'
fallback. Without this, a node-scoped Swarm view showed wrong counts and
services that aren't running on that node. Adds two regression tests.
2026-06-04 10:16:09 +01:00
rcourtman b39cfc3a0e Respect the patrol model's provider in QuickAnalysis (#1360)
Back-port v5 fix 5f372e257 to v6. QuickAnalysis now builds a provider for
the configured PatrolModel via providers.NewForModel (matching the 8 other
call sites that already do so), falling back to the default provider if it
cannot. Previously a patrol model on a different provider than the default
(e.g. default Gemini, patrol OpenAI) was sent to the wrong backend. Guard
changed from provider==nil to cfg disabled, with a re-check after provider
selection. Updates the existing fallback test and adds a positive
patrol-provider test (httptest OpenAI server).
2026-06-04 10:12:07 +01:00
rcourtman 8855b78c0d Prefer live monitor connection state in diagnostics
Back-port v5 fix d310c257a to v6, adapted to v6's connection-status key
format. computeDiagnostics now merges a failed PVE/PBS diagnostics probe
with the monitor's live connection state: if the long-running poller still
reports the instance connected, a transient probe failure (network blip,
TLS re-check) no longer flips it to 'disconnected' in the UI. Uses v6's
'pve-<name>'/'pbs-<name>' status keys (v5 used a bare node name, which
would not match in v6). Adds a merge-logic regression test.
2026-06-04 10:07:10 +01:00
rcourtman 97c6cad01c Normalize typed SMART device labels for temperature merge
Back-port v5 fix 22f8dd3bb to v6. Adds normalizeSMARTDeviceIdentifier
(strips a trailing ' [scsi]'/'[ata]'-style type annotation and the /dev/
prefix) and uses it in the device-path match for both mergeNVMeTempsIntoDisks
and mergeHostAgentSMARTIntoDisks, with a non-empty guard. Without it, disks
whose SMART entries carry a typed label never matched by device path, so
their temperature stayed 0. Function signatures/call sites are unchanged
(canonical guardrail still passes). Adds a unit test.
2026-06-04 09:54:23 +01:00