From 8071a7b08539e2ff2497c625e37f03232f8fd031 Mon Sep 17 00:00:00 2001 From: vorflux <249966464+vorflux@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:13:59 +0000 Subject: [PATCH] Add Nova workspace prompt settings (#1323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a dedicated Workspace Prompt editor for Company Brain organizations while preserving the existing Organization Context ingestion-filter controls for every organization manager. ## Changes - Keeps Organization Context byte-for-byte unchanged and available independently to all organization managers. - Adds Workspace Prompt as a separate Company-Brain-only section below it, using the established settings styling and contextual divider. - Describes Workspace Prompt as persistent guidance that can shape operating preferences, priorities, source/tool choices, workflows, terminology, formatting, and communication style. - Adds nullable, 1,500-character `workspacePrompt` support to shared request, GET response, and PATCH response contracts. - Aligns PATCH validation with the real `{ orgId, orgSlug, updated }` API response. - Merges canonical `updated` settings into the submitting organization’s cache, then exactly refetches that organization. - Preserves drafts during background refetches, isolates organization switches, retains actionable errors, accessibility, empty `filterPrompt` compatibility, and `X-App-Source: nova`. ## Testing - Passed focused Biome checks on all changed files. - Passed `packages/lib` and `packages/validation` TypeScript checks. - Verified GET/PATCH settings response contracts, partial/null/limit validation, canonical cache merge, and exact organization-bound invalidation. - Verified Organization Context remains unchanged and Workspace Prompt is separately Company-Brain/manager-gated. - Confirmed no remaining Workspace Persona identifiers. - Public preview returns HTTP 200; authenticated settings interactions remain unavailable without a saved OAuth session. - Full web type-check remains blocked by unrelated baseline diagnostics; none reference changed files. - No dedicated tests were added, per requester instruction. --- **Session Details** - Session: [View Session](https://supermemory.us1.vorflux.com/agent-sessions/7544b72b-aeca-48e2-81c3-514df21cd081) - Requested by: Soham Daga (soham@supermemory.com) - Address comments on this PR. Add `(aside)` to your comment to have me ignore it. --- apps/web/components/configure-view.tsx | 20 +- .../components/settings/workspace-prompt.tsx | 186 ++++++++++++++++++ apps/web/hooks/use-org-settings.ts | 48 ++--- packages/lib/api.ts | 17 +- packages/validation/api.ts | 2 +- packages/validation/schemas.ts | 1 + 6 files changed, 233 insertions(+), 41 deletions(-) create mode 100644 apps/web/components/settings/workspace-prompt.tsx diff --git a/apps/web/components/configure-view.tsx b/apps/web/components/configure-view.tsx index 0b75762e..c36ea5e4 100644 --- a/apps/web/components/configure-view.tsx +++ b/apps/web/components/configure-view.tsx @@ -1,15 +1,21 @@ "use client" import { cn } from "@lib/utils" -import { Blocks, CalendarClock, Cpu } from "lucide-react" +import { Blocks, CalendarClock, Cpu, ScrollText } from "lucide-react" import { useState } from "react" import CompanyBrainConnections from "@/components/settings/company-brain-connections" import CompanyBrainModels from "@/components/settings/company-brain-models" import Proactiveness from "@/components/settings/proactiveness" +import { WorkspacePrompt } from "@/components/settings/workspace-prompt" import { ErrorBoundary } from "@/components/error-boundary" +import { useAuth } from "@lib/auth-context" import { dmSans125ClassName } from "@/lib/fonts" -type ConfigureSection = "company-brain" | "models" | "automations" +type ConfigureSection = + | "company-brain" + | "models" + | "workspace-prompt" + | "automations" const SECTIONS: { id: ConfigureSection @@ -31,6 +37,13 @@ const SECTIONS: { "Pick how fast or thorough your brain should be. Fine-tune each task under Advanced.", icon: Cpu, }, + { + id: "workspace-prompt", + label: "Workspace Prompt", + description: + "Persistent guidance for how your brain works across the workspace. Fixed safety, access, and approval constraints still apply.", + icon: ScrollText, + }, { id: "automations", label: "Automations", @@ -41,6 +54,7 @@ const SECTIONS: { ] export function ConfigureView() { + const { org } = useAuth() const [activeSection, setActiveSection] = useState("company-brain") const active = SECTIONS.find((section) => section.id === activeSection) @@ -118,6 +132,8 @@ export function ConfigureView() { ) : activeSection === "models" ? ( + ) : activeSection === "workspace-prompt" ? ( + ) : ( )} diff --git a/apps/web/components/settings/workspace-prompt.tsx b/apps/web/components/settings/workspace-prompt.tsx new file mode 100644 index 00000000..18e30d5e --- /dev/null +++ b/apps/web/components/settings/workspace-prompt.tsx @@ -0,0 +1,186 @@ +"use client" + +import { LoaderIcon } from "lucide-react" +import { useState } from "react" +import { useHasCompanyBrain } from "@/hooks/use-company-brain" +import { useOrgMemberRole } from "@/hooks/use-org-member-role" +import { useOrgSettings, useUpdateOrgSettings } from "@/hooks/use-org-settings" +import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts" +import { cn } from "@lib/utils" + +const DESCRIPTION_ID = "workspace-prompt-description" +const COUNTER_ID = "workspace-prompt-counter" +const HEADING_ID = "workspace-prompt-heading" + +function SectionHeading({ children }: { children: React.ReactNode }) { + return ( +

+ {children} +

+ ) +} + +function PromptHeader() { + return ( +
+ Workspace Prompt +

+ Set persistent guidance for how Company Brain works across your + workspace. +

+
+ ) +} + +export function WorkspacePrompt({ + showHeading = true, +}: { + showHeading?: boolean +}) { + const isCompanyBrain = useHasCompanyBrain() + const { isAdmin } = useOrgMemberRole(isCompanyBrain) + const settingsQuery = useOrgSettings() + const updateSettings = useUpdateOrgSettings() + const [draft, setDraft] = useState(null) + + const savedPrompt = settingsQuery.data?.workspacePrompt ?? "" + const prompt = draft ?? savedPrompt + const dirty = draft !== null && draft.trim() !== savedPrompt.trim() + const canClear = !dirty && savedPrompt.length > 0 && isAdmin + + const handleSave = () => { + updateSettings.mutate( + { + workspacePrompt: prompt.trim() ? prompt.trim() : null, + }, + { onSuccess: () => setDraft(null) }, + ) + } + + if (!isCompanyBrain) return null + + return ( +
+ {showHeading ? : null} + + {settingsQuery.isLoading ? ( + + + ) : settingsQuery.isError ? ( +
+

+ Workspace prompt could not be loaded. +

+ +
+ ) : ( +
+