Files
multica/server/internal/featureflags/keys.go
Naiyuan Qing b5992c4454 fix(featureflags): publish the hang stack capture flag to clients (MUL-5345) (#6079)
#6026 shipped the desktop half of hang stack capture fail-closed: the renderer
only enables it when `desktop_hang_stack_capture` arrives from `/api/config` as
an explicit true. The server half was missing — the key was never added to
`frontendPublicFlags`, so `/api/config` never published it and the client had
nothing to receive.

The effect in production is that capture has been inert since release. 0.4.13
recorded two real hard hangs in its first 14 hours and both came back with
empty `stack_*` fields, which reads like "nobody turned the flag on" but is not
fixable that way: with the key absent from the published set, enabling it in
the flag service would not have reached any client.

Publishing it changes no behaviour on its own — the default is off, and the
desktop side still requires an explicit true — it only makes the switch
reachable.

Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 13:37:53 +08:00

62 lines
2.5 KiB
Go

package featureflags
import (
"context"
"github.com/multica-ai/multica/server/pkg/featureflag"
)
const (
// ComposioMCPApps gates the Composio app management UI and — together with
// the MUL-3963 permission_mode / invocation_targets access model it depends
// on — the aligned Private / Public-to picker in the agent create flow.
// The access model exists to gate Composio sharing, so the two ship on the
// same switch.
ComposioMCPApps = "composio_mcp_apps"
// ResourceLabels controls the agent- and skill-scoped label namespaces.
// Issue labels remain available while this release flag is off.
ResourceLabels = "settings_resource_labels"
// DesktopHangStackCapture gates reading a JS call stack out of a hung
// desktop renderer (MUL-5345). Capture holds a debugger channel open on
// every renderer, so the desktop client is fail-closed: it stays off unless
// this key arrives as an explicit true. That makes publishing the key here
// mandatory — a key the client never receives can never be turned on.
DesktopHangStackCapture = "desktop_hang_stack_capture"
// agentBuilderCompat is no longer a release flag. Keep publishing the key
// as enabled so installed desktop clients that still gate the AI creation
// entry on this config decision receive the permanently enabled behavior.
agentBuilderCompat = "agents_agent_builder"
// agentSkillTogglesCompat is no longer a release flag. Keep publishing the
// key as enabled so installed v0.4.0 desktop clients, which still gate the
// switch on this config decision, receive the permanently enabled behavior.
agentSkillTogglesCompat = "agents_skill_toggles"
)
var frontendPublicFlags = []string{
ComposioMCPApps,
ResourceLabels,
DesktopHangStackCapture,
}
func ComposioMCPAppsEnabled(ctx context.Context, flags *featureflag.Service) bool {
return flags.IsEnabled(ctx, ComposioMCPApps, false)
}
func ResourceLabelsEnabled(ctx context.Context, flags *featureflag.Service) bool {
return flags.IsEnabled(ctx, ResourceLabels, false)
}
func DesktopHangStackCaptureEnabled(ctx context.Context, flags *featureflag.Service) bool {
return flags.IsEnabled(ctx, DesktopHangStackCapture, false)
}
func EvaluateFrontendPublicFlags(ctx context.Context, flags *featureflag.Service) map[string]bool {
out := make(map[string]bool, len(frontendPublicFlags)+2)
for _, key := range frontendPublicFlags {
out[key] = flags.IsEnabled(ctx, key, false)
}
out[agentBuilderCompat] = true
out[agentSkillTogglesCompat] = true
return out
}