Commit Graph

3677 Commits

Author SHA1 Message Date
rcourtman f8c5c55d11 Let --report-ip lead host identity and pass it through the installer
An explicit --report-ip is the user naming the primary address on a
multi-NIC host, but identityFromHost appended it after the auto-detected
interface addresses while every consumer of ResourceIdentity.IPAddresses
treats the first entry as primary, so the override never changed what
the Machines table displayed. Prepend it instead.

The install script also rejected --report-ip as an unknown argument even
though the agent supports the flag, forcing hand edits to the service
unit that a later --update run would drop. Accept the flag, render it
into the service ExecStart, persist it in connection state, and
recognise it during saved-state and arg-stream recovery so updates
preserve it.

Refs #829

Contract-Neutral: behavioral fix: user-specified report-ip leads host identity addresses and the installer passes --report-ip through; no public contract change (#829)
2026-07-22 00:49:43 +01:00
rcourtman 0928584625 Pro updater requests its channel from the dual-channel broker
The license-server broker now serves stable and rc manifest slots
(pulse-pro side). The Pro self-updater passes channel=rc for rc-channel
installs so they track the RC slot; stable installs keep the unchanged
default URL, which older brokers also understand. The client-side guard
that refuses a prerelease pin on the stable channel stays as the backstop
for a drifted or single-manifest broker. Contract updated under
deployment-installability; fixture now pins the channel query behavior.
2026-07-22 00:16:43 +01:00
rcourtman d6fa623035 Compose ESXi hosts as members of their owning vCenter connection
One vCenter connection spans many ESXi hosts, so the Infrastructure source
row now lists them the way Proxmox cluster rows list their nodes: an
expandable member list with per-host state, aliases, and last-seen. Members
are API-side composition only — no primary marker, no agent connection, and
the member subtitle reads 'vSphere host' instead of cluster-node wording.
TrueNAS keeps no member composition because that connection monitors exactly
one machine.

Mock mode now feeds its vCenter and TrueNAS fixtures into the connections
aggregator (only when no real instances are configured), so the mock ledger
shows the same platform source rows a real deployment would instead of
omitting vSphere and TrueNAS entirely.
2026-07-21 23:09:29 +01:00
rcourtman 9e8bcd23fe Stop fabricating ledger agent rows for integration-monitored machines
vSphere ESXi hosts and TrueNAS boxes rendered as standalone rows under
'Pulse Agent hosts' in Infrastructure settings even though no Pulse Agent
runs on them. The rows were unmanageable duplicates of their owning platform
connection: they carry no credentials, pause, or remove semantics, and they
can never attach to their vSphere/TrueNAS system because attachment requires
a shared host while the machine hostname differs from the vCenter address.
They also inflated connected-system counts.

buildConnections now skips hosts with a non-empty IntegrationSource, so the
ledger and grouped systems represent those machines solely through their
owning platform connection. Per-machine visibility is unchanged on the
vSphere/TrueNAS platform pages and Machines, which read the unified fabric
directly. Connection.integrationSource stays declared as defense-in-depth
for agent-only client workflows. No alert impact: agent-type rows were
already dropped from alert snapshots.
2026-07-21 22:11:46 +01:00
rcourtman 7354d8d19f Keep integration-monitored machines out of Agent Doctor and surface workload-only agents
The connections ledger derives agent rows from the unified fabric, which
includes machines whose telemetry comes from platform integrations (vSphere
ESXi hosts, TrueNAS). Agent Doctor rendered every one as a permanent
'Unknown / no structured reason' row, while agents the ledger does not carry
(Docker-only, Kubernetes-only) were silently dropped from the fleet view.

- Expose HostView.IntegrationSource() (source-set based: only SourceAgent
  ingest counts, since integration providers fabricate an Agent payload) and
  plumb it through models.Host to the connections ledger as the optional
  integrationSource field.
- Agent Doctor skips integration-backed connections and appends
  diagnostics-only agents, honoring scope, so the doctor covers exactly the
  real Pulse Agent fleet.
- Update readiness agent checks no longer count integration-backed machines
  as registered agents.
- Humanize doctor copy: plain-language stale message with '10m 2s'-style
  durations, offline wording without enum leakage, no 'Supported target:
  Unknown' cell when no target is published, host-local command banner only
  when a command is actually offered, and a compact non-zero summary strip.

Contracts updated for unified-resources, monitoring, api-contracts,
agent-lifecycle, and dependent storage-recovery; verification via
views_test.go, monitor_host_agents_test.go, state_host_test.go,
contract_test.go, and the frontend connections API test.
2026-07-21 21:24:15 +01:00
rcourtman 0e6fa57c08 Normalize Proxmox-reported disk health to the canonical vocabulary
PVE's disks/list endpoint labels healthy SCSI/SAS drives OK while ATA
drives say PASSED, and failing ATA drives come back as FAILED! with the
bang. Pulse ingested those raw strings, so a healthy SAS drive rendered
as health Unknown even though the Proxmox UI showed S.M.A.R.T. OK. The
host agent and TrueNAS paths already normalize their health text; the
PVE ingestion was the only entry that did not.

Map OK/PASS to PASSED and FAIL-containing values to FAILED at ingestion,
and accept OK as healthy in the disk presentation layer as defense for
state produced by older servers.

Refs #1595

Contract-Neutral: behavioral fix: normalize PVE disk health strings at ingestion, no public contract delta (#1595)
2026-07-21 20:30:54 +01:00
rcourtman 59e6f25a65 Detect SAS transport and parse SCSI attributes in SMART collection
smartctl reports SAS drives with device protocol SCSI, so detectDiskType
fell through to its blanket sata default, and that non-empty type also
masked the text-output transport evidence the fallback parser had
already extracted. The wrong sata label then blocked the merge layer
from promoting the smartctl serial over the SAS transport address
Proxmox reports.

Classify SCSI-protocol devices via the scsi_transport_protocol
descriptor, let the text and sysfs refinements upgrade a generic scsi
label, and apply the legacy sata default only after all evidence is
exhausted. Parse the SCSI log-page fields (power-on hours, grown defect
count, endurance used) that SCSI drives report instead of an ATA
attribute table, and let agent-reported sas replace coarse hdd/ssd/sata
types during the disk merge.

Refs #1595

Contract-Neutral: behavioral fix: SAS transport detection and SCSI attribute parsing in host agent SMART collection; no public contract delta (#1595)
2026-07-21 17:36:14 +01:00
rcourtman 6b1dee576e Resolve mangled patrol assessment IDs against active findings
Patrol's verdict contract required the model to echo finding IDs like
update-analysis-docker:<uuid>/<64-hex-digest> exactly. Smaller models
(reported with Gemini 2.5 Flash-Lite) drop the key prefix or digest
tail, every patrol_assess_finding call fails, and the whole run ends
with 'Patrol finding assessment incomplete'. Resolve an ID that
unambiguously identifies one active in-scope finding (case-insensitive,
prefix, or separator-suffix match), record the canonical ID so the
verdict accounting and duplicate guard still hold, and list the valid
active IDs in the lookup error so the model can self-correct instead of
guessing. Hallucinated or ambiguous IDs still fail closed.

Refs support report from Johannes Strasser, 2026-07-20

Contract-Neutral: behavioral fix: tolerant patrol assessment ID resolution for weak models; fail-closed contract unchanged
2026-07-21 15:13:43 +01:00
rcourtman e339af2a6e Order physical interface IPs ahead of virtual bridges in host identity
Agents report network interfaces sorted by name, which places docker0
and br-* bridges ahead of eth*/en* interfaces, and every consumer of
ResourceIdentity.IPAddresses treats the first entry as the host's
primary address. Docker hosts therefore showed a 172.x bridge IP in
the Machines and Docker tables instead of the machine's LAN address.
Collect physical-looking interfaces first so bridge and overlay
addresses never lead the list.

Refs #1529

Contract-Neutral: behavioral fix: order physical interface IPs before virtual bridges in identity collection; no public contract change (#1529)
2026-07-21 14:27:48 +01:00
rcourtman 4f6179368a Add branch-coverage tests for unifiedresources helpers, smartctl targets, intent-policy manager
Covers previously-uncovered pure/near-pure functions from the recent
alert-intent and disk-inventory source drop:

- unifiedresources registry seeded K8s identity/source-id builders
  (cluster/node/pod/deployment/typed) and VMware/proxmox/predicate
  helpers (seededVMwareSourceID, proxmoxGuestFallbackSourceID,
  isDockerNetworkAttachmentRelationship, physicalDiskTopologyCompatible)
- unifiedresources ActionPolicyAuthorizationDigest (digest-excluded
  invariant) and normalizePair ordering invariant
- hostagent smartctlTargetsFromDevices
- alerts Manager UpdateIntentPolicies (validation / nil / revision
  conflict / success), GetIntentPolicies, SetBackupIntentContextResolver

All 13 target functions move 0% to 100% under the new tests; test-only,
no source changes.
2026-07-21 06:14:26 +01:00
rcourtman 4f43878ebb Add focused branch coverage and repair infrastructure source contract
Contract-Neutral: test-only coverage and source-contract assertions; no runtime or contract behavior changed
2026-07-20 22:41:19 +01:00
rcourtman 9f9e02e811 Refuse dispatch when action readiness is lost 2026-07-20 21:44:59 +01:00
rcourtman ed84c92da3 Add Patrol model readiness advisor 2026-07-20 20:45:39 +01:00
rcourtman 057cf74629 Add alert intent policies and delivery receipts 2026-07-20 20:27:39 +01:00
rcourtman e23f19459d Add Agent Doctor fleet diagnostics workflow 2026-07-20 19:53:34 +01:00
rcourtman a70e285c4f Fall through server-error probes in vSphere release negotiation
vCenter answers a newer-than-supported vim25 release path with 404 on
some builds but HTTP 500 on others: 8.0.x returns 500 for the 9.0.0.0
service-content probe. The negotiation loop only continued on
not_found, so it aborted on the first probe and never tried the 8.0.3
release the server actually speaks, failing the connection test with
'vi-json service content request failed with HTTP 500'.

Continue the probe loop on generic endpoint failures as well as 404;
still abort immediately on auth, permission, TLS, and network errors,
which no release string can fix.

Fixes #1596

Contract-Neutral: behavioral fix: vSphere release negotiation falls through HTTP 500 probe responses (issue #1596); no public contract delta
2026-07-20 16:37:22 +01:00
rcourtman a4d800fe3e Keep platform CPU authoritative on hypervisor-managed guests
An agent running inside an LXC measures /proc through the shared kernel
and reports the node's utilisation, not the container's. Because
SourceAgent outranked SourceProxmox in the metric merge, that value
overrode the hypervisor's cgroup-accounted CPU on the guest's canonical
resource, so the workloads row disagreed with the (Proxmox-sourced)
history chart by orders of magnitude on idle guests.

Demote agent-sourced utilisation metrics below the platform source when
the target resource is a hypervisor-managed guest (vm /
system-container). The freshness gate still lets a live agent cover for
a stale platform source.

Fixes #1597

Contract-Neutral: behavioral fix: demote in-guest agent utilisation below platform source on hypervisor-managed guests (issue #1597); no public contract delta
2026-07-20 16:34:58 +01:00
rcourtman b6a74576bc Integrate trust-gate reliability fixes 2026-07-20 16:03:29 +01:00
rcourtman 674b70a02a Add Go branch-coverage tests for remaining monitored-system selector matchers
Test-only (GLM nightly grunt, round 2): new
monitored_system_projection_matchers2_branchcov0720am_test.go covering the
previously-uncovered Docker/TrueNAS/PBS/VMware per-type replacement-selector
matchers and the monitoredSystemReplacementSelectorMatches dispatcher in
internal/unifiedresources. Each per-type predicate drives its nil-source guard,
every OR-chain arm in isolation, no-match, and whitespace-only; the dispatcher
drives each DataSource route plus the unmatched arm. Complements the round-1
Agent/Proxmox/PMG/K8s coverage. No source changed; contract-neutral.
2026-07-20 11:08:29 +01:00
rcourtman a1eb3e9fe5 Dedupe latest-action-audit lookups behind a match-predicate helper
GetLatestActionAuditByOrigin and GetLatestActionAuditByOperationalRecord
were token-for-token identical except for which Origin field they matched,
tripping golangci-lint's dupl check. The scan/latest-selection/clone logic
now lives in latestActionAuditMatching(match); each getter keeps only its
input trimming and predicate.

Contract-Neutral: dupl lint dedup: extract shared latestActionAuditMatching helper, behavior identical, no public-contract delta
2026-07-20 10:56:22 +01:00
rcourtman 325e7f5bb2 Add Go branch-coverage tests for unified-resource pure helpers and platform fixtures
Test-only wave (GLM nightly grunt): new *_branchcov0720am_test.go files raising
branch coverage on previously-uncovered pure value-in/value-out functions. No
source changed; contract-neutral.

internal/unifiedresources (10 files):
  monitored_system_projection selector matchers (Agent/Proxmox/PMG/K8s),
  physical_disk risk classifier + metric-id builder, availability lookup,
  monitored_systems reason/suffix helpers, action refusal classifier +
  human-action-binding validation, action auto-authorization class
  validate/normalize, host APT digest validator, patrol-autopilot stored-evidence
  validation, canonical governance metadata projection, top-level identity basis.
internal/platformsupport: host-identity token/profile lookup + deep-copy safety.
internal/vmware: fixture activity-change projection + connection-error guard.
internal/mock: discovery-fixture type/target filters.
internal/mockruntime: startup-enabled env gate.

Each new test drives every distinct branch (nil/empty, each conditional arm,
error sentinels via errors.Is, and returned-copy independence) and verified
0%->covered on its target functions. Two named functions were intentionally
left uncovered as branchless (ActionPolicyAuthorizationDigest; the default-build
ValidateEnablement, whose branchy twin sits behind the release build tag).
2026-07-20 10:49:11 +01:00
rcourtman 92c567e41d Make agent artifact guard quote agnostic
Contract-Neutral: The canonical agent artifact guard preserves the existing unified-agent contract while accepting equivalent JavaScript quote styles.
2026-07-20 09:14:40 +01:00
rcourtman 1153be4770 Keep agent install handoff on canonical config
Contract-Neutral: The first-session agent install handoff keeps the existing API and setup contracts while binding runtime handlers to the Router-owned canonical config through startup and monitor reloads.
2026-07-20 08:19:00 +01:00
rcourtman c73e78aeea Stabilize RC runtime ownership
Contract-Neutral: RC qualification fixes preserve existing public API, tenant, monitoring, and organization contracts while correcting canonical runtime ownership and test fixtures.
2026-07-20 07:50:22 +01:00
rcourtman 6eb559905d Reuse mock history seeds across tenants
Contract-Neutral: Reuse immutable mock-history seed work across tenant monitors without changing monitoring, tenant-isolation, or API contracts.
2026-07-20 06:30:55 +01:00
rcourtman 2e00d44cd2 Preserve deployment mock settings on reload
Contract-Neutral: Preserve deployment-owned PULSE_MOCK environment values when the watched auth env file reloads; no public contract changes.
2026-07-20 05:52:12 +01:00
rcourtman 62993899fc Isolate agent buffering qualification 2026-07-20 04:35:52 +01:00
rcourtman 99af2a0bf2 Accelerate mock history readiness 2026-07-20 04:21:26 +01:00
rcourtman 12eef62f76 Stabilize cross-platform release gates 2026-07-20 02:32:29 +01:00
rcourtman 57f3fa8431 Add Go branch-coverage tests for unified-resource views and pure helpers
Cover pure functions left at 0% after the canonical Operational Trust and
unified-resource work, surfaced by a fresh coverage probe.

- unifiedresources views.go accessors for K8sNodeView, DockerContainerView,
  PodView, K8sDeploymentView, HostView, DockerHostView, K8sClusterView and
  the smaller ContainerView.Pool / NodeView.IsClusterMember /
  PhysicalDiskView.MetricResourceID / PBSInstanceView.Datastores /
  PMGInstanceView.InstanceID accessors. Each exercises the nil-receiver and
  nil-nested defensive arms, the populated projection, and slice/map
  clone-independence.
- unifiedresources clone.go seven Ceph deep-clone helpers, asserting value
  equality, mutation independence, and nil/empty inputs.
- agentcapabilities firstStringPayloadValue, MCPManifestPromptProjectionSupported
  and JSONRPCError.Error.
- ai/tools data_types NormalizeCollections (three receivers),
  ValidateCurrentResourceAvailable and ErrExecutionContextUnavailable.Error.

Test-only. Every target function moved 0% to covered. No source changes.

Contract-Neutral: test-only branch-coverage tests; no source or contract changes
2026-07-19 22:23:42 +01:00
rcourtman 3fcd26b5c3 Stop the Autopilot gate from leaking acknowledgement_id downstream
GatePatrolAutonomyUpdate validates and consumes the activation
acknowledgement, then re-marshals the settings for the downstream
handler - but the marshaled body still carried acknowledgement_id.
The Pro autonomy handler decodes the body with DisallowUnknownFields
and has no such field, so every Autopilot (full mode) activation on a
Pro build failed with 400 Invalid request body. Lower autonomy levels
forward the original body untouched, which is why only full mode broke.

Clear the consumed acknowledgement before normalizing and hand it to
the downstream handler via request context only. The new regression
test strict-decodes the forwarded body with the enterprise handler's
exact field set.

Contract-Neutral: Autopilot activation gate normalization fix; endpoint contract unchanged
2026-07-19 19:51:32 +01:00
rcourtman 52ed884640 Prepare v6.1.0-rc.4 release 2026-07-19 15:52:42 +01:00
rcourtman 4b066a0f1e Add report-only Unified Agent observer destinations 2026-07-19 15:30:34 +01:00
rcourtman ff93d99fa1 Add Go branch-coverage tests for pure helpers across six packages
Test-only wave, contract-neutral. New *_branchcov0719pm_test.go files cover
previously-uncovered pure value-in/value-out helpers, each verified to move
its target functions from 0% to full coverage:

- internal/agentcontext: formatKubernetesServicePorts (empty/single/cap/overflow
  arms) and addMetricFact (nil-metric, percent/value/ratio arms) now 100%.
- pkg/reporting: reportLogoTypeFromPath 0->100, reportLogoTypeFromData 28.6->100,
  scaledLogoSize 70->90 (extension and aspect branches).
- internal/cloudcp/email: RenderMagicLinkEmail 0->80 (render success path; the
  compile-time template-error arm is unreachable and left uncovered).
- internal/recovery: recoveryDetailString (nil map, missing key, non-string,
  string arms) and recoveryPointObservedAt 40->100.
- internal/ai/tools: ErrStrictResolution/ErrRoutingMismatch ToToolResponse 100.
- internal/ai/providers: every NormalizeCollections receiver 0->100.

No source or existing test modified.
2026-07-19 15:11:02 +01:00
rcourtman 83672a00a8 Harden Operational Trust rollout contracts 2026-07-19 15:11:02 +01:00
rcourtman 7791b35ae9 Add Go branch-coverage tests for canonical trust and recovery helpers
Cover pure functions the recent canonical Operational Trust and protection
posture work landed with no unit test:

- recovery/model posture: enum Valid, struct Clone/Validate/Payload and the
  normalize/cloneTime/validOutcome/sortedUniqueStrings/compareProviderStates
  helpers (20 funcs, all 0%->100%)
- operationaltrust contracts: EvidencePayloadRef/Acknowledgement/Suppression/
  LifecycleTransition Validate plus LifecycleTransition/NotificationLink Clone
- cloudcp/proxytrust: ClientIP, ExtractRemoteIP, rightMostUntrustedForwardedIP
  and IsTrustedProxyIP forwarded-header and CIDR parsing
- mockmodel: NormalizeBlendWeight, SeriesForTimestamps, seriesForProfile,
  diskIOValue and flatValue deterministic seeded math
- updatesignature: DecodePrivateKey and HasTrustedPublicKeys
- recovery keys: ProxmoxPBSGuestLooseContinuityKey branch guards
- vmware SourceID and truenas availableAppLogContainers formatting

Test-only, contract-neutral. New *_branchcov0719_test.go files only; no source
or existing test touched.
2026-07-19 15:11:01 +01:00
rcourtman cd75722da3 Build canonical Operational Trust action loop
Refs #1034
2026-07-19 15:11:01 +01:00
rcourtman 69533c7afb Build canonical availability resource facets 2026-07-19 15:11:01 +01:00
rcourtman 07a4d05ca3 Build canonical Patrol attention workbench 2026-07-19 15:11:01 +01:00
rcourtman 93dbdd91cf Build canonical protection posture 2026-07-19 15:10:38 +01:00
rcourtman cf0486492e Build operational trust lifecycle foundation 2026-07-19 15:10:38 +01:00
rcourtman c2df4ad277 Fix alert detection and notification state boundary 2026-07-19 15:10:09 +01:00
rcourtman ce7702db7a Add Go branch-coverage tests for models, cloudcp and actionlifecycle helpers
New *_branchcov0718_test.go files extend the Go coverage wave to three more
areas of previously-uncovered pure logic. Covered areas include the domain-type
NormalizeCollections normalizers in models.go, the cloud control-plane pure
helpers for hosted-entitlement org-id normalization, magic-link URL redaction
and default MSP backup paths, and the actionlifecycle dispatch-attempt context
round-trip plus the typed-error Error and Unwrap methods.

Test-only, with no runtime or subsystem-contract change. Impure targets that
need a live Docker manager or tenant registry were skipped per the purity gate
and noted in the wave reports. Verified in a clean worktree at HEAD with go vet
and package tests green, gofmt clean, and every covered target moved from 0
percent toward full coverage.
2026-07-19 15:10:09 +01:00
rcourtman 278673aa0f Add Go branch-coverage tests for twelve pure backend helpers
New *_branchcov0718_test.go files raise coverage of previously-uncovered pure
functions across ten packages. Covered areas include securityutil SSRF and URL
validation, truenas path and telemetry parse helpers, storagehealth SMART and
physical-disk risk assessment, vmware inventory sort keys and error classifiers,
servicediscovery token filtering and readiness, telemetry evidence-from-history,
models ToFrontend converters and frontend NormalizeCollections normalizers,
actionplanner type predicates and canonical resource-id sort, config API-token
accessors, and licensing state accessors.

Test-only, with no runtime or subsystem-contract change. Verified in a clean
worktree at HEAD with go vet and package tests green, gofmt clean, and every
named target function moved from 0 percent to covered.
2026-07-19 15:10:09 +01:00
rcourtman f73d4d4d97 Collapse the container update ceremony to a single review
A routine Docker container update took five clicks across two in-row
confirmations and a two-phase Approve then Run dialog, and the review
screen said "Rollback declared: No" while its own intended-change text
promised automatic backup and rollback (#1564 feedback).

- Capabilities can declare rollback support; the planner carries it
  into plan.rollbackAvailable instead of hard-coding false, so the
  container update review now truthfully says rollback is available.
- Container update is classed AutoAuthorizeLowRisk (it ships backup and
  rollback; restart was already low_risk). The action projection
  exposes the read-time capability class plus resolved blast-radius
  names, none of which enter plan identity.
- The review dialog collapses pending approval for low-risk
  capabilities into one Approve and run control that records the
  approval and dispatches execution from a single confirmation; other
  capabilities keep the two-phase flow. Blast radius renders resource
  names with IDs demoted.
- The in-row Update button plans on first click; the dialog is the
  confirmation surface, so the Confirm hop and its cancel affordance
  are gone.

Routine update ceremony is now Update, Approve and run, Close, with
the durable action record unchanged.
2026-07-19 14:48:24 +01:00
rcourtman 49516bf367 Supersede stale Docker host records after agent re-enrollment
Wiping the agent state dir regenerates the agent ID and a fresh install
command mints a fresh token, so Docker host identity resolution correctly
refuses to adopt the old record (a foreign token must never take over a
live host) and creates a new one. Nothing ever reaped the old record, so
it sat in state forever with a stale agent version, stale containers and
stale image digests. That is the source of the outdated-agent banner that
contradicts the update dialog in #1586, the doubled container rows in
#1564, and plausibly the digest-mismatch preflight refusal there too.

Reap a matching record only when it stopped reporting before the
superseding token was minted, the same explicit-reenroll-intent rule the
removal block uses (#1581). A record that is still reporting keeps
advancing LastSeen and is never touched, and supersession sets no
resurrection block.

Refs #1586 #1564

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT: docker host state bug fix; live Codex agent WIP holds the contract docs
2026-07-19 09:13:14 +01:00
rcourtman 1b7488e552 feat(ai): let mobile chat propose actions behind mandatory approval
Field report (Johannes Strasser, 6.1.0-rc.3 mobile chat). The assistant
could find an LXC container but told him it had no tools to restart it.
Relay-mobile tokens carry only the relay access scope, which does not
satisfy the execute-authority check, so mobile chat sessions were offered
read tools only. The gap was one-sided: mobile could already approve and
execute planned actions, it just could not propose them.

Relay-mobile callers now hold execute authority, and the chat entrypoint
pins their sessions to approval-required mode, so every mobile-originated
action lands on the approval queue regardless of the configured control
level. Web sessions are unchanged.
2026-07-18 21:25:28 +01:00
rcourtman dc7204bf44 fix(actions): follow the resource's advertised lifecycle verb
Proxmox guests advertise reboot while container platforms advertise
restart, and the Assistant tool schema plus the resolved-resource action
lists teach models both words. A pulse_control call with the wrong
spelling failed with CapabilityNotFound even though the equivalent
capability was advertised. Plan requests now follow the resource's own
vocabulary when the requested verb is missing but its lifecycle synonym
is advertised; the rewritten name flows into the plan, the audit record
and the executor. Non-synonym verbs still fail closed.
2026-07-18 20:37:16 +01:00
rcourtman 3029db3bcf feat(ai): add compact and max_bytes to the chat messages endpoint
Tool-heavy chat histories exceed the relay proxy's 47KB response cap, so
the mobile app could never load big conversations over Remote Access.
GET /api/ai/sessions/{id}/messages now accepts compact=1 (strips tool
outputs, results and thought signatures while keeping tool_use_id
linkage) and max_bytes=N (returns the newest messages that fit the
budget; a single oversized message is truncated with a notice). The
mobile client sends compact=1&max_bytes=40960 and falls back to a
halving limit ladder when older servers answer 413.
2026-07-18 20:23:41 +01:00
rcourtman c05a665787 Keep high percentage triggers evaluable when the critical cap collides
Percentage metric specs derive their critical escalation as trigger+10
capped at 99. A user-set trigger of 99 or 100 produced critical <=
trigger, which failed spec validation and silently dropped the whole
metric from evaluation while logging a warn every poll cycle. Omit the
escalation threshold instead so the warning-level threshold keeps
working.

Seen in the wild as the 'Skipping invalid canonical unified metric
spec' warn spam in #1593's diagnostics.

Contract-Neutral: behavior fix with regression test; alerts contract docs carry another agent's WIP on the shared tree, delta deferred
2026-07-18 19:44:06 +01:00