feat: implement ensure_publication for zero_publication management

- Added the ensure_publication function to create and verify the zero_publication if it is missing, ensuring idempotency during database initialization.
- Integrated ensure_publication into the create_db_and_tables function to prevent zero-cache crash loops on startup.
- Introduced a self-check script to validate the ensure_publication functionality on a create_all-bootstrapped database.
- Updated various components to reflect the transition from search space to workspace, including adjustments in imports and routing paths.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-07-05 23:17:13 -07:00
parent 7562bc78ee
commit a64c8205fe
164 changed files with 626 additions and 506 deletions

View file

@ -1,8 +1,23 @@
<agent_identity> <agent_identity>
You are **SurfSense's main agent**. Your job is to answer the user using their You are **SurfSense's main agent**, the orchestrator of an open-source
knowledge base, lightweight web research, persistent memory, and **specialist competitive intelligence platform. Users come to you to understand their
subagents** invoked via the `task` tool. You are an orchestrator — most market: what competitors are doing, how audiences react, where rankings and
non-trivial work belongs on a specialist. reviews are moving, and what is being said across the open web — and to put
that intelligence to work alongside their own knowledge base.
You do this by dispatching **specialist subagents** via the `task` tool:
- **Live market data** — Reddit, YouTube, Google Maps, Google Search, and the
web crawler return structured, current platform data (posts, comments,
transcripts, reviews, SERPs, full page content).
- **The user's own context** — their knowledge base, connected apps, and
persistent memory.
- **Deliverables** — reports, podcasts, and presentations built from what the
specialists find.
You are an orchestrator — most non-trivial work belongs on a specialist. Your
value is routing each request to the right specialist, synthesizing evidence
across sources, and answering with what the data shows rather than what you
assume.
Today (UTC): {resolved_today} Today (UTC): {resolved_today}
</agent_identity> </agent_identity>

View file

@ -1,8 +1,23 @@
<agent_identity> <agent_identity>
You are **SurfSense's main agent**. Your job is to answer the user using their You are **SurfSense's main agent**, the orchestrator of an open-source
shared team knowledge base, lightweight web research, persistent memory, and competitive intelligence platform. This team comes to you to understand its
**specialist subagents** invoked via the `task` tool. You are an orchestrator market: what competitors are doing, how audiences react, where rankings and
— most non-trivial work belongs on a specialist. reviews are moving, and what is being said across the open web — and to put
that intelligence to work alongside the team's shared knowledge base.
You do this by dispatching **specialist subagents** via the `task` tool:
- **Live market data** — Reddit, YouTube, Google Maps, Google Search, and the
web crawler return structured, current platform data (posts, comments,
transcripts, reviews, SERPs, full page content).
- **The team's own context** — its shared knowledge base, connected apps, and
persistent team memory.
- **Deliverables** — reports, podcasts, and presentations built from what the
specialists find.
You are an orchestrator — most non-trivial work belongs on a specialist. Your
value is routing each request to the right specialist, synthesizing evidence
across sources, and answering with what the data shows rather than what you
assume.
Today (UTC): {resolved_today} Today (UTC): {resolved_today}

View file

@ -1,5 +1,11 @@
<knowledge_base_first> <knowledge_base_first>
CRITICAL — ground factual answers in what you actually receive this turn: CRITICAL — ground factual answers in what you actually receive this turn:
- **live platform data** via the market specialists —
`task(reddit, ...)`, `task(youtube, ...)`, `task(google_maps, ...)`,
`task(google_search, ...)`, `task(web_crawler, ...)`. Anything about
competitors, markets, rankings, reviews, or audience sentiment is answered
from what these return **this turn**, never from your training data: your
general knowledge of companies, prices, and rankings is stale by definition,
- the user's knowledge base via `task(knowledge_base, ...)` (your PRIMARY - the user's knowledge base via `task(knowledge_base, ...)` (your PRIMARY
source for anything about their own uploaded files, documents, and notes — source for anything about their own uploaded files, documents, and notes —
the `<workspace_tree>` only lists what exists, so delegate to the specialist the `<workspace_tree>` only lists what exists, so delegate to the specialist
@ -7,9 +13,6 @@ CRITICAL — ground factual answers in what you actually receive this turn:
- injected workspace context (see `<dynamic_context>`), - injected workspace context (see `<dynamic_context>`),
- the user's connected apps via `task(mcp_discovery, ...)` (Slack, Jira, - the user's connected apps via `task(mcp_discovery, ...)` (Slack, Jira,
Notion, Gmail, Calendar, etc. — live data that is NOT in the knowledge base), Notion, Gmail, Calendar, etc. — live data that is NOT in the knowledge base),
- results from your specialist calls — the web crawler via
`task(web_crawler, ...)` or the Google Search specialist via
`task(google_search, ...)`,
- or substantive summaries returned by a `task` specialist you invoked. - or substantive summaries returned by a `task` specialist you invoked.
For questions about the user's own files and notes, dispatch For questions about the user's own files and notes, dispatch

View file

@ -1,4 +1,5 @@
<reminder> <reminder>
Concise · KB-grounded · delegation-first · one `task` per turn · no direct Concise · grounded in this turn's specialist data, never stale general
filesystem · persist memory when durable facts appear. knowledge · delegation-first · no direct filesystem · persist memory when
durable facts appear.
</reminder> </reminder>

View file

@ -27,6 +27,17 @@ can retrieve — retrieve them, then answer with the facts and cite the page.
Large results are fine: extract and return them, don't ask permission for Large results are fine: extract and return them, don't ask permission for
bounded fan-out (≤20 sites) the user already requested. bounded fan-out (≤20 sites) the user already requested.
**Audience sentiment lives on the platforms.** What people *say and feel*
about a brand, product, or topic is answered from the platform where they
say it — `task(reddit, …)` for community discussion and threads,
`task(youtube, …)` for video content, transcripts, and comment sections,
`task(google_maps, …)` for customer reviews of physical businesses. Web
search only finds articles *about* the conversation; the platform
specialists return the conversation itself, structured and current. For
competitive questions ("what are people saying about X", "how is Y
reviewed", "monitor Z"), go to the platform specialists first and cite
what they return.
**Places go to Maps, the open web goes to Search.** Discovering physical **Places go to Maps, the open web goes to Search.** Discovering physical
businesses or venues of a type in a geography ("clinics in X", "tutoring businesses or venues of a type in a geography ("clinics in X", "tutoring
centers near Y", lead lists of local businesses) is the Maps specialist's centers near Y", lead lists of local businesses) is the Maps specialist's
@ -113,6 +124,20 @@ user: "What did Maya say about the Q2 roadmap in Slack last week?"
timestamp.") timestamp.")
</example> </example>
<example>
user: "What are people saying about Cursor vs Windsurf lately?"
→ Audience sentiment — go to the platform, not web search. Independent
sources, so parallel `task` calls:
task(reddit, "Search Reddit for recent discussion comparing Cursor and
Windsurf (past month, sort by top). Return the strongest quotes with
subreddit, score, and post URL, and summarise which way sentiment
leans and why.")
task(youtube, "Find recent YouTube videos comparing Cursor and Windsurf.
For the top results return title, channel, views, publish date, and
the main takeaways from each (use subtitles where available).")
Then synthesise both into one answer, attributing claims to their source.
</example>
<example> <example>
user: "What's the current USD/INR rate?" user: "What's the current USD/INR rate?"
→ Public web lookup — delegate to the Google Search specialist: → Public web lookup — delegate to the Google Search specialist:

View file

@ -3041,6 +3041,11 @@ async def create_db_and_tables():
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector")) await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm")) await conn.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm"))
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
# create_all never creates zero_publication (a migration-only
# artifact), and without it zero-cache crash-loops. Idempotent.
from app.zero_publication import ensure_publication
await conn.run_sync(ensure_publication)
await setup_indexes() await setup_indexes()

View file

@ -173,6 +173,37 @@ def apply_publication(conn: Connection) -> None:
conn.execute(text(build_set_table_sql(conn))) conn.execute(text(build_set_table_sql(conn)))
def ensure_publication(conn: Connection) -> None:
"""Create ``zero_publication`` if missing, then reconcile if its shape drifted.
Startup-bootstrap counterpart of migration 116: databases created via
``Base.metadata.create_all`` (dev/test, ``DB_BOOTSTRAP_ON_STARTUP=TRUE``)
never run migrations, so without this zero-cache crash-loops on
``Unknown or invalid publications``. Idempotent: when the publication
already matches the canonical shape no DDL is emitted, so a normal boot
fires no event triggers and never disturbs a running zero-cache.
"""
exists = conn.execute(
text("SELECT 1 FROM pg_publication WHERE pubname = :name"),
{"name": PUBLICATION_NAME},
).fetchone()
if not exists:
# Seed with one table; the reconcile below sets the full canonical
# shape. CREATE PUBLICATION is safe here (unlike in migrations, see
# 116_create_zero_publication.py): the publication does not exist, so
# no zero-cache replica can be attached to it yet.
conn.execute(
text(
f"CREATE PUBLICATION {_quote_identifier(PUBLICATION_NAME)} "
"FOR TABLE notifications"
)
)
if verify_publication(conn):
conn.execute(text(build_set_table_sql(conn)))
def _actual_publication_shape(conn: Connection) -> dict[str, list[str] | None]: def _actual_publication_shape(conn: Connection) -> dict[str, list[str] | None]:
rows = conn.execute( rows = conn.execute(
text( text(

View file

@ -0,0 +1,46 @@
"""Self-check for ensure_publication on a create_all-bootstrapped scratch DB."""
import asyncio
import asyncpg
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
SCRATCH_DB = "surfsense_zero_pub_check"
ADMIN_DSN = "postgresql://postgres:postgres@localhost:5432/postgres"
SCRATCH_URL = f"postgresql+asyncpg://postgres:postgres@localhost:5432/{SCRATCH_DB}"
async def main() -> None:
admin = await asyncpg.connect(ADMIN_DSN)
await admin.execute(f'DROP DATABASE IF EXISTS "{SCRATCH_DB}" WITH (FORCE)')
await admin.execute(f'CREATE DATABASE "{SCRATCH_DB}"')
await admin.close()
from app.db import Base
from app.zero_publication import ensure_publication, verify_publication
engine = create_async_engine(SCRATCH_URL)
try:
async with engine.begin() as conn:
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm"))
await conn.run_sync(Base.metadata.create_all)
await conn.run_sync(ensure_publication)
mismatches = await conn.run_sync(verify_publication)
assert not mismatches, f"shape wrong after ensure: {mismatches}"
# Second call must be a no-op that leaves a verified shape.
await conn.run_sync(ensure_publication)
mismatches = await conn.run_sync(verify_publication)
assert not mismatches, f"shape wrong after re-ensure: {mismatches}"
finally:
await engine.dispose()
admin = await asyncpg.connect(ADMIN_DSN)
await admin.execute(f'DROP DATABASE IF EXISTS "{SCRATCH_DB}" WITH (FORCE)')
await admin.close()
print("OK: ensure_publication creates and verifies on a create_all DB, idempotently.")
asyncio.run(main())

View file

@ -156,10 +156,7 @@ export function AutomationBuilderForm({
if (mode === "edit" && automation) { if (mode === "edit" && automation) {
return { ...buildUpdatePayload(formForPayload), status: automation.status }; return { ...buildUpdatePayload(formForPayload), status: automation.status };
} }
const { workspace_id: _ignored, ...rest } = buildCreatePayload( const { workspace_id: _ignored, ...rest } = buildCreatePayload(formForPayload, workspaceId);
formForPayload,
workspaceId
);
return rest; return rest;
} }

View file

@ -51,7 +51,7 @@ export function AutomationModelFields({
errors, errors,
}: AutomationModelFieldsProps) { }: AutomationModelFieldsProps) {
const { llm, image, vision, isLoading } = useAutomationEligibleModels(); const { llm, image, vision, isLoading } = useAutomationEligibleModels();
const rolesHref = `/dashboard/${workspaceId}/search-space-settings/models`; const rolesHref = `/dashboard/${workspaceId}/workspace-settings/models`;
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">

View file

@ -13,7 +13,7 @@ import {
modelConnectionsAtom, modelConnectionsAtom,
modelRolesAtom, modelRolesAtom,
} from "@/atoms/model-connections/model-connections-query.atoms"; } from "@/atoms/model-connections/model-connections-query.atoms";
import { activeWorkspaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { DocumentUploadDialogProvider } from "@/components/assistant-ui/document-upload-popup"; import { DocumentUploadDialogProvider } from "@/components/assistant-ui/document-upload-popup";
import { LayoutDataProvider } from "@/components/layout"; import { LayoutDataProvider } from "@/components/layout";
import { OnboardingTour } from "@/components/onboarding-tour"; import { OnboardingTour } from "@/components/onboarding-tour";

View file

@ -1,10 +1,6 @@
import { TeamContent } from "./team-content"; import { TeamContent } from "./team-content";
export default async function TeamPage({ export default async function TeamPage({ params }: { params: Promise<{ workspace_id: string }> }) {
params,
}: {
params: Promise<{ workspace_id: string }>;
}) {
const { workspace_id } = await params; const { workspace_id } = await params;
return ( return (

View file

@ -599,7 +599,7 @@ function MemberRow({
<DropdownMenuSeparator className="bg-popover-border" /> <DropdownMenuSeparator className="bg-popover-border" />
<DropdownMenuItem <DropdownMenuItem
onClick={() => onClick={() =>
router.push(`/dashboard/${workspaceId}/search-space-settings/team-roles`) router.push(`/dashboard/${workspaceId}/workspace-settings/team-roles`)
} }
> >
Manage Roles Manage Roles

View file

@ -6,7 +6,7 @@ import { AlertTriangle, Info, ShieldCheck, Trash2 } from "lucide-react";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom"; import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
import { activeWorkspaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { import {
AlertDialog, AlertDialog,

View file

@ -13,9 +13,9 @@ import {
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import type { SearchSpace } from "@/contracts/types/search-space.types"; import type { SearchSpace } from "@/contracts/types/workspace.types";
import { useElectronAPI } from "@/hooks/use-platform"; import { useElectronAPI } from "@/hooks/use-platform";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
export function DesktopContent() { export function DesktopContent() {
const api = useElectronAPI(); const api = useElectronAPI();

View file

@ -17,8 +17,8 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import type { SearchSpace } from "@/contracts/types/search-space.types"; import type { SearchSpace } from "@/contracts/types/workspace.types";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { authenticatedFetch } from "@/lib/auth-fetch"; import { authenticatedFetch } from "@/lib/auth-fetch";
import { buildBackendUrl } from "@/lib/env-config"; import { buildBackendUrl } from "@/lib/env-config";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";

View file

@ -11,7 +11,5 @@ export default function UserSettingsLayout({
}) { }) {
const { workspace_id } = use(params); const { workspace_id } = use(params);
return ( return <UserSettingsLayoutShell workspaceId={workspace_id}>{children}</UserSettingsLayoutShell>;
<UserSettingsLayoutShell workspaceId={workspace_id}>{children}</UserSettingsLayoutShell>
);
} }

View file

@ -76,7 +76,7 @@ export function SearchSpaceSettingsLayoutShell({
const selectedLabel = navItems.find((item) => item.value === activeTab)?.label ?? t("title"); const selectedLabel = navItems.find((item) => item.value === activeTab)?.label ?? t("title");
const hrefFor = (tab: SearchSpaceSettingsTab) => const hrefFor = (tab: SearchSpaceSettingsTab) =>
`/dashboard/${workspaceId}/search-space-settings/${tab}`; `/dashboard/${workspaceId}/workspace-settings/${tab}`;
return ( return (
<section className="flex h-full min-h-[min(680px,calc(100vh-5rem))] w-full select-none flex-col gap-6 md:pt-6 md:flex-row"> <section className="flex h-full min-h-[min(680px,calc(100vh-5rem))] w-full select-none flex-col gap-6 md:pt-6 md:flex-row">

View file

@ -6,5 +6,5 @@ export default async function SearchSpaceSettingsPage({
params: Promise<{ workspace_id: string }>; params: Promise<{ workspace_id: string }>;
}) { }) {
const { workspace_id } = await params; const { workspace_id } = await params;
redirect(`/dashboard/${workspace_id}/search-space-settings/general`); redirect(`/dashboard/${workspace_id}/workspace-settings/general`);
} }

View file

@ -6,7 +6,7 @@ import { motion } from "motion/react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { CreateSearchSpaceDialog } from "@/components/layout"; import { CreateSearchSpaceDialog } from "@/components/layout";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";

View file

@ -14,7 +14,7 @@ import { Separator } from "@/components/ui/separator";
import { ShortcutKbd } from "@/components/ui/shortcut-kbd"; import { ShortcutKbd } from "@/components/ui/shortcut-kbd";
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
import { useElectronAPI } from "@/hooks/use-platform"; import { useElectronAPI } from "@/hooks/use-platform";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { getPostLoginRedirectPath } from "@/lib/auth-utils"; import { getPostLoginRedirectPath } from "@/lib/auth-utils";
type ShortcutKey = "generalAssist" | "quickAsk" | "screenshotAssist"; type ShortcutKey = "generalAssist" | "quickAsk" | "screenshotAssist";

View file

@ -98,15 +98,11 @@ export default function InviteAcceptPage() {
// Track invite accepted and user added events // Track invite accepted and user added events
trackSearchSpaceInviteAccepted( trackSearchSpaceInviteAccepted(
result.search_space_id, result.workspace_id,
result.search_space_name, result.workspace_name,
result.role_name
);
trackSearchSpaceUserAdded(
result.search_space_id,
result.search_space_name,
result.role_name result.role_name
); );
trackSearchSpaceUserAdded(result.workspace_id, result.workspace_name, result.role_name);
} }
} catch (err: any) { } catch (err: any) {
setError(err.message || "Failed to accept invite"); setError(err.message || "Failed to accept invite");
@ -117,7 +113,7 @@ export default function InviteAcceptPage() {
const handleDecline = () => { const handleDecline = () => {
// Track invite declined event // Track invite declined event
trackSearchSpaceInviteDeclined(inviteInfo?.search_space_name); trackSearchSpaceInviteDeclined(inviteInfo?.workspace_name);
router.push("/dashboard"); router.push("/dashboard");
}; };
@ -180,7 +176,7 @@ export default function InviteAcceptPage() {
</motion.div> </motion.div>
<CardTitle className="text-2xl">Welcome to the team!</CardTitle> <CardTitle className="text-2xl">Welcome to the team!</CardTitle>
<CardDescription> <CardDescription>
You've successfully joined {acceptedData.search_space_name} You've successfully joined {acceptedData.workspace_name}
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@ -190,7 +186,7 @@ export default function InviteAcceptPage() {
<Users className="h-5 w-5 text-primary" /> <Users className="h-5 w-5 text-primary" />
</div> </div>
<div> <div>
<p className="font-medium">{acceptedData.search_space_name}</p> <p className="font-medium">{acceptedData.workspace_name}</p>
<p className="text-sm text-muted-foreground">Search Space</p> <p className="text-sm text-muted-foreground">Search Space</p>
</div> </div>
</div> </div>
@ -208,7 +204,7 @@ export default function InviteAcceptPage() {
<CardFooter> <CardFooter>
<Button <Button
className="w-full gap-2" className="w-full gap-2"
onClick={() => router.push(`/dashboard/${acceptedData.search_space_id}`)} onClick={() => router.push(`/dashboard/${acceptedData.workspace_id}`)}
> >
Go to Search Space Go to Search Space
<ArrowRight className="h-4 w-4" /> <ArrowRight className="h-4 w-4" />
@ -260,7 +256,7 @@ export default function InviteAcceptPage() {
</motion.div> </motion.div>
<CardTitle className="text-2xl">You're Invited!</CardTitle> <CardTitle className="text-2xl">You're Invited!</CardTitle>
<CardDescription> <CardDescription>
Sign in to join {inviteInfo?.search_space_name || "this search space"} Sign in to join {inviteInfo?.workspace_name || "this search space"}
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@ -270,7 +266,7 @@ export default function InviteAcceptPage() {
<Users className="h-5 w-5 text-primary" /> <Users className="h-5 w-5 text-primary" />
</div> </div>
<div> <div>
<p className="font-medium">{inviteInfo?.search_space_name}</p> <p className="font-medium">{inviteInfo?.workspace_name}</p>
<p className="text-sm text-muted-foreground">Search Space</p> <p className="text-sm text-muted-foreground">Search Space</p>
</div> </div>
</div> </div>
@ -307,7 +303,7 @@ export default function InviteAcceptPage() {
</motion.div> </motion.div>
<CardTitle className="text-2xl">You're Invited!</CardTitle> <CardTitle className="text-2xl">You're Invited!</CardTitle>
<CardDescription> <CardDescription>
Accept this invite to join {inviteInfo?.search_space_name || "this search space"} Accept this invite to join {inviteInfo?.workspace_name || "this search space"}
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@ -317,7 +313,7 @@ export default function InviteAcceptPage() {
<Users className="h-5 w-5 text-primary" /> <Users className="h-5 w-5 text-primary" />
</div> </div>
<div> <div>
<p className="font-medium">{inviteInfo?.search_space_name}</p> <p className="font-medium">{inviteInfo?.workspace_name}</p>
<p className="text-sm text-muted-foreground">Search Space</p> <p className="text-sm text-muted-foreground">Search Space</p>
</div> </div>
</div> </div>

View file

@ -2,7 +2,7 @@ import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { agentToolsApiService } from "@/lib/apis/agent-tools-api.service"; import { agentToolsApiService } from "@/lib/apis/agent-tools-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
export const agentToolsAtom = atomWithQuery((_get) => ({ export const agentToolsAtom = atomWithQuery((_get) => ({
queryKey: cacheKeys.agentTools.all(), queryKey: cacheKeys.agentTools.all(),
@ -42,7 +42,7 @@ const hydratedForAtom = atom<string | null>(null);
*/ */
export const disabledToolsAtom = atom( export const disabledToolsAtom = atom(
(get) => { (get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const hydratedFor = get(hydratedForAtom); const hydratedFor = get(hydratedForAtom);
if (searchSpaceId && hydratedFor !== searchSpaceId) { if (searchSpaceId && hydratedFor !== searchSpaceId) {
return loadDisabledTools(searchSpaceId); return loadDisabledTools(searchSpaceId);
@ -50,7 +50,7 @@ export const disabledToolsAtom = atom(
return get(disabledToolsBaseAtom); return get(disabledToolsBaseAtom);
}, },
(get, set, update: string[] | ((prev: string[]) => string[])) => { (get, set, update: string[] | ((prev: string[]) => string[])) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const prev = get(disabledToolsBaseAtom); const prev = get(disabledToolsBaseAtom);
const next = typeof update === "function" ? update(prev) : update; const next = typeof update === "function" ? update(prev) : update;
set(disabledToolsBaseAtom, next); set(disabledToolsBaseAtom, next);
@ -66,7 +66,7 @@ export const disabledToolsAtom = atom(
* Call this from a useEffect in a component that has access to the search space. * Call this from a useEffect in a component that has access to the search space.
*/ */
export const hydrateDisabledToolsAtom = atom(null, (get, set) => { export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
if (!searchSpaceId) return; if (!searchSpaceId) return;
const stored = loadDisabledTools(searchSpaceId); const stored = loadDisabledTools(searchSpaceId);
set(disabledToolsBaseAtom, stored); set(disabledToolsBaseAtom, stored);
@ -75,7 +75,7 @@ export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
/** Toggle a single tool's enabled/disabled state */ /** Toggle a single tool's enabled/disabled state */
export const toggleToolAtom = atom(null, (get, set, toolName: string) => { export const toggleToolAtom = atom(null, (get, set, toolName: string) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const current = get(disabledToolsBaseAtom); const current = get(disabledToolsBaseAtom);
const next = current.includes(toolName) const next = current.includes(toolName)
? current.filter((t) => t !== toolName) ? current.filter((t) => t !== toolName)

View file

@ -49,10 +49,10 @@ export const createAutomationMutationAtom = atomWithMutation(() => ({
return automationsApiService.createAutomation(request); return automationsApiService.createAutomation(request);
}, },
onSuccess: (automation, variables) => { onSuccess: (automation, variables) => {
invalidateList(variables.search_space_id); invalidateList(variables.workspace_id);
toast.success("Automation created"); toast.success("Automation created");
trackAutomationCreated({ trackAutomationCreated({
search_space_id: variables.search_space_id, workspace_id: variables.workspace_id,
automation_id: automation.id, automation_id: automation.id,
task_count: variables.definition.plan.length, task_count: variables.definition.plan.length,
trigger_type: variables.triggers?.[0]?.type ?? "none", trigger_type: variables.triggers?.[0]?.type ?? "none",
@ -67,7 +67,7 @@ export const createAutomationMutationAtom = atomWithMutation(() => ({
console.error("Error creating automation:", error); console.error("Error creating automation:", error);
toast.error("Failed to create automation"); toast.error("Failed to create automation");
trackAutomationCreateFailed({ trackAutomationCreateFailed({
search_space_id: variables.search_space_id, workspace_id: variables.workspace_id,
error: error.message, error: error.message,
}); });
}, },
@ -80,20 +80,20 @@ export const updateAutomationMutationAtom = atomWithMutation(() => ({
}, },
onSuccess: (automation, vars) => { onSuccess: (automation, vars) => {
invalidateDetail(vars.automationId); invalidateDetail(vars.automationId);
invalidateList(automation.search_space_id); invalidateList(automation.workspace_id);
toast.success("Automation updated"); toast.success("Automation updated");
// A status-only patch (pause/resume/archive) is a distinct action from a // A status-only patch (pause/resume/archive) is a distinct action from a
// definition/name edit, so split it into its own event. // definition/name edit, so split it into its own event.
if (vars.patch.status && !vars.patch.definition) { if (vars.patch.status && !vars.patch.definition) {
trackAutomationStatusChanged({ trackAutomationStatusChanged({
automation_id: vars.automationId, automation_id: vars.automationId,
search_space_id: automation.search_space_id, workspace_id: automation.workspace_id,
next_status: vars.patch.status, next_status: vars.patch.status,
}); });
} else { } else {
trackAutomationUpdated({ trackAutomationUpdated({
automation_id: vars.automationId, automation_id: vars.automationId,
search_space_id: automation.search_space_id, workspace_id: automation.workspace_id,
has_definition_change: !!vars.patch.definition, has_definition_change: !!vars.patch.definition,
has_name_change: vars.patch.name != null, has_name_change: vars.patch.name != null,
has_description_change: vars.patch.description !== undefined, has_description_change: vars.patch.description !== undefined,
@ -123,7 +123,7 @@ export const deleteAutomationMutationAtom = atomWithMutation(() => ({
toast.success("Automation deleted"); toast.success("Automation deleted");
trackAutomationDeleted({ trackAutomationDeleted({
automation_id: vars.automationId, automation_id: vars.automationId,
search_space_id: vars.searchSpaceId, workspace_id: vars.searchSpaceId,
}); });
}, },
onError: (error: Error, vars) => { onError: (error: Error, vars) => {

View file

@ -1,5 +1,5 @@
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { automationsApiService } from "@/lib/apis/automations-api.service"; import { automationsApiService } from "@/lib/apis/automations-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
@ -11,7 +11,7 @@ const DEFAULT_LIMIT = 50;
const DEFAULT_OFFSET = 0; const DEFAULT_OFFSET = 0;
export const automationsListAtom = atomWithQuery((get) => { export const automationsListAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.automations.list(Number(searchSpaceId ?? 0), DEFAULT_LIMIT, DEFAULT_OFFSET), queryKey: cacheKeys.automations.list(Number(searchSpaceId ?? 0), DEFAULT_LIMIT, DEFAULT_OFFSET),
@ -22,7 +22,7 @@ export const automationsListAtom = atomWithQuery((get) => {
return { items: [], total: 0 }; return { items: [], total: 0 };
} }
return automationsApiService.listAutomations({ return automationsApiService.listAutomations({
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
limit: DEFAULT_LIMIT, limit: DEFAULT_LIMIT,
offset: DEFAULT_OFFSET, offset: DEFAULT_OFFSET,
}); });

View file

@ -10,10 +10,10 @@ import type {
import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { queryClient } from "@/lib/query-client/client"; import { queryClient } from "@/lib/query-client/client";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
export const createConnectorMutationAtom = atomWithMutation((get) => { export const createConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""), mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
@ -32,7 +32,7 @@ export const createConnectorMutationAtom = atomWithMutation((get) => {
}); });
export const updateConnectorMutationAtom = atomWithMutation((get) => { export const updateConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""), mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
@ -54,7 +54,7 @@ export const updateConnectorMutationAtom = atomWithMutation((get) => {
}); });
export const deleteConnectorMutationAtom = atomWithMutation((get) => { export const deleteConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""), mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
@ -80,7 +80,7 @@ export const deleteConnectorMutationAtom = atomWithMutation((get) => {
}); });
export const indexConnectorMutationAtom = atomWithMutation((get) => { export const indexConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.connectors.index(), mutationKey: cacheKeys.connectors.index(),

View file

@ -1,10 +1,10 @@
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
export const connectorsAtom = atomWithQuery((get) => { export const connectorsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.connectors.all(searchSpaceId!), queryKey: cacheKeys.connectors.all(searchSpaceId!),
@ -13,7 +13,7 @@ export const connectorsAtom = atomWithQuery((get) => {
queryFn: async () => { queryFn: async () => {
return connectorsApiService.getConnectors({ return connectorsApiService.getConnectors({
queryParams: { queryParams: {
search_space_id: searchSpaceId!, workspace_id: searchSpaceId!,
}, },
}); });
}, },

View file

@ -1,6 +1,6 @@
import { atomWithMutation } from "jotai-tanstack-query"; import { atomWithMutation } from "jotai-tanstack-query";
import { toast } from "sonner"; import { toast } from "sonner";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import type { import type {
CreateDocumentRequest, CreateDocumentRequest,
DeleteDocumentRequest, DeleteDocumentRequest,
@ -14,7 +14,7 @@ import { queryClient } from "@/lib/query-client/client";
import { globalDocumentsQueryParamsAtom } from "./ui.atoms"; import { globalDocumentsQueryParamsAtom } from "./ui.atoms";
export const createDocumentMutationAtom = atomWithMutation((get) => { export const createDocumentMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const documentsQueryParams = get(globalDocumentsQueryParamsAtom); const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
return { return {
@ -34,7 +34,7 @@ export const createDocumentMutationAtom = atomWithMutation((get) => {
}); });
export const uploadDocumentMutationAtom = atomWithMutation((get) => { export const uploadDocumentMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const documentsQueryParams = get(globalDocumentsQueryParamsAtom); const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
return { return {
@ -54,7 +54,7 @@ export const uploadDocumentMutationAtom = atomWithMutation((get) => {
}); });
export const updateDocumentMutationAtom = atomWithMutation((get) => { export const updateDocumentMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const documentsQueryParams = get(globalDocumentsQueryParamsAtom); const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
return { return {
@ -77,7 +77,7 @@ export const updateDocumentMutationAtom = atomWithMutation((get) => {
}); });
export const deleteDocumentMutationAtom = atomWithMutation((get) => { export const deleteDocumentMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
const documentsQueryParams = get(globalDocumentsQueryParamsAtom); const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
return { return {

View file

@ -20,7 +20,7 @@ export const createInviteMutationAtom = atomWithMutation(() => ({
}, },
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()), queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
}); });
toast.success("Invite created successfully"); toast.success("Invite created successfully");
}, },
@ -40,7 +40,7 @@ export const updateInviteMutationAtom = atomWithMutation(() => ({
}, },
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()), queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
}); });
toast.success("Invite updated successfully"); toast.success("Invite updated successfully");
}, },
@ -60,7 +60,7 @@ export const deleteInviteMutationAtom = atomWithMutation(() => ({
}, },
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()), queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
}); });
toast.success("Invite deleted successfully"); toast.success("Invite deleted successfully");
}, },

View file

@ -1,10 +1,10 @@
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { invitesApiService } from "@/lib/apis/invites-api.service"; import { invitesApiService } from "@/lib/apis/invites-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
export const invitesAtom = atomWithQuery((get) => { export const invitesAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.invites.all(searchSpaceId?.toString() ?? ""), queryKey: cacheKeys.invites.all(searchSpaceId?.toString() ?? ""),
@ -15,7 +15,7 @@ export const invitesAtom = atomWithQuery((get) => {
return []; return [];
} }
return invitesApiService.getInvites({ return invitesApiService.getInvites({
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
}); });
}, },
}; };

View file

@ -1,5 +1,5 @@
import { atomWithMutation } from "jotai-tanstack-query"; import { atomWithMutation } from "jotai-tanstack-query";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import type { import type {
CreateLogRequest, CreateLogRequest,
DeleteLogRequest, DeleteLogRequest,
@ -13,7 +13,7 @@ import { queryClient } from "@/lib/query-client/client";
* Create Log Mutation * Create Log Mutation
*/ */
export const createLogMutationAtom = atomWithMutation((get) => { export const createLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined), mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,
@ -29,7 +29,7 @@ export const createLogMutationAtom = atomWithMutation((get) => {
* Update Log Mutation * Update Log Mutation
*/ */
export const updateLogMutationAtom = atomWithMutation((get) => { export const updateLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined), mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,
@ -45,7 +45,7 @@ export const updateLogMutationAtom = atomWithMutation((get) => {
* Delete Log Mutation * Delete Log Mutation
*/ */
export const deleteLogMutationAtom = atomWithMutation((get) => { export const deleteLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined), mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,

View file

@ -21,7 +21,7 @@ export const updateMemberMutationAtom = atomWithMutation(() => {
onSuccess: (_: UpdateMembershipResponse, request: UpdateMembershipRequest) => { onSuccess: (_: UpdateMembershipResponse, request: UpdateMembershipRequest) => {
toast.success("Member updated successfully"); toast.success("Member updated successfully");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.members.all(request.search_space_id.toString()), queryKey: cacheKeys.members.all(request.workspace_id.toString()),
}); });
}, },
onError: () => { onError: () => {
@ -39,7 +39,7 @@ export const deleteMemberMutationAtom = atomWithMutation(() => {
onSuccess: (_: DeleteMembershipResponse, request: DeleteMembershipRequest) => { onSuccess: (_: DeleteMembershipResponse, request: DeleteMembershipRequest) => {
toast.success("Member removed successfully"); toast.success("Member removed successfully");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.members.all(request.search_space_id.toString()), queryKey: cacheKeys.members.all(request.workspace_id.toString()),
}); });
}, },
onError: () => { onError: () => {
@ -57,7 +57,7 @@ export const leaveSearchSpaceMutationAtom = atomWithMutation(() => {
onSuccess: (_: LeaveSearchSpaceResponse, request: LeaveSearchSpaceRequest) => { onSuccess: (_: LeaveSearchSpaceResponse, request: LeaveSearchSpaceRequest) => {
toast.success("Successfully left the search space"); toast.success("Successfully left the search space");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.members.all(request.search_space_id.toString()), queryKey: cacheKeys.members.all(request.workspace_id.toString()),
}); });
}, },
onError: () => { onError: () => {

View file

@ -1,10 +1,10 @@
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { membersApiService } from "@/lib/apis/members-api.service"; import { membersApiService } from "@/lib/apis/members-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
export const membersAtom = atomWithQuery((get) => { export const membersAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.members.all(searchSpaceId?.toString() ?? ""), queryKey: cacheKeys.members.all(searchSpaceId?.toString() ?? ""),
@ -16,14 +16,14 @@ export const membersAtom = atomWithQuery((get) => {
return []; return [];
} }
return membersApiService.getMembers({ return membersApiService.getMembers({
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
}); });
}, },
}; };
}); });
export const myAccessAtom = atomWithQuery((get) => { export const myAccessAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.members.myAccess(searchSpaceId?.toString() ?? ""), queryKey: cacheKeys.members.myAccess(searchSpaceId?.toString() ?? ""),
@ -34,7 +34,7 @@ export const myAccessAtom = atomWithQuery((get) => {
return null; return null;
} }
return membersApiService.getMyAccess({ return membersApiService.getMyAccess({
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
}); });
}, },
}; };

View file

@ -16,7 +16,7 @@ import type {
import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service"; import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { queryClient } from "@/lib/query-client/client"; import { queryClient } from "@/lib/query-client/client";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
function invalidateModelConnections(searchSpaceId: number) { function invalidateModelConnections(searchSpaceId: number) {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
@ -40,14 +40,14 @@ function upsertModelConnection(searchSpaceId: number, connection: ConnectionRead
} }
export const createModelConnectionMutationAtom = atomWithMutation((get) => { export const createModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-connections", "create"], mutationKey: ["model-connections", "create"],
mutationFn: (request: ConnectionCreateRequest) => mutationFn: (request: ConnectionCreateRequest) =>
modelConnectionsApiService.createConnection(request), modelConnectionsApiService.createConnection(request),
onSuccess: (connection: ConnectionRead, request: ConnectionCreateRequest) => { onSuccess: (connection: ConnectionRead, request: ConnectionCreateRequest) => {
const resolvedSearchSpaceId = Number( const resolvedSearchSpaceId = Number(
request.search_space_id ?? connection.search_space_id ?? searchSpaceId request.workspace_id ?? connection.workspace_id ?? searchSpaceId
); );
toast.success("Connection created"); toast.success("Connection created");
if (resolvedSearchSpaceId > 0) { if (resolvedSearchSpaceId > 0) {
@ -60,7 +60,7 @@ export const createModelConnectionMutationAtom = atomWithMutation((get) => {
}); });
export const updateModelConnectionMutationAtom = atomWithMutation((get) => { export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-connections", "update"], mutationKey: ["model-connections", "update"],
mutationFn: ({ id, data }: { id: number; data: ConnectionUpdateRequest }) => mutationFn: ({ id, data }: { id: number; data: ConnectionUpdateRequest }) =>
@ -74,7 +74,7 @@ export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
}); });
export const deleteModelConnectionMutationAtom = atomWithMutation((get) => { export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-connections", "delete"], mutationKey: ["model-connections", "delete"],
mutationFn: (id: number) => modelConnectionsApiService.deleteConnection(id), mutationFn: (id: number) => modelConnectionsApiService.deleteConnection(id),
@ -87,7 +87,7 @@ export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
}); });
export const verifyModelConnectionMutationAtom = atomWithMutation((get) => { export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-connections", "verify"], mutationKey: ["model-connections", "verify"],
mutationFn: (id: number) => modelConnectionsApiService.verifyConnection(id), mutationFn: (id: number) => modelConnectionsApiService.verifyConnection(id),
@ -110,7 +110,7 @@ export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
}); });
export const discoverConnectionModelsMutationAtom = atomWithMutation((get) => { export const discoverConnectionModelsMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-connections", "discover"], mutationKey: ["model-connections", "discover"],
mutationFn: (id: number) => modelConnectionsApiService.discoverModels(id), mutationFn: (id: number) => modelConnectionsApiService.discoverModels(id),
@ -149,7 +149,7 @@ export const testPreviewModelMutationAtom = atomWithMutation(() => {
}); });
export const addManualModelMutationAtom = atomWithMutation((get) => { export const addManualModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["models", "add-manual"], mutationKey: ["models", "add-manual"],
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelCreateRequest }) => mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelCreateRequest }) =>
@ -163,7 +163,7 @@ export const addManualModelMutationAtom = atomWithMutation((get) => {
}); });
export const updateModelMutationAtom = atomWithMutation((get) => { export const updateModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["models", "update"], mutationKey: ["models", "update"],
mutationFn: ({ id, data }: { id: number; data: ModelUpdateRequest }) => mutationFn: ({ id, data }: { id: number; data: ModelUpdateRequest }) =>
@ -174,7 +174,7 @@ export const updateModelMutationAtom = atomWithMutation((get) => {
}); });
export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => { export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["models", "bulk-update"], mutationKey: ["models", "bulk-update"],
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelsBulkUpdateRequest }) => mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelsBulkUpdateRequest }) =>
@ -185,7 +185,7 @@ export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
}); });
export const testModelMutationAtom = atomWithMutation((get) => { export const testModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["models", "test"], mutationKey: ["models", "test"],
mutationFn: (id: number) => modelConnectionsApiService.testModel(id), mutationFn: (id: number) => modelConnectionsApiService.testModel(id),
@ -199,7 +199,7 @@ export const testModelMutationAtom = atomWithMutation((get) => {
}); });
export const updateModelRolesMutationAtom = atomWithMutation((get) => { export const updateModelRolesMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
mutationKey: ["model-roles", "update"], mutationKey: ["model-roles", "update"],
mutationFn: (roles: ModelRoles) => mutationFn: (roles: ModelRoles) =>

View file

@ -2,7 +2,7 @@ import { atomWithQuery } from "jotai-tanstack-query";
import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service"; import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service";
import { isAuthenticated } from "@/lib/auth-utils"; import { isAuthenticated } from "@/lib/auth-utils";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
export const globalModelConnectionsAtom = atomWithQuery(() => ({ export const globalModelConnectionsAtom = atomWithQuery(() => ({
queryKey: cacheKeys.modelConnections.global(), queryKey: cacheKeys.modelConnections.global(),
@ -26,7 +26,7 @@ export const modelProvidersAtom = atomWithQuery(() => ({
})); }));
export const modelConnectionsAtom = atomWithQuery((get) => { export const modelConnectionsAtom = atomWithQuery((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
queryKey: cacheKeys.modelConnections.all(searchSpaceId), queryKey: cacheKeys.modelConnections.all(searchSpaceId),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,
@ -36,7 +36,7 @@ export const modelConnectionsAtom = atomWithQuery((get) => {
}); });
export const modelRolesAtom = atomWithQuery((get) => { export const modelRolesAtom = atomWithQuery((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom)); const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return { return {
queryKey: cacheKeys.modelConnections.roles(searchSpaceId), queryKey: cacheKeys.modelConnections.roles(searchSpaceId),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,

View file

@ -1,10 +1,10 @@
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service"; import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
export const publicChatSnapshotsAtom = atomWithQuery((get) => { export const publicChatSnapshotsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeWorkspaceIdAtom);
return { return {
queryKey: cacheKeys.publicChatSnapshots.bySearchSpace(Number(searchSpaceId) || 0), queryKey: cacheKeys.publicChatSnapshots.bySearchSpace(Number(searchSpaceId) || 0),
@ -15,7 +15,7 @@ export const publicChatSnapshotsAtom = atomWithQuery((get) => {
return { snapshots: [] }; return { snapshots: [] };
} }
return chatThreadsApiService.listPublicChatSnapshotsForSearchSpace({ return chatThreadsApiService.listPublicChatSnapshotsForSearchSpace({
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
}); });
}, },
}; };

View file

@ -21,7 +21,7 @@ export const createRoleMutationAtom = atomWithMutation(() => {
onSuccess: (_: CreateRoleResponse, request: CreateRoleRequest) => { onSuccess: (_: CreateRoleResponse, request: CreateRoleRequest) => {
toast.success("Role created successfully"); toast.success("Role created successfully");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()), queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
}); });
}, },
onError: () => { onError: () => {
@ -39,13 +39,10 @@ export const updateRoleMutationAtom = atomWithMutation(() => {
onSuccess: (_: UpdateRoleResponse, request: UpdateRoleRequest) => { onSuccess: (_: UpdateRoleResponse, request: UpdateRoleRequest) => {
toast.success("Role updated successfully"); toast.success("Role updated successfully");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()), queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
}); });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.roles.byId( queryKey: cacheKeys.roles.byId(request.workspace_id.toString(), request.role_id.toString()),
request.search_space_id.toString(),
request.role_id.toString()
),
}); });
}, },
onError: () => { onError: () => {
@ -63,7 +60,7 @@ export const deleteRoleMutationAtom = atomWithMutation(() => {
onSuccess: (_: DeleteRoleResponse, request: DeleteRoleRequest) => { onSuccess: (_: DeleteRoleResponse, request: DeleteRoleRequest) => {
toast.success("Role deleted successfully"); toast.success("Role deleted successfully");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()), queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
}); });
}, },
onError: () => { onError: () => {

View file

@ -5,11 +5,11 @@ import type {
DeleteSearchSpaceRequest, DeleteSearchSpaceRequest,
UpdateSearchSpaceApiAccessRequest, UpdateSearchSpaceApiAccessRequest,
UpdateSearchSpaceRequest, UpdateSearchSpaceRequest,
} from "@/contracts/types/search-space.types"; } from "@/contracts/types/workspace.types";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { queryClient } from "@/lib/query-client/client"; import { queryClient } from "@/lib/query-client/client";
import { activeSearchSpaceIdAtom } from "./search-space-query.atoms"; import { activeWorkspaceIdAtom } from "./workspace-query.atoms";
export const createSearchSpaceMutationAtom = atomWithMutation(() => { export const createSearchSpaceMutationAtom = atomWithMutation(() => {
return { return {
@ -28,7 +28,7 @@ export const createSearchSpaceMutationAtom = atomWithMutation(() => {
}); });
export const updateSearchSpaceMutationAtom = atomWithMutation((get) => { export const updateSearchSpaceMutationAtom = atomWithMutation((get) => {
const activeSearchSpaceId = get(activeSearchSpaceIdAtom); const activeSearchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: ["update-search-space", activeSearchSpaceId], mutationKey: ["update-search-space", activeSearchSpaceId],
@ -52,7 +52,7 @@ export const updateSearchSpaceMutationAtom = atomWithMutation((get) => {
}); });
export const updateSearchSpaceApiAccessMutationAtom = atomWithMutation((get) => { export const updateSearchSpaceApiAccessMutationAtom = atomWithMutation((get) => {
const activeSearchSpaceId = get(activeSearchSpaceIdAtom); const activeSearchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: ["update-search-space-api-access", activeSearchSpaceId], mutationKey: ["update-search-space-api-access", activeSearchSpaceId],
@ -74,7 +74,7 @@ export const updateSearchSpaceApiAccessMutationAtom = atomWithMutation((get) =>
}); });
export const deleteSearchSpaceMutationAtom = atomWithMutation((get) => { export const deleteSearchSpaceMutationAtom = atomWithMutation((get) => {
const activeSearchSpaceId = get(activeSearchSpaceIdAtom); const activeSearchSpaceId = get(activeWorkspaceIdAtom);
return { return {
mutationKey: ["delete-search-space", activeSearchSpaceId], mutationKey: ["delete-search-space", activeSearchSpaceId],

View file

@ -1,10 +1,10 @@
import { atom } from "jotai"; import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithQuery } from "jotai-tanstack-query";
import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types"; import type { GetSearchSpacesRequest } from "@/contracts/types/workspace.types";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
export const activeSearchSpaceIdAtom = atom<string | null>(null); export const activeWorkspaceIdAtom = atom<string | null>(null);
export const searchSpacesQueryParamsAtom = atom<GetSearchSpacesRequest["queryParams"]>({ export const searchSpacesQueryParamsAtom = atom<GetSearchSpacesRequest["queryParams"]>({
skip: 0, skip: 0,

View file

@ -29,7 +29,7 @@ import {
globalModelConnectionsAtom, globalModelConnectionsAtom,
modelConnectionsAtom, modelConnectionsAtom,
} from "@/atoms/model-connections/model-connections-query.atoms"; } from "@/atoms/model-connections/model-connections-query.atoms";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { import {
CitationMetadataProvider, CitationMetadataProvider,
useAllCitationMetadata, useAllCitationMetadata,
@ -491,7 +491,7 @@ export const AssistantMessage: FC = () => {
const commentPanelRef = useRef<HTMLDivElement>(null); const commentPanelRef = useRef<HTMLDivElement>(null);
const commentTriggerRef = useRef<HTMLButtonElement>(null); const commentTriggerRef = useRef<HTMLButtonElement>(null);
const messageId = useAuiState(({ message }) => message?.id); const messageId = useAuiState(({ message }) => message?.id);
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const dbMessageId = parseMessageId(messageId); const dbMessageId = parseMessageId(messageId);
const commentsEnabled = useAtomValue(commentsEnabledAtom); const commentsEnabled = useAtomValue(commentsEnabledAtom);

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom"; import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
import { Tabs, TabsContent } from "@/components/ui/tabs"; import { Tabs, TabsContent } from "@/components/ui/tabs";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
@ -36,7 +36,7 @@ interface ConnectorIndicatorProps {
export const ConnectorIndicator = forwardRef<ConnectorIndicatorHandle, ConnectorIndicatorProps>( export const ConnectorIndicator = forwardRef<ConnectorIndicatorHandle, ConnectorIndicatorProps>(
(_props, ref) => { (_props, ref) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
// Real-time document type counts via Zero (updates instantly as docs are indexed) // Real-time document type counts via Zero (updates instantly as docs are indexed)
const documentTypeCounts = useZeroDocumentTypeCounts(searchSpaceId); const documentTypeCounts = useZeroDocumentTypeCounts(searchSpaceId);

View file

@ -18,7 +18,7 @@ export interface CirclebackConfigProps extends ConnectorConfigProps {
// Type-safe schema for webhook info response // Type-safe schema for webhook info response
const circlebackWebhookInfoSchema = z.object({ const circlebackWebhookInfoSchema = z.object({
webhook_url: z.string(), webhook_url: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
method: z.string(), method: z.string(),
content_type: z.string(), content_type: z.string(),
description: z.string(), description: z.string(),
@ -40,12 +40,12 @@ export const CirclebackConfig: FC<CirclebackConfigProps> = ({ connector, onNameC
const controller = new AbortController(); const controller = new AbortController();
const doFetch = async () => { const doFetch = async () => {
if (!connector.search_space_id) return; if (!connector.workspace_id) return;
setIsLoading(true); setIsLoading(true);
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/webhooks/circleback/${connector.search_space_id}/info`), buildBackendUrl(`/api/v1/webhooks/circleback/${connector.workspace_id}/info`),
{ signal: controller.signal } { signal: controller.signal }
); );
if (controller.signal.aborted) return; if (controller.signal.aborted) return;
@ -70,7 +70,7 @@ export const CirclebackConfig: FC<CirclebackConfigProps> = ({ connector, onNameC
doFetch().catch(() => {}); doFetch().catch(() => {});
return () => controller.abort(); return () => controller.abort();
}, [connector.search_space_id]); }, [connector.workspace_id]);
const handleNameChange = (value: string) => { const handleNameChange = (value: string) => {
setName(value); setName(value);

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { ArrowLeft, Info, RefreshCw } from "lucide-react"; import { ArrowLeft, Info, RefreshCw } from "lucide-react";
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
@ -78,7 +78,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
onConfigChange, onConfigChange,
onNameChange, onNameChange,
}) => { }) => {
const searchSpaceIdAtom = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceIdAtom = useAtomValue(activeWorkspaceIdAtom);
const isAuthExpired = connector.config?.auth_expired === true; const isAuthExpired = connector.config?.auth_expired === true;
const reauthEndpoint = getReauthEndpoint(connector); const reauthEndpoint = getReauthEndpoint(connector);
const [reauthing, setReauthing] = useState(false); const [reauthing, setReauthing] = useState(false);

View file

@ -10,7 +10,7 @@ import {
updateConnectorMutationAtom, updateConnectorMutationAtom,
} from "@/atoms/connectors/connector-mutation.atoms"; } from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms"; import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { searchSourceConnector } from "@/contracts/types/connector.types"; import { searchSourceConnector } from "@/contracts/types/connector.types";
@ -58,7 +58,7 @@ function clearOAuthResultCookie(): void {
} }
export const useConnectorDialog = () => { export const useConnectorDialog = () => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const { data: allConnectors, refetch: refetchAllConnectors } = useAtomValue(connectorsAtom); const { data: allConnectors, refetch: refetchAllConnectors } = useAtomValue(connectorsAtom);
const { mutateAsync: indexConnector } = useAtomValue(indexConnectorMutationAtom); const { mutateAsync: indexConnector } = useAtomValue(indexConnectorMutationAtom);
const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom); const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom);
@ -165,7 +165,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: connector.id, connector_id: connector.id,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
start_date: format(startDate, "yyyy-MM-dd"), start_date: format(startDate, "yyyy-MM-dd"),
end_date: format(endDate, "yyyy-MM-dd"), end_date: format(endDate, "yyyy-MM-dd"),
}, },
@ -418,7 +418,7 @@ export const useConnectorDialog = () => {
enable_vision_llm: false, enable_vision_llm: false,
}, },
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
}); });
@ -519,7 +519,7 @@ export const useConnectorDialog = () => {
enable_vision_llm: false, enable_vision_llm: false,
}, },
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
}); });
// Refetch connectors to get the new one // Refetch connectors to get the new one
@ -612,7 +612,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: connector.id, connector_id: connector.id,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
start_date: startDateStr, start_date: startDateStr,
end_date: endDateStr, end_date: endDateStr,
}, },
@ -847,7 +847,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: indexingConfig.connectorId, connector_id: indexingConfig.connectorId,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
body: { body: {
folders: selectedFolders || [], folders: selectedFolders || [],
@ -870,14 +870,14 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: indexingConfig.connectorId, connector_id: indexingConfig.connectorId,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
}); });
} else { } else {
await indexConnector({ await indexConnector({
connector_id: indexingConfig.connectorId, connector_id: indexingConfig.connectorId,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
start_date: startDateStr, start_date: startDateStr,
end_date: endDateStr, end_date: endDateStr,
}, },
@ -1114,7 +1114,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: editingConnector.id, connector_id: editingConnector.id,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
body: { body: {
folders: selectedFolders || [], folders: selectedFolders || [],
@ -1134,7 +1134,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: editingConnector.id, connector_id: editingConnector.id,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}, },
}); });
indexingDescription = "Re-indexing started with updated configuration."; indexingDescription = "Re-indexing started with updated configuration.";
@ -1143,7 +1143,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: editingConnector.id, connector_id: editingConnector.id,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
start_date: startDateStr, start_date: startDateStr,
end_date: endDateStr, end_date: endDateStr,
}, },
@ -1296,7 +1296,7 @@ export const useConnectorDialog = () => {
await indexConnector({ await indexConnector({
connector_id: connectorId, connector_id: connectorId,
queryParams: { queryParams: {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
start_date: startDateStr, start_date: startDateStr,
end_date: endDateStr, end_date: endDateStr,
}, },

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { ArrowLeft, Plus, RefreshCw, Server } from "lucide-react"; import { ArrowLeft, Plus, RefreshCw, Server } from "lucide-react";
import { type FC, useCallback, useState } from "react"; import { type FC, useCallback, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
@ -44,7 +44,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
isConnecting = false, isConnecting = false,
addButtonText, addButtonText,
}) => { }) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const [reauthingId, setReauthingId] = useState<number | null>(null); const [reauthingId, setReauthingId] = useState<number | null>(null);
const [confirmDisconnectId, setConfirmDisconnectId] = useState<number | null>(null); const [confirmDisconnectId, setConfirmDisconnectId] = useState<number | null>(null);
const [disconnectingId, setDisconnectingId] = useState<number | null>(null); const [disconnectingId, setDisconnectingId] = useState<number | null>(null);

View file

@ -165,7 +165,7 @@ export const YouTubeCrawlerView: FC<YouTubeCrawlerViewProps> = ({ searchSpaceId,
{ {
document_type: "YOUTUBE_VIDEO", document_type: "YOUTUBE_VIDEO",
content: videoUrls, content: videoUrls,
search_space_id: parseInt(searchSpaceId, 10), workspace_id: parseInt(searchSpaceId, 10),
}, },
{ {
onSuccess: () => { onSuccess: () => {

View file

@ -10,7 +10,7 @@ import {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { DocumentUploadTab } from "@/components/sources/DocumentUploadTab"; import { DocumentUploadTab } from "@/components/sources/DocumentUploadTab";
import { import {
Dialog, Dialog,
@ -90,7 +90,7 @@ const DocumentUploadPopupContent: FC<{
isOpen: boolean; isOpen: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
}> = ({ isOpen, onOpenChange }) => { }> = ({ isOpen, onOpenChange }) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
if (!searchSpaceId) return null; if (!searchSpaceId) return null;

View file

@ -188,7 +188,7 @@ function FilePathLink({ path, className }: { path: string; className?: string })
const openEditorPanel = useSetAtom(openEditorPanelAtom); const openEditorPanel = useSetAtom(openEditorPanelAtom);
const params = useParams(); const params = useParams();
const electronAPI = useElectronAPI(); const electronAPI = useElectronAPI();
const searchSpaceIdParam = params?.search_space_id; const searchSpaceIdParam = params?.workspace_id;
const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam) const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam)
? Number(searchSpaceIdParam[0]) ? Number(searchSpaceIdParam[0])
: Number(searchSpaceIdParam); : Number(searchSpaceIdParam);
@ -228,7 +228,7 @@ function FilePathLink({ path, className }: { path: string; className?: string })
if (!resolvedSearchSpaceId || !path.startsWith("/documents/")) return; if (!resolvedSearchSpaceId || !path.startsWith("/documents/")) return;
try { try {
const doc = await documentsApiService.getDocumentByVirtualPath({ const doc = await documentsApiService.getDocumentByVirtualPath({
search_space_id: resolvedSearchSpaceId, workspace_id: resolvedSearchSpaceId,
virtual_path: path, virtual_path: path,
}); });
openEditorPanel({ openEditorPanel({

View file

@ -458,7 +458,7 @@ const Composer: FC = () => {
const prevMentionedDocsRef = useRef<Map<string, MentionedDocumentInfo>>(new Map()); const prevMentionedDocsRef = useRef<Map<string, MentionedDocumentInfo>>(new Map());
const documentPickerRef = useRef<DocumentMentionPickerRef>(null); const documentPickerRef = useRef<DocumentMentionPickerRef>(null);
const promptPickerRef = useRef<PromptPickerRef>(null); const promptPickerRef = useRef<PromptPickerRef>(null);
const { search_space_id, chat_id } = useParams(); const { workspace_id, chat_id } = useParams();
const aui = useAui(); const aui = useAui();
// Desktop-only auto-focus; on mobile, programmatic focus would // Desktop-only auto-focus; on mobile, programmatic focus would
// summon the soft keyboard on every picker close / thread switch. // summon the soft keyboard on every picker close / thread switch.
@ -824,7 +824,7 @@ const Composer: FC = () => {
const handleDocumentsMention = useCallback( const handleDocumentsMention = useCallback(
(mentions: MentionedDocumentInfo[]) => { (mentions: MentionedDocumentInfo[]) => {
const parsedSearchSpaceId = Number(search_space_id); const parsedSearchSpaceId = Number(workspace_id);
const editorMentionedDocs = editorRef.current?.getMentionedDocuments() ?? []; const editorMentionedDocs = editorRef.current?.getMentionedDocuments() ?? [];
const editorDocKeys = new Set(editorMentionedDocs.map((doc) => getMentionDocKey(doc))); const editorDocKeys = new Set(editorMentionedDocs.map((doc) => getMentionDocKey(doc)));
@ -844,7 +844,7 @@ const Composer: FC = () => {
setMentionQuery(""); setMentionQuery("");
setSuggestionAnchorPoint(null); setSuggestionAnchorPoint(null);
}, },
[search_space_id] [workspace_id]
); );
useEffect(() => { useEffect(() => {
@ -893,7 +893,7 @@ const Composer: FC = () => {
<ComposerSuggestionPopoverContent side="top"> <ComposerSuggestionPopoverContent side="top">
<DocumentMentionPicker <DocumentMentionPicker
ref={documentPickerRef} ref={documentPickerRef}
searchSpaceId={Number(search_space_id)} searchSpaceId={Number(workspace_id)}
enableChatMentions enableChatMentions
currentChatId={threadId} currentChatId={threadId}
onSelectionChange={handleDocumentsMention} onSelectionChange={handleDocumentsMention}
@ -959,7 +959,7 @@ const Composer: FC = () => {
</div> </div>
<ComposerAction <ComposerAction
isBlockedByOtherUser={isBlockedByOtherUser} isBlockedByOtherUser={isBlockedByOtherUser}
searchSpaceId={Number(search_space_id)} searchSpaceId={Number(workspace_id)}
onChatModelSelected={handleChatModelSelected} onChatModelSelected={handleChatModelSelected}
/> />
<ConnectorIndicator showTrigger={false} /> <ConnectorIndicator showTrigger={false} />

View file

@ -75,7 +75,7 @@ const UserTextPart: FC = () => {
const openEditorPanel = useSetAtom(openEditorPanelAtom); const openEditorPanel = useSetAtom(openEditorPanelAtom);
const router = useRouter(); const router = useRouter();
const params = useParams(); const params = useParams();
const searchSpaceIdParam = params?.search_space_id; const searchSpaceIdParam = params?.workspace_id;
const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam) const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam)
? Number(searchSpaceIdParam[0]) ? Number(searchSpaceIdParam[0])
: Number(searchSpaceIdParam); : Number(searchSpaceIdParam);

View file

@ -77,7 +77,7 @@ export const CitationPanelContent: FC<CitationPanelContentProps> = ({
if (!data) return; if (!data) return;
openEditorPanel({ openEditorPanel({
documentId: data.id, documentId: data.id,
searchSpaceId: data.search_space_id, searchSpaceId: data.workspace_id,
title: data.title, title: data.title,
}); });
}; };

View file

@ -276,7 +276,7 @@ export function EditorPanelContent({
} }
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl( buildBackendUrl(
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content` `/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/editor-content`
), ),
{ method: "GET" } { method: "GET" }
); );
@ -412,7 +412,7 @@ export function EditorPanelContent({
throw new Error("Missing document context"); throw new Error("Missing document context");
} }
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/save`), buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/save`),
{ {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@ -520,7 +520,7 @@ export function EditorPanelContent({
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl( buildBackendUrl(
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/download-markdown` `/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/download-markdown`
), ),
{ method: "GET" } { method: "GET" }
); );

View file

@ -27,7 +27,7 @@ interface MemoryReadResponse {
function getMemoryPath(scope: MemoryScope, searchSpaceId?: number | null) { function getMemoryPath(scope: MemoryScope, searchSpaceId?: number | null) {
if (scope === "user") return "/api/v1/users/me/memory"; if (scope === "user") return "/api/v1/users/me/memory";
if (!searchSpaceId) throw new Error("Missing search space context"); if (!searchSpaceId) throw new Error("Missing search space context");
return `/api/v1/searchspaces/${searchSpaceId}/memory`; return `/api/v1/workspaces/${searchSpaceId}/memory`;
} }
export function getMemoryLimitState(length: number, limits?: MemoryLimits | null) { export function getMemoryLimitState(length: number, limits?: MemoryLimits | null) {

View file

@ -13,10 +13,10 @@ import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom"; import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
import { announcementsDialogAtom } from "@/atoms/layout/dialogs.atom"; import { announcementsDialogAtom } from "@/atoms/layout/dialogs.atom";
import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom"; import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom";
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { removeChatTabAtom, syncChatTabAtom, type Tab } from "@/atoms/tabs/tabs.atom"; import { removeChatTabAtom, syncChatTabAtom, type Tab } from "@/atoms/tabs/tabs.atom";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { deleteSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { ActionLogDialog } from "@/components/agent-action-log/action-log-dialog"; import { ActionLogDialog } from "@/components/agent-action-log/action-log-dialog";
import { AnnouncementSpotlight } from "@/components/announcements/AnnouncementSpotlight"; import { AnnouncementSpotlight } from "@/components/announcements/AnnouncementSpotlight";
import { AnnouncementsDialog } from "@/components/announcements/AnnouncementsDialog"; import { AnnouncementsDialog } from "@/components/announcements/AnnouncementsDialog";
@ -47,7 +47,7 @@ import { useInbox } from "@/hooks/use-inbox";
import { useIsMobile } from "@/hooks/use-mobile"; import { useIsMobile } from "@/hooks/use-mobile";
import { useArchiveThread, useDeleteThread, useRenameThread } from "@/hooks/use-thread-mutations"; import { useArchiveThread, useDeleteThread, useRenameThread } from "@/hooks/use-thread-mutations";
import { notificationsApiService } from "@/lib/apis/notifications-api.service"; import { notificationsApiService } from "@/lib/apis/notifications-api.service";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { getLoginPath, logout } from "@/lib/auth-utils"; import { getLoginPath, logout } from "@/lib/auth-utils";
import { fetchThreads } from "@/lib/chat/thread-persistence"; import { fetchThreads } from "@/lib/chat/thread-persistence";
import { resetUser, trackLogout } from "@/lib/posthog/events"; import { resetUser, trackLogout } from "@/lib/posthog/events";
@ -397,7 +397,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const handleSearchSpaceSettings = useCallback( const handleSearchSpaceSettings = useCallback(
(space: SearchSpace) => { (space: SearchSpace) => {
router.push(`/dashboard/${space.id}/search-space-settings`); router.push(`/dashboard/${space.id}/workspace-settings`);
}, },
[router] [router]
); );
@ -595,7 +595,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
); );
const handleSettings = useCallback(() => { const handleSettings = useCallback(() => {
router.push(`/dashboard/${searchSpaceId}/search-space-settings`); router.push(`/dashboard/${searchSpaceId}/workspace-settings`);
}, [router, searchSpaceId]); }, [router, searchSpaceId]);
const handleManageMembers = useCallback(() => { const handleManageMembers = useCallback(() => {
@ -699,7 +699,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
// Detect if we're on the chat page (needs overflow-hidden for chat's own scroll) // Detect if we're on the chat page (needs overflow-hidden for chat's own scroll)
const isChatPage = pathname?.includes("/new-chat") ?? false; const isChatPage = pathname?.includes("/new-chat") ?? false;
const isUserSettingsPage = pathname?.includes("/user-settings") === true; const isUserSettingsPage = pathname?.includes("/user-settings") === true;
const isSearchSpaceSettingsPage = pathname?.includes("/search-space-settings") === true; const isSearchSpaceSettingsPage = pathname?.includes("/workspace-settings") === true;
const isTeamPage = pathname?.endsWith("/team") === true; const isTeamPage = pathname?.endsWith("/team") === true;
const isAutomationsPage = pathname?.includes("/automations") === true; const isAutomationsPage = pathname?.includes("/automations") === true;
const useWorkspacePanel = const useWorkspacePanel =

View file

@ -7,7 +7,7 @@ import { useTranslations } from "next-intl";
import { useState } from "react"; import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import * as z from "zod"; import * as z from "zod";
import { createSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { createSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,

View file

@ -1 +1 @@
export { CreateSearchSpaceDialog } from "./CreateSearchSpaceDialog"; export { CreateSearchSpaceDialog } from "./CreateWorkspaceDialog";

View file

@ -3,8 +3,8 @@
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom"; import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { activeTabAtom } from "@/atoms/tabs/tabs.atom"; import { activeTabAtom } from "@/atoms/tabs/tabs.atom";
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { ActionLogButton } from "@/components/agent-action-log/action-log-button"; import { ActionLogButton } from "@/components/agent-action-log/action-log-button";
import { ChatShareButton } from "@/components/new-chat/chat-share-button"; import { ChatShareButton } from "@/components/new-chat/chat-share-button";
import { ArtifactsToggleButton } from "@/features/chat-artifacts"; import { ArtifactsToggleButton } from "@/features/chat-artifacts";
@ -16,7 +16,7 @@ interface HeaderProps {
export function Header({ mobileMenuTrigger }: HeaderProps) { export function Header({ mobileMenuTrigger }: HeaderProps) {
const pathname = usePathname(); const pathname = usePathname();
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const activeTab = useAtomValue(activeTabAtom); const activeTab = useAtomValue(activeTabAtom);
const isFreePage = pathname?.startsWith("/free") ?? false; const isFreePage = pathname?.startsWith("/free") ?? false;
@ -56,7 +56,7 @@ export function Header({ mobileMenuTrigger }: HeaderProps) {
id: currentThreadState.id, id: currentThreadState.id,
visibility: currentThreadState.visibility, visibility: currentThreadState.visibility,
created_by_id: null, created_by_id: null,
search_space_id: currentThreadState.searchSpaceId, workspace_id: currentThreadState.searchSpaceId,
title: "", title: "",
archived: false, archived: false,
created_at: "", created_at: "",

View file

@ -7,7 +7,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import type { NavItem, SearchSpace, User } from "../../types/layout.types"; import type { NavItem, SearchSpace, User } from "../../types/layout.types";
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile"; import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
import { SearchSpaceAvatar } from "./SearchSpaceAvatar"; import { SearchSpaceAvatar } from "./WorkspaceAvatar";
interface IconRailProps { interface IconRailProps {
searchSpaces: SearchSpace[]; searchSpaces: SearchSpace[];

View file

@ -1,3 +1,3 @@
export { IconRail } from "./IconRail"; export { IconRail } from "./IconRail";
export { NavIcon } from "./NavIcon"; export { NavIcon } from "./NavIcon";
export { SearchSpaceAvatar } from "./SearchSpaceAvatar"; export { SearchSpaceAvatar } from "./WorkspaceAvatar";

View file

@ -35,7 +35,7 @@ import {
folderWatchDialogOpenAtom, folderWatchDialogOpenAtom,
folderWatchInitialFolderAtom, folderWatchInitialFolderAtom,
} from "@/atoms/folder-sync/folder-sync.atoms"; } from "@/atoms/folder-sync/folder-sync.atoms";
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog"; import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog";
import type { DocumentNodeDoc } from "@/components/documents/DocumentNode"; import type { DocumentNodeDoc } from "@/components/documents/DocumentNode";
import { DocumentsFilters } from "@/components/documents/DocumentsFilters"; import { DocumentsFilters } from "@/components/documents/DocumentsFilters";
@ -76,7 +76,7 @@ import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service"; import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service";
import { documentsApiService } from "@/lib/apis/documents-api.service"; import { documentsApiService } from "@/lib/apis/documents-api.service";
import { foldersApiService } from "@/lib/apis/folders-api.service"; import { foldersApiService } from "@/lib/apis/folders-api.service";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { authenticatedFetch } from "@/lib/auth-fetch"; import { authenticatedFetch } from "@/lib/auth-fetch";
import { getMentionDocKey } from "@/lib/chat/mention-doc-key"; import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
import { buildBackendUrl } from "@/lib/env-config"; import { buildBackendUrl } from "@/lib/env-config";
@ -228,7 +228,7 @@ function AuthenticatedDocumentsSidebarBase({
const platformElectronAPI = useElectronAPI(); const platformElectronAPI = useElectronAPI();
const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null; const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null;
const { etlService } = useRuntimeConfig(); const { etlService } = useRuntimeConfig();
const searchSpaceId = Number(params.search_space_id); const searchSpaceId = Number(params.workspace_id);
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom); const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
const openEditorPanel = useSetAtom(openEditorPanelAtom); const openEditorPanel = useSetAtom(openEditorPanelAtom);
const { data: agentFlags } = useAtomValue(agentFlagsAtom); const { data: agentFlags } = useAtomValue(agentFlagsAtom);
@ -430,7 +430,7 @@ function AuthenticatedDocumentsSidebarBase({
path: meta.folder_path as string, path: meta.folder_path as string,
name: bf.name, name: bf.name,
rootFolderId: bf.id, rootFolderId: bf.id,
searchSpaceId: bf.search_space_id, searchSpaceId: bf.workspace_id,
excludePatterns: (meta.exclude_patterns as string[]) ?? [], excludePatterns: (meta.exclude_patterns as string[]) ?? [],
fileExtensions: (meta.file_extensions as string[] | null) ?? null, fileExtensions: (meta.file_extensions as string[] | null) ?? null,
active: true, active: true,
@ -583,7 +583,7 @@ function AuthenticatedDocumentsSidebarBase({
await foldersApiService.createFolder({ await foldersApiService.createFolder({
name, name,
parent_id: createFolderParentId, parent_id: createFolderParentId,
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
}); });
toast.success("Folder created"); toast.success("Folder created");
if (createFolderParentId !== null) { if (createFolderParentId !== null) {
@ -751,7 +751,7 @@ function AuthenticatedDocumentsSidebarBase({
.trim() .trim()
.slice(0, 80) || "folder"; .slice(0, 80) || "folder";
await doExport( await doExport(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, { buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/export`, {
folder_id: ctx.folder.id, folder_id: ctx.folder.id,
}), }),
`${safeName}.zip` `${safeName}.zip`
@ -805,7 +805,7 @@ function AuthenticatedDocumentsSidebarBase({
.trim() .trim()
.slice(0, 80) || "folder"; .slice(0, 80) || "folder";
await doExport( await doExport(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, { buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/export`, {
folder_id: folder.id, folder_id: folder.id,
}), }),
`${safeName}.zip` `${safeName}.zip`
@ -828,7 +828,7 @@ function AuthenticatedDocumentsSidebarBase({
const endpoint = const endpoint =
doc.document_type === "USER_MEMORY" doc.document_type === "USER_MEMORY"
? buildBackendUrl("/api/v1/users/me/memory") ? buildBackendUrl("/api/v1/users/me/memory")
: buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory`); : buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory`);
const response = await authenticatedFetch(endpoint, { method: "GET" }); const response = await authenticatedFetch(endpoint, { method: "GET" });
if (!response.ok) { if (!response.ok) {
const errorData = await response.json().catch(() => ({ detail: "Export failed" })); const errorData = await response.json().catch(() => ({ detail: "Export failed" }));
@ -856,7 +856,7 @@ function AuthenticatedDocumentsSidebarBase({
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${doc.id}/export`, { buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/documents/${doc.id}/export`, {
format, format,
}), }),
{ method: "GET" } { method: "GET" }
@ -1038,7 +1038,7 @@ function AuthenticatedDocumentsSidebarBase({
const endpoint = const endpoint =
doc.document_type === "USER_MEMORY" doc.document_type === "USER_MEMORY"
? buildBackendUrl("/api/v1/users/me/memory/reset") ? buildBackendUrl("/api/v1/users/me/memory/reset")
: buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory/reset`); : buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory/reset`);
try { try {
const response = await authenticatedFetch(endpoint, { method: "POST" }); const response = await authenticatedFetch(endpoint, { method: "POST" });
if (!response.ok) { if (!response.ok) {

View file

@ -165,7 +165,7 @@ export function InboxSidebarContent({
const router = useRouter(); const router = useRouter();
const params = useParams(); const params = useParams();
const isMobile = !useMediaQuery("(min-width: 640px)"); const isMobile = !useMediaQuery("(min-width: 640px)");
const searchSpaceId = params?.search_space_id ? Number(params.search_space_id) : null; const searchSpaceId = params?.workspace_id ? Number(params.workspace_id) : null;
const [, setTargetCommentId] = useAtom(setTargetCommentIdAtom); const [, setTargetCommentId] = useAtom(setTargetCommentIdAtom);
@ -207,7 +207,7 @@ export function InboxSidebarContent({
queryFn: () => queryFn: () =>
notificationsApiService.getNotifications({ notificationsApiService.getNotifications({
queryParams: { queryParams: {
search_space_id: searchSpaceId ?? undefined, workspace_id: searchSpaceId ?? undefined,
type: searchTypeFilter, type: searchTypeFilter,
search: debouncedSearch.trim(), search: debouncedSearch.trim(),
limit: 50, limit: 50,
@ -363,7 +363,7 @@ export function InboxSidebarContent({
if (item.type === "new_mention") { if (item.type === "new_mention") {
if (isNewMentionMetadata(item.metadata)) { if (isNewMentionMetadata(item.metadata)) {
const searchSpaceId = item.search_space_id; const searchSpaceId = item.workspace_id;
const threadId = item.metadata.thread_id; const threadId = item.metadata.thread_id;
const commentId = item.metadata.comment_id; const commentId = item.metadata.comment_id;
@ -381,7 +381,7 @@ export function InboxSidebarContent({
} }
} else if (item.type === "comment_reply") { } else if (item.type === "comment_reply") {
if (isCommentReplyMetadata(item.metadata)) { if (isCommentReplyMetadata(item.metadata)) {
const searchSpaceId = item.search_space_id; const searchSpaceId = item.workspace_id;
const threadId = item.metadata.thread_id; const threadId = item.metadata.thread_id;
const replyId = item.metadata.reply_id; const replyId = item.metadata.reply_id;

View file

@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"; import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types"; import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types";
import { SearchSpaceAvatar } from "../icon-rail/SearchSpaceAvatar"; import { SearchSpaceAvatar } from "../icon-rail/WorkspaceAvatar";
import { Sidebar } from "./Sidebar"; import { Sidebar } from "./Sidebar";
interface MobileSidebarProps { interface MobileSidebarProps {

View file

@ -377,7 +377,7 @@ function SidebarUsageFooter({
onNavigate?: () => void; onNavigate?: () => void;
}) { }) {
const params = useParams(); const params = useParams();
const searchSpaceId = params?.search_space_id ?? ""; const searchSpaceId = params?.workspace_id ?? "";
const isAnonymous = useIsAnonymous(); const isAnonymous = useIsAnonymous();
if (isCollapsed) return null; if (isCollapsed) return null;

View file

@ -104,7 +104,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl( buildBackendUrl(
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content` `/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/editor-content`
), ),
{ method: "GET" } { method: "GET" }
); );
@ -154,7 +154,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
setSaving(true); setSaving(true);
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/save`), buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/save`),
{ {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@ -313,7 +313,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl( buildBackendUrl(
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/download-markdown` `/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/download-markdown`
), ),
{ method: "GET" } { method: "GET" }
); );

View file

@ -51,7 +51,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
// Use Jotai atom for visibility (single source of truth) // Use Jotai atom for visibility (single source of truth)
const currentThreadState = useAtomValue(currentThreadAtom); const currentThreadState = useAtomValue(currentThreadAtom);
const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.search_space_id ?? 0); const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.workspace_id ?? 0);
// Snapshot creation mutation // Snapshot creation mutation
const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue( const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue(
@ -139,9 +139,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => onClick={() =>
router.push( router.push(`/dashboard/${thread.workspace_id}/workspace-settings/public-links`)
`/dashboard/${thread.search_space_id}/search-space-settings/public-links`
)
} }
className="size-8 bg-muted/50 hover:bg-accent hover:text-accent-foreground" className="size-8 bg-muted/50 hover:bg-accent hover:text-accent-foreground"
> >

View file

@ -316,7 +316,7 @@ export const DocumentMentionPicker = forwardRef<
const titleSearchParams = useMemo( const titleSearchParams = useMemo(
() => ({ () => ({
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
page: 0, page: 0,
page_size: PAGE_SIZE, page_size: PAGE_SIZE,
...(isSearchValid ? { title: debouncedSearch.trim() } : {}), ...(isSearchValid ? { title: debouncedSearch.trim() } : {}),
@ -362,7 +362,7 @@ export const DocumentMentionPicker = forwardRef<
try { try {
const queryParams = { const queryParams = {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
page: nextPage, page: nextPage,
page_size: PAGE_SIZE, page_size: PAGE_SIZE,
...(isSearchValid ? { title: debouncedSearch.trim() } : {}), ...(isSearchValid ? { title: debouncedSearch.trim() } : {}),

View file

@ -146,7 +146,7 @@ export function ImageModelSelector({
function manageModelConnections() { function manageModelConnections() {
setOpen(false); setOpen(false);
router.push(`/dashboard/${searchSpaceId}/search-space-settings/models`); router.push(`/dashboard/${searchSpaceId}/workspace-settings/models`);
} }
const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => { const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => {

View file

@ -149,7 +149,7 @@ export function ModelSelector({
function manageModelConnections() { function manageModelConnections() {
setOpen(false); setOpen(false);
router.push(`/dashboard/${searchSpaceId}/search-space-settings/models`); router.push(`/dashboard/${searchSpaceId}/workspace-settings/models`);
} }
const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => { const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => {

View file

@ -69,9 +69,9 @@ export const PromptPicker = forwardRef<PromptPickerRef, PromptPickerProps>(funct
const createPromptIndex = filtered.length; const createPromptIndex = filtered.length;
const totalItems = filtered.length + 1; const totalItems = filtered.length + 1;
const searchSpaceId = Array.isArray(params?.search_space_id) const searchSpaceId = Array.isArray(params?.workspace_id)
? params.search_space_id[0] ? params.workspace_id[0]
: params?.search_space_id; : params?.workspace_id;
const handleSelect = useCallback( const handleSelect = useCallback(
(index: number) => { (index: number) => {

View file

@ -7,8 +7,8 @@ import { useTheme } from "next-themes";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms"; import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useIsMobile } from "@/hooks/use-mobile"; import { useIsMobile } from "@/hooks/use-mobile";
import { useZeroDocumentTypeCounts } from "@/hooks/use-zero-document-type-counts"; import { useZeroDocumentTypeCounts } from "@/hooks/use-zero-document-type-counts";
@ -391,7 +391,7 @@ export function OnboardingTour() {
// Get user data // Get user data
const { data: user } = useAtomValue(currentUserAtom); const { data: user } = useAtomValue(currentUserAtom);
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
// Fetch threads data // Fetch threads data
const { data: threadsData } = useQuery({ const { data: threadsData } = useQuery({

View file

@ -28,7 +28,7 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
}); });
// Redirect to the new chat page with cloned content // Redirect to the new chat page with cloned content
router.push(`/dashboard/${response.search_space_id}/new-chat/${response.thread_id}`); router.push(`/dashboard/${response.workspace_id}/new-chat/${response.thread_id}`);
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : "Failed to copy chat"; const message = error instanceof Error ? error.message : "Failed to copy chat";
toast.error(message); toast.error(message);

View file

@ -38,7 +38,7 @@ export function AutoReloadSettings() {
const pathname = usePathname(); const pathname = usePathname();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const searchSpaceId = Number(params?.search_space_id); const searchSpaceId = Number(params?.workspace_id);
const [enabled, setEnabled] = useState(false); const [enabled, setEnabled] = useState(false);
const [thresholdInput, setThresholdInput] = useState(""); const [thresholdInput, setThresholdInput] = useState("");
@ -79,7 +79,7 @@ export function AutoReloadSettings() {
const setupMutation = useMutation({ const setupMutation = useMutation({
mutationFn: () => mutationFn: () =>
stripeApiService.createAutoReloadSetupSession({ search_space_id: searchSpaceId }), stripeApiService.createAutoReloadSetupSession({ workspace_id: searchSpaceId }),
onSuccess: (response) => { onSuccess: (response) => {
window.location.assign(response.checkout_url); window.location.assign(response.checkout_url);
}, },

View file

@ -37,7 +37,7 @@ const formatUsd = (micros: number) => {
export function BuyCreditsContent() { export function BuyCreditsContent() {
const params = useParams(); const params = useParams();
const searchSpaceId = Number(params?.search_space_id); const searchSpaceId = Number(params?.workspace_id);
const [quantity, setQuantity] = useState(1); const [quantity, setQuantity] = useState(1);
// Raw text of the amount field so the user can clear it while typing; // Raw text of the amount field so the user can clear it while typing;
// committed back to a clamped integer on blur. // committed back to a clamped integer on blur.
@ -176,7 +176,7 @@ export function BuyCreditsContent() {
<Button <Button
className="w-full" className="w-full"
disabled={purchaseMutation.isPending} disabled={purchaseMutation.isPending}
onClick={() => purchaseMutation.mutate({ quantity, search_space_id: searchSpaceId })} onClick={() => purchaseMutation.mutate({ quantity, workspace_id: searchSpaceId })}
> >
{purchaseMutation.isPending ? ( {purchaseMutation.isPending ? (
<> <>

View file

@ -32,7 +32,7 @@ const formatRewardUsd = (micros: number) => {
export function EarnCreditsContent() { export function EarnCreditsContent() {
const params = useParams(); const params = useParams();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const searchSpaceId = params?.search_space_id ?? ""; const searchSpaceId = params?.workspace_id ?? "";
useEffect(() => { useEffect(() => {
trackIncentivePageViewed(); trackIncentivePageViewed();

View file

@ -8,13 +8,13 @@ import { toast } from "sonner";
import { import {
updateSearchSpaceApiAccessMutationAtom, updateSearchSpaceApiAccessMutationAtom,
updateSearchSpaceMutationAtom, updateSearchSpaceMutationAtom,
} from "@/atoms/search-spaces/search-space-mutation.atoms"; } from "@/atoms/workspaces/workspace-mutation.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { authenticatedFetch } from "@/lib/auth-fetch"; import { authenticatedFetch } from "@/lib/auth-fetch";
import { buildBackendUrl } from "@/lib/env-config"; import { buildBackendUrl } from "@/lib/env-config";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
@ -57,7 +57,7 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
setIsExporting(true); setIsExporting(true);
try { try {
const response = await authenticatedFetch( const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`), buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/export`),
{ method: "GET" } { method: "GET" }
); );
if (!response.ok) { if (!response.ok) {

View file

@ -148,7 +148,7 @@ export function ConnectionSettingsDialog({
base_url: data.base_url, base_url: data.base_url,
api_key: apiKeyForTest, api_key: apiKeyForTest,
scope: "SEARCH_SPACE", scope: "SEARCH_SPACE",
search_space_id: connection.search_space_id, workspace_id: connection.workspace_id,
extra: connection.extra ?? {}, extra: connection.extra ?? {},
enabled: connection.enabled, enabled: connection.enabled,
models: [], models: [],

View file

@ -138,7 +138,7 @@ export function ModelProviderConnectionsPanel({
base_url: draft.base_url, base_url: draft.base_url,
api_key: draft.api_key, api_key: draft.api_key,
scope: "SEARCH_SPACE" as const, scope: "SEARCH_SPACE" as const,
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
extra: draft.extra, extra: draft.extra,
enabled: true, enabled: true,
models, models,
@ -171,7 +171,7 @@ export function ModelProviderConnectionsPanel({
base_url: null, base_url: null,
api_key: null, api_key: null,
scope: "SEARCH_SPACE", scope: "SEARCH_SPACE",
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
extra: {}, extra: {},
enabled: true, enabled: true,
models: [], models: [],
@ -190,7 +190,7 @@ export function ModelProviderConnectionsPanel({
base_url: draft.base_url, base_url: draft.base_url,
api_key: draft.api_key, api_key: draft.api_key,
scope: "SEARCH_SPACE", scope: "SEARCH_SPACE",
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
extra: draft.extra, extra: draft.extra,
enabled: true, enabled: true,
models: [], models: [],

View file

@ -5,13 +5,13 @@ import { useAtomValue } from "jotai";
import { AlertTriangle, Info } from "lucide-react"; import { AlertTriangle, Info } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { updateSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { updateSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
import { Spinner } from "../ui/spinner"; import { Spinner } from "../ui/spinner";

View file

@ -279,7 +279,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const { data: roles = [], isLoading: rolesLoading } = useQuery({ const { data: roles = [], isLoading: rolesLoading } = useQuery({
queryKey: cacheKeys.roles.all(searchSpaceId.toString()), queryKey: cacheKeys.roles.all(searchSpaceId.toString()),
queryFn: () => rolesApiService.getRoles({ search_space_id: searchSpaceId }), queryFn: () => rolesApiService.getRoles({ workspace_id: searchSpaceId }),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,
}); });
@ -311,7 +311,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
} }
): Promise<Role> => { ): Promise<Role> => {
const request: UpdateRoleRequest = { const request: UpdateRoleRequest = {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
role_id: roleId, role_id: roleId,
data: data, data: data,
}; };
@ -323,7 +323,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const handleDeleteRole = useCallback( const handleDeleteRole = useCallback(
async (roleId: number): Promise<boolean> => { async (roleId: number): Promise<boolean> => {
const request: DeleteRoleRequest = { const request: DeleteRoleRequest = {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
role_id: roleId, role_id: roleId,
}; };
await deleteRole(request); await deleteRole(request);
@ -335,7 +335,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const handleCreateRole = useCallback( const handleCreateRole = useCallback(
async (roleData: CreateRoleRequest["data"]): Promise<Role> => { async (roleData: CreateRoleRequest["data"]): Promise<Role> => {
const request: CreateRoleRequest = { const request: CreateRoleRequest = {
search_space_id: searchSpaceId, workspace_id: searchSpaceId,
data: roleData, data: roleData,
}; };
return await createRole(request); return await createRole(request);

View file

@ -364,7 +364,7 @@ export function DocumentUploadTab({
batch.map((e) => e.file), batch.map((e) => e.file),
{ {
folder_name: folderUpload.folderName, folder_name: folderUpload.folderName,
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
relative_paths: batch.map((e) => e.relativePath), relative_paths: batch.map((e) => e.relativePath),
root_folder_id: rootFolderId, root_folder_id: rootFolderId,
use_vision_llm: useVisionLlm, use_vision_llm: useVisionLlm,
@ -413,7 +413,7 @@ export function DocumentUploadTab({
uploadDocuments( uploadDocuments(
{ {
files: rawFiles, files: rawFiles,
search_space_id: Number(searchSpaceId), workspace_id: Number(searchSpaceId),
use_vision_llm: useVisionLlm, use_vision_llm: useVisionLlm,
processing_mode: processingMode, processing_mode: processingMode,
}, },

View file

@ -9,7 +9,7 @@ import {
AutomationModelFields, AutomationModelFields,
type AutomationModelSelection, type AutomationModelSelection,
} from "@/app/dashboard/[workspace_id]/automations/components/builder/automation-model-fields"; } from "@/app/dashboard/[workspace_id]/automations/components/builder/automation-model-fields";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { JsonView } from "@/components/json-view"; import { JsonView } from "@/components/json-view";
import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { TextShimmerLoader } from "@/components/prompt-kit/loader";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
@ -27,7 +27,7 @@ import {
} from "@/lib/posthog/events"; } from "@/lib/posthog/events";
import { AutomationDraftPreview } from "./automation-draft-preview"; import { AutomationDraftPreview } from "./automation-draft-preview";
const editArgsSchema = automationCreateRequest.omit({ search_space_id: true }); const editArgsSchema = automationCreateRequest.omit({ workspace_id: true });
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Result discrimination — mirrors the backend return shapes in // Result discrimination — mirrors the backend return shapes in
@ -35,7 +35,7 @@ const editArgsSchema = automationCreateRequest.omit({ search_space_id: true });
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type AutomationCreateContext = { type AutomationCreateContext = {
search_space_id?: number; workspace_id?: number;
}; };
interface SavedResult { interface SavedResult {
@ -81,7 +81,7 @@ function hasStatus(value: unknown, status: string): boolean {
// //
// Edit toggle reuses the same primitives as the Create-via-JSON page: raw // Edit toggle reuses the same primitives as the Create-via-JSON page: raw
// textarea, Format, Zod validation against ``AutomationCreate`` (minus the // textarea, Format, Zod validation against ``AutomationCreate`` (minus the
// ``search_space_id`` field, which the backend injects). Approve dispatches // ``workspace_id`` field, which the backend injects). Approve dispatches
// an ``edit`` decision with the parsed args when edits are pending, otherwise // an ``edit`` decision with the parsed args when edits are pending, otherwise
// a plain ``approve``. Multi-turn chat refinement still works as a fallback. // a plain ``approve``. Multi-turn chat refinement still works as a fallback.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -110,7 +110,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
// Per-automation model selection. The card always supplies models (chosen // Per-automation model selection. The card always supplies models (chosen
// here, not snapshotted from the search space), so Approve dispatches an // here, not snapshotted from the search space), so Approve dispatches an
// `edit` decision carrying `definition.models`. // `edit` decision carrying `definition.models`.
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const eligibleModels = useAutomationEligibleModels(); const eligibleModels = useAutomationEligibleModels();
const [modelSelection, setModelSelection] = useState<AutomationModelSelection>({ const [modelSelection, setModelSelection] = useState<AutomationModelSelection>({
chatModelId: 0, chatModelId: 0,
@ -156,7 +156,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
const plan = Array.isArray(baseDefinition.plan) ? baseDefinition.plan : []; const plan = Array.isArray(baseDefinition.plan) ? baseDefinition.plan : [];
const triggers = Array.isArray(baseArgs.triggers) ? baseArgs.triggers : []; const triggers = Array.isArray(baseArgs.triggers) ? baseArgs.triggers : [];
trackAutomationChatApproved({ trackAutomationChatApproved({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
edited: pendingEdits !== null, edited: pendingEdits !== null,
task_count: plan.length, task_count: plan.length,
trigger_type: trigger_type:
@ -191,7 +191,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
if (phase !== "pending" || !canReject || isEditing) return; if (phase !== "pending" || !canReject || isEditing) return;
setRejected(); setRejected();
trackAutomationChatRejected({ trackAutomationChatRejected({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
}); });
onDecision({ type: "reject", message: "User rejected the automation draft." }); onDecision({ type: "reject", message: "User rejected the automation draft." });
}, [phase, canReject, isEditing, setRejected, onDecision, searchSpaceId]); }, [phase, canReject, isEditing, setRejected, onDecision, searchSpaceId]);
@ -268,7 +268,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
setPendingEdits(parsed); setPendingEdits(parsed);
setIsEditing(false); setIsEditing(false);
trackAutomationChatDraftEdited({ trackAutomationChatDraftEdited({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
}); });
}} }}
onCancel={() => setIsEditing(false)} onCancel={() => setIsEditing(false)}
@ -385,7 +385,7 @@ function JsonEditor({ initialValue, onSave, onCancel }: JsonEditorProps) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
function SavedCard({ result }: { result: SavedResult }) { function SavedCard({ result }: { result: SavedResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false); const tracked = useRef(false);
useEffect(() => { useEffect(() => {
if (tracked.current) return; if (tracked.current) return;
@ -393,7 +393,7 @@ function SavedCard({ result }: { result: SavedResult }) {
trackAutomationChatCreateSucceeded({ trackAutomationChatCreateSucceeded({
automation_id: result.automation_id, automation_id: result.automation_id,
name: result.name, name: result.name,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
}); });
}, [result.automation_id, result.name, searchSpaceId]); }, [result.automation_id, result.name, searchSpaceId]);
@ -429,7 +429,7 @@ function SavedCard({ result }: { result: SavedResult }) {
} }
function InvalidCard({ result }: { result: InvalidResult }) { function InvalidCard({ result }: { result: InvalidResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false); const tracked = useRef(false);
useEffect(() => { useEffect(() => {
if (tracked.current) return; if (tracked.current) return;
@ -437,7 +437,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
trackAutomationChatCreateFailed({ trackAutomationChatCreateFailed({
reason: "invalid", reason: "invalid",
issue_count: result.issues.length, issue_count: result.issues.length,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
}); });
}, [result.issues.length, searchSpaceId]); }, [result.issues.length, searchSpaceId]);
@ -464,7 +464,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
} }
function ErrorCard({ result }: { result: ErrorResult }) { function ErrorCard({ result }: { result: ErrorResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false); const tracked = useRef(false);
useEffect(() => { useEffect(() => {
if (tracked.current) return; if (tracked.current) return;
@ -472,7 +472,7 @@ function ErrorCard({ result }: { result: ErrorResult }) {
trackAutomationChatCreateFailed({ trackAutomationChatCreateFailed({
reason: "error", reason: "error",
message: result.message, message: result.message,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
}); });
}, [result.message, searchSpaceId]); }, [result.message, searchSpaceId]);
@ -531,7 +531,7 @@ export const CreateAutomationToolUI = ({
* Project raw args into the shape ``AutomationDraftPreview`` expects. * Project raw args into the shape ``AutomationDraftPreview`` expects.
* *
* The args dict is the full ``AutomationCreate`` payload (minus * The args dict is the full ``AutomationCreate`` payload (minus
* ``search_space_id`` which is injected server-side), so we trust the * ``workspace_id`` which is injected server-side), so we trust the
* top-level fields but defend against missing nested defaults. * top-level fields but defend against missing nested defaults.
*/ */
function extractDraft(args: Record<string, unknown>) { function extractDraft(args: Record<string, unknown>) {

View file

@ -87,7 +87,7 @@ External boundaries (LLM, embedding, chunking, Redis) are mocked in both suites.
1. **Database setup** — `TEST_DATABASE_URL` defaults to `surfsense_test`. Tables and extensions (`vector`, `pg_trgm`) are created once per session and dropped after. 1. **Database setup** — `TEST_DATABASE_URL` defaults to `surfsense_test`. Tables and extensions (`vector`, `pg_trgm`) are created once per session and dropped after.
2. **Transaction isolation** — Each test runs inside a savepoint that rolls back, so tests don't affect each other. 2. **Transaction isolation** — Each test runs inside a savepoint that rolls back, so tests don't affect each other.
3. **User creation** — Integration tests register a test user via `POST /auth/register` on first run, then log in for subsequent requests. 3. **User creation** — Integration tests register a test user via `POST /auth/register` on first run, then log in for subsequent requests.
4. **Search space discovery** — Tests call `GET /api/v1/searchspaces` and use the first available space. 4. **Search space discovery** — Tests call `GET /api/v1/workspaces` and use the first available space.
5. **Cleanup** — A session fixture purges stale documents before tests run. Per-test cleanup deletes documents via API, falling back to direct DB access for stuck records. 5. **Cleanup** — A session fixture purges stale documents before tests run. Per-test cleanup deletes documents via API, falling back to direct DB access for stuck records.
## Writing New Tests ## Writing New Tests

View file

@ -118,7 +118,7 @@ export type Trigger = z.infer<typeof trigger>;
// ============================================================================= // =============================================================================
export const automationCreateRequest = z.object({ export const automationCreateRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
name: z.string().min(1).max(200), name: z.string().min(1).max(200),
description: z.string().nullable().optional(), description: z.string().nullable().optional(),
definition: automationDefinition, definition: automationDefinition,
@ -136,7 +136,7 @@ export type AutomationUpdateRequest = z.infer<typeof automationUpdateRequest>;
export const automationSummary = z.object({ export const automationSummary = z.object({
id: z.number(), id: z.number(),
search_space_id: z.number(), workspace_id: z.number(),
name: z.string(), name: z.string(),
description: z.string().nullable().optional(), description: z.string().nullable().optional(),
status: automationStatus, status: automationStatus,
@ -159,7 +159,7 @@ export const automationListResponse = z.object({
export type AutomationListResponse = z.infer<typeof automationListResponse>; export type AutomationListResponse = z.infer<typeof automationListResponse>;
export const automationListParams = z.object({ export const automationListParams = z.object({
search_space_id: z.number(), workspace_id: z.number(),
limit: z.number().int().min(1).max(200).default(50), limit: z.number().int().min(1).max(200).default(50),
offset: z.number().int().min(0).default(0), offset: z.number().int().min(0).default(0),
}); });

View file

@ -52,8 +52,8 @@ export const mentionContext = z.object({
thread_id: z.number(), thread_id: z.number(),
thread_title: z.string(), thread_title: z.string(),
message_id: z.number(), message_id: z.number(),
search_space_id: z.number(), workspace_id: z.number(),
search_space_name: z.string(), workspace_name: z.string(),
}); });
export const mentionComment = z.object({ export const mentionComment = z.object({
@ -144,7 +144,7 @@ export const deleteCommentResponse = z.object({
* Get mentions * Get mentions
*/ */
export const getMentionsRequest = z.object({ export const getMentionsRequest = z.object({
search_space_id: z.number().optional(), workspace_id: z.number().optional(),
}); });
export const getMentionsResponse = z.object({ export const getMentionsResponse = z.object({

View file

@ -62,7 +62,7 @@ export const publicChatSnapshotDetail = z.object({
* List public chat snapshots for search space * List public chat snapshots for search space
*/ */
export const publicChatSnapshotsBySpaceRequest = z.object({ export const publicChatSnapshotsBySpaceRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const publicChatSnapshotsBySpaceResponse = z.object({ export const publicChatSnapshotsBySpaceResponse = z.object({

View file

@ -47,7 +47,7 @@ export const searchSourceConnector = z.object({
periodic_indexing_enabled: z.boolean(), periodic_indexing_enabled: z.boolean(),
indexing_frequency_minutes: z.number().nullable(), indexing_frequency_minutes: z.number().nullable(),
next_scheduled_at: z.string().nullable(), next_scheduled_at: z.string().nullable(),
search_space_id: z.number(), workspace_id: z.number(),
user_id: z.string(), user_id: z.string(),
created_at: z.string(), created_at: z.string(),
}); });
@ -72,7 +72,7 @@ export const getConnectorsRequest = z.object({
queryParams: paginationQueryParams queryParams: paginationQueryParams
.pick({ skip: true, limit: true }) .pick({ skip: true, limit: true })
.extend({ .extend({
search_space_id: z.number().or(z.string()).nullish(), workspace_id: z.number().or(z.string()).nullish(),
}) })
.nullish(), .nullish(),
}); });
@ -103,7 +103,7 @@ export const createConnectorRequest = z.object({
next_scheduled_at: true, next_scheduled_at: true,
}), }),
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number().or(z.string()), workspace_id: z.number().or(z.string()),
}), }),
}); });
@ -175,7 +175,7 @@ export const googleDriveIndexBody = z.object({
export const indexConnectorRequest = z.object({ export const indexConnectorRequest = z.object({
connector_id: z.number(), connector_id: z.number(),
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number().or(z.string()), workspace_id: z.number().or(z.string()),
start_date: z.string().optional(), start_date: z.string().optional(),
end_date: z.string().optional(), end_date: z.string().optional(),
}), }),
@ -185,7 +185,7 @@ export const indexConnectorRequest = z.object({
export const indexConnectorResponse = z.object({ export const indexConnectorResponse = z.object({
message: z.string(), message: z.string(),
connector_id: z.number(), connector_id: z.number(),
search_space_id: z.number(), workspace_id: z.number(),
indexing_from: z.string(), indexing_from: z.string(),
indexing_to: z.string(), indexing_to: z.string(),
}); });

View file

@ -46,7 +46,7 @@ export const document = z.object({
unique_identifier_hash: z.string().nullable(), unique_identifier_hash: z.string().nullable(),
created_at: z.string(), created_at: z.string(),
updated_at: z.string().nullable(), updated_at: z.string().nullable(),
search_space_id: z.number(), workspace_id: z.number(),
created_by_id: z.string().nullable().optional(), created_by_id: z.string().nullable().optional(),
created_by_name: z.string().nullable().optional(), created_by_name: z.string().nullable().optional(),
created_by_email: z.string().nullable().optional(), created_by_email: z.string().nullable().optional(),
@ -85,7 +85,7 @@ export const sortOrderEnum = z.enum(["asc", "desc"]);
export const getDocumentsRequest = z.object({ export const getDocumentsRequest = z.object({
queryParams: paginationQueryParams queryParams: paginationQueryParams
.extend({ .extend({
search_space_id: z.number().or(z.string()).optional(), workspace_id: z.number().or(z.string()).optional(),
document_types: z.array(documentTypeEnum).optional(), document_types: z.array(documentTypeEnum).optional(),
sort_by: documentSortByEnum.optional(), sort_by: documentSortByEnum.optional(),
sort_order: sortOrderEnum.optional(), sort_order: sortOrderEnum.optional(),
@ -112,7 +112,7 @@ export const getDocumentResponse = document;
* Create documents * Create documents
*/ */
export const createDocumentRequest = document export const createDocumentRequest = document
.pick({ document_type: true, search_space_id: true }) .pick({ document_type: true, workspace_id: true })
.extend({ .extend({
content: z.string().or(z.array(z.string())).or(z.array(extensionDocumentContent)), content: z.string().or(z.array(z.string())).or(z.array(extensionDocumentContent)),
}); });
@ -129,7 +129,7 @@ export const processingModeEnum = z.enum(["basic", "premium"]);
export const uploadDocumentRequest = z.object({ export const uploadDocumentRequest = z.object({
files: z.array(z.instanceof(File)), files: z.array(z.instanceof(File)),
search_space_id: z.number(), workspace_id: z.number(),
use_vision_llm: z.boolean().default(false), use_vision_llm: z.boolean().default(false),
processing_mode: processingModeEnum.default("basic"), processing_mode: processingModeEnum.default("basic"),
}); });
@ -148,7 +148,7 @@ export const uploadDocumentResponse = z.object({
*/ */
export const getDocumentsStatusRequest = z.object({ export const getDocumentsStatusRequest = z.object({
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number(), workspace_id: z.number(),
document_ids: z.array(z.number()).min(1), document_ids: z.array(z.number()).min(1),
}), }),
}); });
@ -175,7 +175,7 @@ export const getDocumentsStatusResponse = z.object({
export const searchDocumentsRequest = z.object({ export const searchDocumentsRequest = z.object({
queryParams: paginationQueryParams queryParams: paginationQueryParams
.extend({ .extend({
search_space_id: z.number().or(z.string()).optional(), workspace_id: z.number().or(z.string()).optional(),
document_types: z.array(documentTypeEnum).optional(), document_types: z.array(documentTypeEnum).optional(),
title: z.string().optional(), title: z.string().optional(),
}) })
@ -201,7 +201,7 @@ export const documentTitleRead = z.object({
export const searchDocumentTitlesRequest = z.object({ export const searchDocumentTitlesRequest = z.object({
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number(), workspace_id: z.number(),
title: z.string().optional(), title: z.string().optional(),
page: z.number().optional(), page: z.number().optional(),
page_size: z.number().optional(), page_size: z.number().optional(),
@ -219,7 +219,7 @@ export const searchDocumentTitlesResponse = z.object({
export const getDocumentTypeCountsRequest = z.object({ export const getDocumentTypeCountsRequest = z.object({
queryParams: z queryParams: z
.object({ .object({
search_space_id: z.number().or(z.string()).optional(), workspace_id: z.number().or(z.string()).optional(),
}) })
.nullish(), .nullish(),
}); });
@ -266,7 +266,7 @@ export const getDocumentChunksResponse = z.object({
*/ */
export const updateDocumentRequest = z.object({ export const updateDocumentRequest = z.object({
id: z.number(), id: z.number(),
data: document.pick({ search_space_id: true, document_type: true, content: true }), data: document.pick({ workspace_id: true, document_type: true, content: true }),
}); });
export const updateDocumentResponse = document; export const updateDocumentResponse = document;

View file

@ -5,7 +5,7 @@ export const folder = z.object({
name: z.string(), name: z.string(),
position: z.string(), position: z.string(),
parent_id: z.number().nullable(), parent_id: z.number().nullable(),
search_space_id: z.number(), workspace_id: z.number(),
created_by_id: z.string().nullable().optional(), created_by_id: z.string().nullable().optional(),
created_at: z.string(), created_at: z.string(),
updated_at: z.string(), updated_at: z.string(),
@ -15,7 +15,7 @@ export const folder = z.object({
export const folderCreateRequest = z.object({ export const folderCreateRequest = z.object({
name: z.string().min(1).max(255), name: z.string().min(1).max(255),
parent_id: z.number().nullable().optional(), parent_id: z.number().nullable().optional(),
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const folderUpdateRequest = z.object({ export const folderUpdateRequest = z.object({

View file

@ -7,7 +7,7 @@ import { z } from "zod";
export const imageGenerationListItem = z.object({ export const imageGenerationListItem = z.object({
id: z.number(), id: z.number(),
prompt: z.string(), prompt: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
created_at: z.string(), created_at: z.string(),
is_success: z.boolean(), is_success: z.boolean(),
image_count: z.number().nullish(), image_count: z.number().nullish(),

View file

@ -152,7 +152,7 @@ export const inboxItemMetadata = z.union([
export const inboxItem = z.object({ export const inboxItem = z.object({
id: z.number(), id: z.number(),
user_id: z.string(), user_id: z.string(),
search_space_id: z.number().nullable(), workspace_id: z.number().nullable(),
type: inboxItemTypeEnum, type: inboxItemTypeEnum,
title: z.string(), title: z.string(),
message: z.string(), message: z.string(),
@ -210,7 +210,7 @@ export type NotificationCategory = z.infer<typeof notificationCategory>;
*/ */
export const getNotificationsRequest = z.object({ export const getNotificationsRequest = z.object({
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number().optional(), workspace_id: z.number().optional(),
type: inboxItemTypeEnum.optional(), type: inboxItemTypeEnum.optional(),
category: notificationCategory.optional(), category: notificationCategory.optional(),
source_type: z.string().optional(), source_type: z.string().optional(),
@ -260,7 +260,7 @@ export const markAllNotificationsReadResponse = z.object({
* Request schema for getting unread count * Request schema for getting unread count
*/ */
export const getUnreadCountRequest = z.object({ export const getUnreadCountRequest = z.object({
search_space_id: z.number().optional(), workspace_id: z.number().optional(),
}); });
/** /**

View file

@ -5,7 +5,7 @@ export const invite = z.object({
id: z.number(), id: z.number(),
name: z.string().max(100).nullable().optional(), name: z.string().max(100).nullable().optional(),
invite_code: z.string(), invite_code: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
created_by_id: z.string().nullable(), created_by_id: z.string().nullable(),
role_id: z.number().nullable(), role_id: z.number().nullable(),
expires_at: z.string().nullable(), expires_at: z.string().nullable(),
@ -20,7 +20,7 @@ export const invite = z.object({
* Create invite * Create invite
*/ */
export const createInviteRequest = z.object({ export const createInviteRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
data: z.object({ data: z.object({
name: z.string().max(100).optional(), name: z.string().max(100).optional(),
role_id: z.number().nullable().optional(), role_id: z.number().nullable().optional(),
@ -35,7 +35,7 @@ export const createInviteResponse = invite;
* Get invites * Get invites
*/ */
export const getInvitesRequest = z.object({ export const getInvitesRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const getInvitesResponse = z.array(invite); export const getInvitesResponse = z.array(invite);
@ -44,7 +44,7 @@ export const getInvitesResponse = z.array(invite);
* Update invite * Update invite
*/ */
export const updateInviteRequest = z.object({ export const updateInviteRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
invite_id: z.number(), invite_id: z.number(),
data: z.object({ data: z.object({
name: z.string().max(100).optional(), name: z.string().max(100).optional(),
@ -61,7 +61,7 @@ export const updateInviteResponse = invite;
* Delete invite * Delete invite
*/ */
export const deleteInviteRequest = z.object({ export const deleteInviteRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
invite_id: z.number(), invite_id: z.number(),
}); });
@ -77,7 +77,7 @@ export const getInviteInfoRequest = z.object({
}); });
export const getInviteInfoResponse = z.object({ export const getInviteInfoResponse = z.object({
search_space_name: z.string(), workspace_name: z.string(),
role_name: z.string().nullable(), role_name: z.string().nullable(),
is_valid: z.boolean(), is_valid: z.boolean(),
message: z.string().nullable(), message: z.string().nullable(),
@ -92,8 +92,8 @@ export const acceptInviteRequest = z.object({
export const acceptInviteResponse = z.object({ export const acceptInviteResponse = z.object({
message: z.string(), message: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
search_space_name: z.string(), workspace_name: z.string(),
role_name: z.string().nullable(), role_name: z.string().nullable(),
}); });

View file

@ -19,7 +19,7 @@ export const log = z.object({
source: z.string().nullable().optional(), source: z.string().nullable().optional(),
log_metadata: z.record(z.string(), z.any()).nullable().optional(), log_metadata: z.record(z.string(), z.any()).nullable().optional(),
created_at: z.string(), created_at: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const logBase = log.omit({ id: true, created_at: true }); export const logBase = log.omit({ id: true, created_at: true });
@ -27,7 +27,7 @@ export const logBase = log.omit({ id: true, created_at: true });
/** /**
* Create log * Create log
*/ */
export const createLogRequest = logBase.extend({ search_space_id: z.number() }); export const createLogRequest = logBase.extend({ workspace_id: z.number() });
export const createLogResponse = log; export const createLogResponse = log;
/** /**
@ -48,7 +48,7 @@ export const deleteLogResponse = z.object({
* Get logs (list) * Get logs (list)
*/ */
export const logFilters = z.object({ export const logFilters = z.object({
search_space_id: z.number().optional(), workspace_id: z.number().optional(),
level: logLevelEnum.optional(), level: logLevelEnum.optional(),
status: logStatusEnum.optional(), status: logStatusEnum.optional(),
source: z.string().optional(), source: z.string().optional(),
@ -59,7 +59,7 @@ export const logFilters = z.object({
export const getLogsRequest = z.object({ export const getLogsRequest = z.object({
queryParams: paginationQueryParams queryParams: paginationQueryParams
.extend({ .extend({
search_space_id: z.number().optional(), workspace_id: z.number().optional(),
level: logLevelEnum.optional(), level: logLevelEnum.optional(),
status: logStatusEnum.optional(), status: logStatusEnum.optional(),
source: z.string().optional(), source: z.string().optional(),
@ -106,7 +106,7 @@ export const logSummary = z.object({
recent_failures: z.array(logFailure), recent_failures: z.array(logFailure),
}); });
export const getLogSummaryRequest = z.object({ export const getLogSummaryRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
hours: z.number().optional(), hours: z.number().optional(),
}); });
export const getLogSummaryResponse = logSummary; export const getLogSummaryResponse = logSummary;

View file

@ -37,7 +37,7 @@ export const mcpConnectorRead = z.object({
name: z.string(), name: z.string(),
connector_type: z.literal("MCP_CONNECTOR"), connector_type: z.literal("MCP_CONNECTOR"),
server_config: mcpServerConfig, server_config: mcpServerConfig,
search_space_id: z.number(), workspace_id: z.number(),
user_id: z.string(), user_id: z.string(),
created_at: z.string(), created_at: z.string(),
updated_at: z.string(), updated_at: z.string(),
@ -49,7 +49,7 @@ export const mcpConnectorRead = z.object({
export const createMCPConnectorRequest = z.object({ export const createMCPConnectorRequest = z.object({
data: mcpConnectorCreate, data: mcpConnectorCreate,
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number().or(z.string()), workspace_id: z.number().or(z.string()),
}), }),
}); });
@ -60,7 +60,7 @@ export const updateMCPConnectorRequest = z.object({
export const getMCPConnectorsRequest = z.object({ export const getMCPConnectorsRequest = z.object({
queryParams: z.object({ queryParams: z.object({
search_space_id: z.number().or(z.string()), workspace_id: z.number().or(z.string()),
}), }),
}); });

View file

@ -4,7 +4,7 @@ import { role } from "./roles.types";
export const membership = z.object({ export const membership = z.object({
id: z.number(), id: z.number(),
user_id: z.string(), user_id: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
role_id: z.number().nullable(), role_id: z.number().nullable(),
is_owner: z.boolean(), is_owner: z.boolean(),
joined_at: z.string(), joined_at: z.string(),
@ -21,7 +21,7 @@ export const membership = z.object({
* Get members * Get members
*/ */
export const getMembersRequest = z.object({ export const getMembersRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const getMembersResponse = z.array(membership); export const getMembersResponse = z.array(membership);
@ -30,7 +30,7 @@ export const getMembersResponse = z.array(membership);
* Update membership * Update membership
*/ */
export const updateMembershipRequest = z.object({ export const updateMembershipRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
membership_id: z.number(), membership_id: z.number(),
data: z.object({ data: z.object({
role_id: z.number().nullable(), role_id: z.number().nullable(),
@ -43,7 +43,7 @@ export const updateMembershipResponse = membership;
* Delete membership * Delete membership
*/ */
export const deleteMembershipRequest = z.object({ export const deleteMembershipRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
membership_id: z.number(), membership_id: z.number(),
}); });
@ -55,7 +55,7 @@ export const deleteMembershipResponse = z.object({
* Leave search space * Leave search space
*/ */
export const leaveSearchSpaceRequest = z.object({ export const leaveSearchSpaceRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const leaveSearchSpaceResponse = z.object({ export const leaveSearchSpaceResponse = z.object({
@ -66,12 +66,12 @@ export const leaveSearchSpaceResponse = z.object({
* Get my access * Get my access
*/ */
export const getMyAccessRequest = z.object({ export const getMyAccessRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const getMyAccessResponse = z.object({ export const getMyAccessResponse = z.object({
search_space_name: z.string(), workspace_name: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
is_owner: z.boolean(), is_owner: z.boolean(),
permissions: z.array(z.string()), permissions: z.array(z.string()),
role_name: z.string().nullable(), role_name: z.string().nullable(),

View file

@ -28,7 +28,7 @@ export const connectionRead = z.object({
api_key: z.string().nullable().optional(), api_key: z.string().nullable().optional(),
extra: z.record(z.string(), z.any()).default({}), extra: z.record(z.string(), z.any()).default({}),
scope: z.union([connectionScopeEnum, z.string()]), scope: z.union([connectionScopeEnum, z.string()]),
search_space_id: z.number().nullable().optional(), workspace_id: z.number().nullable().optional(),
user_id: z.string().nullable().optional(), user_id: z.string().nullable().optional(),
enabled: z.boolean(), enabled: z.boolean(),
has_api_key: z.boolean(), has_api_key: z.boolean(),
@ -57,7 +57,7 @@ export const connectionCreateRequest = z.object({
api_key: z.string().nullable().optional(), api_key: z.string().nullable().optional(),
extra: z.record(z.string(), z.any()).default({}), extra: z.record(z.string(), z.any()).default({}),
scope: connectionScopeEnum.default("SEARCH_SPACE"), scope: connectionScopeEnum.default("SEARCH_SPACE"),
search_space_id: z.number().nullable().optional(), workspace_id: z.number().nullable().optional(),
enabled: z.boolean().default(true), enabled: z.boolean().default(true),
models: z.array(modelSelection).default([]), models: z.array(modelSelection).default([]),
}); });

View file

@ -151,7 +151,7 @@ export const podcastDetail = z.object({
duration_seconds: z.number().nullable(), duration_seconds: z.number().nullable(),
error: z.string().nullable(), error: z.string().nullable(),
created_at: z.string(), created_at: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
thread_id: z.number().nullable(), thread_id: z.number().nullable(),
}); });
export type PodcastDetail = z.infer<typeof podcastDetail>; export type PodcastDetail = z.infer<typeof podcastDetail>;
@ -162,7 +162,7 @@ export const podcastSummary = z.object({
title: z.string(), title: z.string(),
status: podcastStatus, status: podcastStatus,
created_at: z.string(), created_at: z.string(),
search_space_id: z.number(), workspace_id: z.number(),
thread_id: z.number().nullish(), thread_id: z.number().nullish(),
}); });
export type PodcastSummary = z.infer<typeof podcastSummary>; export type PodcastSummary = z.infer<typeof podcastSummary>;

View file

@ -7,7 +7,7 @@ export const promptRead = z.object({
name: z.string(), name: z.string(),
prompt: z.string(), prompt: z.string(),
mode: z.enum(["transform", "explore"]), mode: z.enum(["transform", "explore"]),
search_space_id: z.number().nullable(), workspace_id: z.number().nullable(),
is_public: z.boolean(), is_public: z.boolean(),
version: z.number(), version: z.number(),
created_at: z.string(), created_at: z.string(),
@ -29,7 +29,7 @@ export const promptCreateRequest = z.object({
name: z.string().min(1).max(200), name: z.string().min(1).max(200),
prompt: z.string().min(1), prompt: z.string().min(1),
mode: z.enum(["transform", "explore"]), mode: z.enum(["transform", "explore"]),
search_space_id: z.number().nullable().optional(), workspace_id: z.number().nullable().optional(),
is_public: z.boolean().optional(), is_public: z.boolean().optional(),
}); });

View file

@ -47,7 +47,7 @@ export const clonePublicChatRequest = z.object({
export const clonePublicChatResponse = z.object({ export const clonePublicChatResponse = z.object({
thread_id: z.number(), thread_id: z.number(),
search_space_id: z.number(), workspace_id: z.number(),
}); });
// Type exports // Type exports

View file

@ -7,7 +7,7 @@ export const role = z.object({
permissions: z.array(z.string()), permissions: z.array(z.string()),
is_default: z.boolean(), is_default: z.boolean(),
is_system_role: z.boolean(), is_system_role: z.boolean(),
search_space_id: z.number(), workspace_id: z.number(),
created_at: z.string(), created_at: z.string(),
}); });
@ -15,7 +15,7 @@ export const role = z.object({
* Create role * Create role
*/ */
export const createRoleRequest = z.object({ export const createRoleRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
data: role.pick({ data: role.pick({
name: true, name: true,
description: true, description: true,
@ -30,7 +30,7 @@ export const createRoleResponse = role;
* Get roles * Get roles
*/ */
export const getRolesRequest = z.object({ export const getRolesRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
}); });
export const getRolesResponse = z.array(role); export const getRolesResponse = z.array(role);
@ -39,7 +39,7 @@ export const getRolesResponse = z.array(role);
* Get role by ID * Get role by ID
*/ */
export const getRoleByIdRequest = z.object({ export const getRoleByIdRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
role_id: z.number(), role_id: z.number(),
}); });
@ -49,7 +49,7 @@ export const getRoleByIdResponse = role;
* Update role * Update role
*/ */
export const updateRoleRequest = z.object({ export const updateRoleRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
role_id: z.number(), role_id: z.number(),
data: role data: role
.pick({ .pick({
@ -67,7 +67,7 @@ export const updateRoleResponse = role;
* Delete role * Delete role
*/ */
export const deleteRoleRequest = z.object({ export const deleteRoleRequest = z.object({
search_space_id: z.number(), workspace_id: z.number(),
role_id: z.number(), role_id: z.number(),
}); });

View file

@ -8,7 +8,7 @@ export const purchaseStatusEnum = z.enum(["pending", "completed", "failed"]);
export const createCreditCheckoutSessionRequest = z.object({ export const createCreditCheckoutSessionRequest = z.object({
quantity: z.number().int().min(1).max(10_000), quantity: z.number().int().min(1).max(10_000),
search_space_id: z.number().int().min(1), workspace_id: z.number().int().min(1),
}); });
export const createCreditCheckoutSessionResponse = z.object({ export const createCreditCheckoutSessionResponse = z.object({
@ -90,7 +90,7 @@ export const updateAutoReloadSettingsRequest = z.object({
}); });
export const createAutoReloadSetupSessionRequest = z.object({ export const createAutoReloadSetupSessionRequest = z.object({
search_space_id: z.number().int().min(1), workspace_id: z.number().int().min(1),
}); });
export const createAutoReloadSetupSessionResponse = z.object({ export const createAutoReloadSetupSessionResponse = z.object({

Some files were not shown because too many files have changed in this diff Show more