Implements Phase 1-2 of multi-tenancy support using a directory-per-tenant
strategy that preserves existing file-based persistence.
Key changes:
- Add MultiTenantPersistence manager for org-scoped config routing
- Add TenantMiddleware for X-Pulse-Org-ID header extraction and context propagation
- Add MultiTenantMonitor for per-tenant monitor lifecycle management
- Refactor handlers (ConfigHandlers, AlertHandlers, AIHandlers, etc.) to be
context-aware with getConfig(ctx)/getMonitor(ctx) helpers
- Add Organization model for future tenant metadata
- Update server and router to wire multi-tenant components
All handlers maintain backward compatibility via legacy field fallbacks
for single-tenant deployments using the "default" org.
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
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
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
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.
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.