mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
## What changed
- Make `/brain` reuse the active Company Brain, switch to a single existing Company Brain, show a picker for multiple choices, or create one only when none exists.
- Wait for active-organization restoration before making that decision.
- Make Company Brain onboarding match organizations by the confirmed company domain and never offer unrelated-domain workspaces.
- Create a new Company Brain when no matching workspace exists and show an actionable workspace-limit toast when creation is blocked.
- Improve the organization picker, loading, and error states.
## Why
The previous flows could use or mutate the currently active normal organization, start research against stale Company Brain metadata, or offer unrelated Company Brain workspaces after a quota failure.
## Impact
Normal organizations are no longer silently converted. Research is scoped to the Company Brain for the confirmed domain, and users receive a clear recovery path when they reach their workspace limit.
This keeps the existing `{ domain }` research API contract; no organization-reconfiguration API is required.
## Validation
- Biome checks passed for all five changed web files
- Targeted web TypeScript diagnostics reported no errors for the changed files
- React Doctor against `origin/main`: no issues found
- `git diff --check`
No test files were added.
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
import {
|
|
getBrainMode,
|
|
getBrainWorkspaceDomain,
|
|
getCompanyBrainOverride,
|
|
hasCompanyBrain,
|
|
} from "./billing-utils"
|
|
|
|
export type BrainEntryOrganization = {
|
|
id: string
|
|
name: string
|
|
slug: string
|
|
metadata?: Record<string, unknown> | string | null
|
|
}
|
|
|
|
export type CompanyBrainEntryDecision =
|
|
| { action: "use"; organization: BrainEntryOrganization }
|
|
| { action: "switch"; organization: BrainEntryOrganization }
|
|
| { action: "choose"; organizations: BrainEntryOrganization[] }
|
|
| { action: "create" }
|
|
|
|
export function isCompanyBrainOrganization(
|
|
organization: BrainEntryOrganization,
|
|
): boolean {
|
|
const override = getCompanyBrainOverride(organization.metadata)
|
|
if (override !== undefined) return override
|
|
return (
|
|
hasCompanyBrain(organization.metadata) ||
|
|
getBrainMode(organization.metadata) === "team"
|
|
)
|
|
}
|
|
|
|
export function getCompanyBrainOrganizations(
|
|
organizations: BrainEntryOrganization[],
|
|
): BrainEntryOrganization[] {
|
|
return organizations.filter(isCompanyBrainOrganization)
|
|
}
|
|
|
|
function normalizeDomain(domain: string): string {
|
|
return domain
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/^https?:\/\//, "")
|
|
.replace(/^www\./, "")
|
|
.replace(/\/.*$/, "")
|
|
}
|
|
|
|
export function resolveCompanyBrainEntry(
|
|
activeOrganizationId: string | null | undefined,
|
|
organizations: BrainEntryOrganization[],
|
|
requestedDomain?: string,
|
|
): CompanyBrainEntryDecision {
|
|
const normalizedRequestedDomain = requestedDomain
|
|
? normalizeDomain(requestedDomain)
|
|
: null
|
|
const companyBrains = getCompanyBrainOrganizations(organizations).filter(
|
|
(organization) => {
|
|
if (!normalizedRequestedDomain) return true
|
|
const organizationDomain = getBrainWorkspaceDomain(organization.metadata)
|
|
return (
|
|
organizationDomain !== null &&
|
|
normalizeDomain(organizationDomain) === normalizedRequestedDomain
|
|
)
|
|
},
|
|
)
|
|
const active = organizations.find(
|
|
(organization) => organization.id === activeOrganizationId,
|
|
)
|
|
if (
|
|
active &&
|
|
companyBrains.some((organization) => organization.id === active.id)
|
|
) {
|
|
return { action: "use", organization: active }
|
|
}
|
|
if (companyBrains.length === 1 && companyBrains[0]) {
|
|
return { action: "switch", organization: companyBrains[0] }
|
|
}
|
|
if (companyBrains.length > 1) {
|
|
return { action: "choose", organizations: companyBrains }
|
|
}
|
|
return { action: "create" }
|
|
}
|