When a resource exists in the hostname or IP index but has been removed from
the main resources map, looking up and accessing .Type would cause a nil
pointer dereference panic.
The MachineID lookup already had this check, but hostname and IP lookups
were missing it. This adds consistent nil checking across all three lookup
paths.
GetHostAgentConfig was loading profiles and assignments from disk on
every agent report (every 10-30 seconds per host). With multiple hosts,
this caused disk I/O contention that eventually led to request timeouts.
Added in-memory caching with 60-second TTL:
- Fast path reads from cache without locks when valid
- Double-checked locking pattern for cache refresh
- Cache auto-invalidates after TTL, no manual invalidation needed
When system settings API fails, the catch blocks in App.tsx now call
markSystemSettingsLoadedWithDefaults() to ensure the UI doesn't stay
permanently in a loading state with disabled update buttons.
Add three new MCP tools for Docker container update management:
- pulse_list_docker_updates: list containers with pending updates
- pulse_check_docker_updates: trigger update check on a host
- pulse_update_docker_container: apply update with approval workflow
Changes:
- Add UpdatesProvider interface to executor.go
- Add response types to data_types.go
- Add UpdatesMCPAdapter to adapters.go
- Register tools and handlers in tools_infrastructure.go
- Add SetUpdatesProvider() to service.go
- Wire provider in router.go wireOpenCodeProviders()
Chat panel fixes:
- Fix collapse button not working (use store's isOpenSignal)
- Add isOpenSignal accessor for proper SolidJS reactivity
- Click-outside handler to close dropdowns
Session management UI:
- Session actions menu (summarize, diff, revert)
- Better tool result handling with exit codes
- Continuation messages after command execution
- Session title generation from first message
OpenCode API additions:
- Session summarize/diff/revert endpoints
- Better model format handling
OpenCode client improvements:
- Fix session listing with proper timestamp parsing
- Model selection with provider inference (anthropic, google, etc)
- Add session management APIs (summarize, diff, fork, revert)
- Generated session titles from first user message
Control level refactoring:
- IsAutonomous() helper for cleaner checks
- Legacy autonomous_mode maps to control_level for backwards compat
- Simplified system instructions (rely on tool descriptions instead)
Includes tests for model provider inference.
Adds automatic Docker detection for Proxmox LXC containers:
- New HasDocker and DockerCheckedAt fields on Container model
- Docker socket check via connected agents on first run, restart, or start
- Parallel checking with timeouts for efficiency
- Caches results and only re-checks after state transitions
This enables the AI to know which LXC containers are Docker hosts
for better infrastructure guidance.
Extends the demo mode behavior to also apply when PULSE_DEV=true,
allowing developers to test Pro features during development without
requiring a license key.
Implements exponential backoff restart loop in the wrapper script
for Unraid/Slackware installations. When the agent exits unexpectedly,
it will automatically restart with increasing delays (5s -> 60s max).
This improves reliability for users who don't have systemd for
automatic service restarts.
- Add control_level and protected_guests to AISettings interface
- Add control_level and protected_guests to AISettingsUpdateRequest
- Fix type error in filteredRows by casting filterType as UnifiedAgentType
- Make formatRelativeTime and formatAbsoluteTime accept undefined
The uninstall command was including '--token <api-token>' as a literal
string when no token was configured, causing a shell syntax error when
users copied and ran it. The angle brackets are interpreted as shell
redirects.
The uninstall script works without a token (it's primarily a local
cleanup operation), so we now only include the token parameter when
a real token is available.
Related to #1099
The agent was crashing with 'fatal error: concurrent map writes' when
handleCheckUpdatesCommand spawned a goroutine that called collectOnce
concurrently with the main collection loop. Both code paths access
a.prevContainerCPU without synchronization.
Added a.cpuMu mutex to protect all accesses to prevContainerCPU in:
- pruneStaleCPUSamples()
- collectContainer() delete operation
- calculateContainerCPUPercent()
Related to #1063
Add ability for users to describe what kind of agent profile they need
in natural language, and have AI generate a suggestion with name,
description, config values, and rationale.
- Add ProfileSuggestionHandler with schema-aware prompting
- Add SuggestProfileModal component with example prompts
- Update AgentProfilesPanel with suggest button and description field
- Streamline ValidConfigKeys to only agent-supported settings
- Update profile validation tests for simplified schema
Previously, logout cleared all localStorage, wiping user preferences
like column visibility, view modes, temperature units, and filters.
Now only auth and session-specific caches are cleared.
Fixes#1107
Users with removable/unmounted datastores (e.g., external HDDs for
offline backup) experienced excessive PBS log entries because Pulse
was querying all datastores including unavailable ones.
Added `excludeDatastores` field to PBS node configuration that accepts
patterns to exclude specific datastores from monitoring:
- Exact names: "exthdd1500gb"
- Prefix patterns: "ext*"
- Suffix patterns: "*hdd"
- Contains patterns: "*removable*"
Pattern matching is case-insensitive.
Fixes#1105
Users with 100+ containers were hitting the payload size limit,
causing "Failed to decode request body" 400 errors. This aligns
the Docker agent limit with the Kubernetes agent limit (2MB).
Fixes#1104
OpenCode's frontend uses window.location.origin for API calls. When
embedded in Pulse's iframe, this points to Pulse instead of OpenCode.
This commit proxies OpenCode's API paths through Pulse:
- /global/, /session/, /tui/, /config/, /file/, /find/, /instance/,
/mcp/, /permission/, /project/, /provider/, /pty/, /question/,
/experimental/
Changes:
- router.go: Add OpenCode API paths to route check and register handlers
- ai_handler.go: Add HandleOpenCodeAPI to proxy requests to OpenCode
- vite.config.ts: Add proxy entries for OpenCode API paths
- AIChat.tsx: Revert to iframe approach now that proxying works
- ThinkingBlock.tsx: Make collapsible for better UX
The iframe approach doesn't work because OpenCode's frontend tries to
connect to its backend at the page origin, which is Pulse instead of
OpenCode. The API/WebSocket calls fail.
Switch to using the custom Chat component which communicates with
OpenCode through Pulse's backend API (/api/ai/...), providing a
native Pulse experience with proper streaming and tool execution.
OpenCode's HTML uses absolute paths like /assets/... for static files.
When embedded in Pulse's iframe, these paths don't go through the
/opencode/ proxy and fail to load.
Modified the proxy's ModifyResponse to rewrite src="/" and href="/"
attributes in HTML responses to include the /opencode/ prefix, ensuring
all assets load correctly through the proxy.
The OpenCode reverse proxy now properly modifies response headers to
allow iframe embedding within Pulse's AI panel:
- ai_handler.go: Add ModifyResponse to strip X-Frame-Options and modify
CSP frame-ancestors from OpenCode's responses
- security.go: Skip frame-related security headers for /opencode/ paths
since the proxy manages its own headers
This fixes the "refused to connect" error when opening the AI sidebar.
OpenCode handles session management internally through its embedded UI.
Disable the Pulse-side session sync to prevent 404 errors from calling
non-existent endpoints.
The aiChatStore still handles:
- Panel open/close state
- Context management for investigate buttons
- Initial prompt handling
Replace custom Chat components with an iframe that embeds OpenCode's
native web UI. This provides a more polished experience and automatically
benefits from OpenCode improvements.
Changes:
- Add reverse proxy for /opencode/ route to OpenCode's web server
- Simplify AIChat component to iframe wrapper with header
- Add GetBaseURL() method to OpenCode service
- Configure Vite proxy for development
The Pulse Pro value proposition is now: managed OpenCode deployment
with rich MCP tools that provide infrastructure context.
Update the "Kiosk / Dashboard" token preset description to mention
the ?kiosk=1 URL parameter that hides navigation and filter panels,
making it clearer how to set up a wall display.
Addresses feedback from #1102
Allow users to set custom disk usage thresholds per mounted filesystem
on host agents, rather than applying a single threshold to all volumes.
This addresses NAS/NVR use cases where some volumes (e.g., NVR storage)
intentionally run at 99% while others need strict monitoring.
Backend:
- Check for disk-specific overrides before using HostDefaults.Disk
- Override key format: host:<hostId>/disk:<mountpoint>
- Support both custom thresholds and disable per-disk
Frontend:
- Add 'hostDisk' resource type
- Add "Host Disks" collapsible section in Thresholds → Hosts tab
- Group disks by host for easier navigation
Closes#1103
The audit logging feature was showing the UI for Pro users but the
SQLiteLogger was never actually initialized - it fell back to the
ConsoleLogger which only writes to console and returns empty arrays
for queries.
This fix:
- Adds initAuditLoggerIfLicensed() helper to license_handlers.go
- Calls it when loading a persisted license at startup
- Calls it when activating a new license via API
- Creates SQLiteLogger with 90-day default retention when audit_logging
feature is enabled
The audit.db will be created in {dataDir}/audit/ when Pro is licensed.
The upgrade banners were showing briefly even for Pro users because
hasFeature() returned false before the license status was loaded.
Now we check licenseLoaded() before showing the upgrade CTA.
The previous fix (4090d981) addressed memory reporting for Docker agents
running in LXC containers, but the same issue also affects host agents.
When gopsutil runs inside an LXC, it can read Total and Free memory
from cgroup limits, but reports 0 for Used memory.
Added the same Total - Free fallback calculation to the host agent
processing path, which populates the Hosts tab.
Fixes#1075
Users should be able to configure alert thresholds, notifications, and
schedules regardless of whether alert notifications are enabled. The
enable/disable toggle only controls whether notifications are sent,
not whether users can access the configuration.
Previously, when alerts were in 'pending_review' or 'snoozed' state,
all configuration tabs were disabled, forcing users to the Overview
tab only.
Fixes#1101
The Grouped/List toggle buttons in DockerFilter were updating the
external groupingMode state, but DockerUnifiedTable had its own
internal sortKey state for determining whether to show grouped vs
flat view. These two states were completely disconnected.
Added groupingMode prop to DockerUnifiedTable and a createEffect to
sync the external state with the internal sort state.
Fixes#1100