mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 22:12:23 +00:00
7cb45489e8
Introduce the single privacy dial that will govern what infrastructure context cloud models may see, replacing the binary share_operational_context_with_cloud toggle as the canonical operator control. This increment adds and surfaces the setting; it does not change the redaction seam (that is increment 2). Config (internal/config/ai.go): add AIConfig.CloudContextPrivacy with the full|redacted|local_only levels, default "full", plus NormalizeCloudContextPrivacy and the nil-safe GetCloudContextPrivacy getter. NewDefaultAIConfig defaults a fresh self-hosted install to "full" so the Assistant answers with real resource detail out of the box. The legacy ShareOperationalContextWithCloud boolean is retained as the field the redaction seam still reads until it is wired into the dial directly. Migration (internal/config/persistence.go): LoadAIConfig derives the dial from the legacy toggle for pre-dial configs (legacy on -> full, off/absent -> redacted) and persists it, leaving the legacy boolean untouched so existing installs keep their current cloud behavior byte-for-byte. Fresh installs (no config file) default to full. API (internal/api/ai_handlers.go): round-trip cloud_context_privacy through /api/settings/ai field-by-field like discovery_enabled. The response always serializes GetCloudContextPrivacy() (no omitempty) so the UI binds a 3-option control to the concrete value; the update request carries an optional *string validated against NormalizeCloudContextPrivacy (unknown values -> 400). When the dial is provided it supersedes and re-syncs the legacy boolean (full -> true, redacted/local_only -> false) so the existing seam honors the dial's full/redacted axis without new redaction code paths. Frontend: replace the binary "Share operational context with cloud models" toggle with a "Cloud model privacy" 3-option FormSelect in AIRuntimeControlsSection.tsx, bound to state.form.cloudContextPrivacy and the cloud_context_privacy payload via useAISettingsState. CloudContextPrivacy type + payload fields in types/ai.ts; label/help/option/summary copy in aiSettingsPresentation.ts. Governance (ai-runtime + frontend-primitives substantive deltas; dependent api-contracts, agent-lifecycle, storage-recovery notes): the contracts now name the dial as canonical with the legacy boolean as the synced/migrated seam field. Proofs: ai_config_test.go (getter/normalize/default), persistence_ai_test.go (migration cases), ai_handlers_test.go (round-trip + legacy sync + 400), contract_test.go JSON snapshots, settingsArchitecture + aiSettingsPresentation tests. Live-verified in the preview drawer: dial renders with all three levels, the migrated value (redacted) is selected, the summary updates reactively, and an end-to-end UI save round-trips full (legacy sync true) then restores redacted.