The saveEdit function was not preserving backup and snapshot fields from
existing overrides when saving threshold changes. This caused custom
backup age thresholds to reset to 0 after any edit.
Changes:
- Added existingOverrideCheck lookup before creating new override
- Include backup/snapshot in hasStateOnlyOverride check to prevent
accidental removal of backup-only overrides
- Preserve both fields when creating/updating overrides
DSM data scrubbing causes RAID arrays to enter a 'check' state with
RebuildPercent > 0, which was incorrectly triggering rebuild warnings.
Now distinguishes between:
- 'check' state: scheduled data scrubbing (no alert)
- 'recover'/'resync' state: actual rebuild (warning alert)
- 'clean' state with RebuildPercent: scrub in progress (no alert)
SolidJS requires reactive comparisons inside For loops to use getter
functions. Changed isActive from a static value to a getter function
in both SettingsSectionNav.tsx and Settings.tsx mobile tabs.
- AI Cleanup: Remove legacy AI Chat components and context indicators
- Table Layouts: Implement fixed table layout for Dashboard and Docker tables
- Styles: Fix column shifting and add explicit width support
- Add comprehensive tests for internal/api/config_handlers.go (Phases 1-3)
- Improve test coverage for AI tools, chat service, and session management
- Enhance alert and notification tests (ResolvedAlert, Webhook)
- Add frontend unit tests for utils (searchHistory, tagColors, temperature, url)
- Add proximity client API tests
- Constrain count columns (Temp, VMs, CTs, Storage, Disks, Backups) to fixed widths
- Make CPU/Memory/Disk columns flexible with min-width 140px and max-width 180px
- Fix Uptime column to 80px to give more space to Node column
- Use table-layout: fixed for consistent column sizing
This prevents the count columns from expanding unnecessarily and
gives more space to the Node name and metric progress bars.
The UnifiedNodeSelector is the main dashboard content that users want
to display on wall-mounted screens. Only the filter/search UI should
be hidden in kiosk mode, not the actual data tables.
- Hide header branding (logo/title) in kiosk mode
- Hide navigation tabs in kiosk mode
- Hide footer in kiosk mode
- Make Dashboard/Docker/Hosts filter panels reactive to kiosk toggle
- Hide UnifiedNodeSelector in kiosk mode on Dashboard
The kiosk toggle button now properly hides all chrome for clean
dashboard display on wall-mounted screens. Related to #917
Closes#1122
Adds a kiosk mode toggle button in the header next to the logout button.
Previously, users had to manually add ?kiosk=1 to the URL to enable
kiosk mode (which hides navigation and filters for wall displays).
Now users can:
- Click the toggle button to enter/exit kiosk mode instantly
- Button shows maximize icon when off, minimize icon when on
- Hover reveals 'Kiosk' or 'Exit' label
- Blue highlight when active for clear visual feedback
- State persists in sessionStorage like the URL param method
Implementation:
- Added setKioskMode() and subscribeToKioskMode() to url.ts for
programmatic control and reactive state tracking
- Added toggle button in AppLayout header with lucide icons
- Uses same sessionStorage key as URL param approach for compatibility
Closes#1115 (discussion feedback)
Two API consistency issues reported by @FabienD74:
1. Version format mismatch in /api/version:
- currentVersion: "5.0.16" (no prefix)
- latestVersion: "v5.0.16" (with prefix)
Fixed: LatestVersion now strips the "v" prefix to match CurrentVersion format.
2. Guest ID separator inconsistency:
- Some code used colons: "instance:node:vmid"
- BuildGuestKey used dashes: "instance-node-vmid"
Fixed: BuildGuestKey now uses colon separator matching the canonical
format used by makeGuestID in the monitoring package. The existing
legacy migration in GetWithLegacyMigration handles old dash-format
entries in guest_metadata.json.
Adds .air.toml for air-verse/air hot-reload during development:
- Watches Go files in cmd/, internal/, pkg/
- Excludes tests, vendor, node_modules, frontend
- Automatically rebuilds on file changes
- Supports Pro build detection via HOT_DEV_USE_PRO env var
- Kill delay and graceful shutdown configured
approval/store.go:
- Make Approve() idempotent - return success if already approved
- Handles double-clicks and race conditions gracefully
auth.go:
- Add dev mode admin bypass (disabled by default)
- When ALLOW_ADMIN_BYPASS=1, sets X-Authenticated-User header
server.go:
- Call router.StopOpenCodeAI() during shutdown
- Ensures AI service stops cleanly on server termination
- Rename findings_mcp_adapter.go -> findings_tools_adapter.go
- Update imports from mcp to tools package
- Add findings_tools_adapter_test.go with basic tests
- Add SetChatPatrol method as alias for SetOpenCodePatrol
(maintains API compatibility during transition)
Adapts API handlers to use the new native chat service:
ai_handler.go:
- Replace opencode.Service with chat.Service
- Add AIService interface for testability
- Add factory function for service creation (mockable)
- Update provider wiring to use tools package types
ai_handlers.go:
- Add Notable field to model list response
- Simplify command approval - execution handled by agentic loop
- Remove inline command execution from approval endpoint
router.go:
- Update imports: mcp -> tools, opencode -> chat
- Add monitor wrapper types for cleaner dependency injection
- Update patrol wiring for new chat service
agent_profiles:
- Rename agent_profiles_mcp.go -> agent_profiles_tools.go
- Update imports for tools package
monitor_wrappers.go:
- New file with wrapper types for alert/notification monitors
- Enables interface-based dependency injection
Adapts frontend to work with the new native chat service:
Model selector improvements:
- Extract ModelSelector to reusable component
- Add notable model filtering (shows recent models by default)
- Add "Show older models" toggle for legacy model access
- Add notable badge indicator for recommended models
Chat flow changes:
- Simplify approval handling - backend agentic loop executes commands
- Tool results now come via stream events, not approval response
- Session model selection preserved when switching sessions
Type updates:
- Add 'notable' field to ModelInfo interface
- Add 'notable' to API response types
The opencode package implemented the old architecture where:
- Pulse ran an OpenCode subprocess as a sidecar
- AI messages were proxied through OpenCode
- OpenCode connected back to Pulse's MCP server for tool access
This is now replaced by the native chat service (internal/ai/chat)
which calls AI providers directly and executes tools inline.
Removed files:
- sidecar.go: OpenCode process management
- service.go: Message proxying and session management
- client.go: HTTP client for OpenCode API
- bridge.go: MCP tool bridging
- patrol.go: Patrol context extraction
- *_test.go: Associated tests
Benefits of new architecture:
- No external subprocess dependency
- Direct streaming from AI providers
- Lower latency (no proxy hop)
- Simpler deployment
This refactoring removes the MCP (Model Context Protocol) server layer and
converts AI tools to be called directly by the chat service.
Key changes:
- Rename package from internal/ai/mcp to internal/ai/tools
- Remove server.go - tools no longer exposed via MCP server
- Tools are now called directly by the chat service via ExecuteTool()
New tools added:
- Kubernetes: clusters, nodes, pods, deployments (4 tools)
- PMG: mail gateway status, mail stats, queues, spam stats (4 tools)
- Infrastructure: snapshots, PBS jobs, backup tasks, network stats,
disk I/O, cluster status, swarm, services, tasks, recent tasks,
physical disks, RAID status, host Ceph, resource disks (14 tools)
- Patrol: connection health, resolved alerts (2 tools)
Test coverage:
- Added comprehensive test files for adapters, infrastructure,
patrol, profiles, and query tools
Total tools: 50 (was ~25)
Replace the OpenCode sidecar with a native chat service that handles:
- Real-time streaming responses from AI providers
- Multi-turn conversation sessions with history
- Tool execution with automatic function calling
- Agentic workflows for autonomous task completion
- Patrol integration for automated health analysis
The chat service directly communicates with AI providers using the
new StreamingProvider interface, eliminating the need for an external
sidecar process. Sessions are managed in-memory with configurable
history limits.
Key components:
- service.go: Main chat service with provider integration
- session.go: Session management and message history
- agentic.go: Agentic loop for autonomous tool execution
- patrol.go: Patrol-specific chat context and analysis
- tools.go: Tool execution bridge to tools package
- types.go: Chat message and event type definitions
- Add ChatStream method to all providers (Anthropic, OpenAI, Gemini, Ollama)
for real-time streaming of AI responses with tool call support
- Add StreamingProvider interface with StreamEvent types for content,
thinking, tool_start, tool_end, done, and error events
- Add notable models feature that fetches model metadata from models.dev
to identify recent/recommended models (within last 3 months)
- Add Notable field to ModelInfo struct to flag "latest and greatest" models
- Add SupportsThinking method to check for extended reasoning capability
The streaming support enables real-time AI chat responses instead of
waiting for complete responses. The notable models feature helps users
identify which models are current and recommended.
Adds a Mention field to webhook configurations that allows users to tag
individuals or groups when alerts are sent. This works with:
- Discord: @everyone, <@USER_ID>, <@&ROLE_ID>
- Microsoft Teams: @General, user email
- Mattermost: @channel, @all, @username
The mention is included in the webhook payload via the {{.Mention}} template
variable. Built-in templates for Discord, Slack, and Teams now conditionally
include mentions when configured.
Backend changes:
- Add Mention field to WebhookConfig struct
- Add Mention field to WebhookPayloadData for template access
- Pass mention through sendGroupedWebhook
Frontend changes:
- Add mention field to Webhook interface
- Add Mention input to webhook configuration form
- Show service-specific help text for mention formats
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.