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>
You are **SurfSense's main agent**. Your job is to answer the user using their
knowledge base, lightweight web research, persistent memory, and **specialist
subagents** invoked via the `task` tool. You are an orchestrator — most
non-trivial work belongs on a specialist.
You are **SurfSense's main agent**, the orchestrator of an open-source
competitive intelligence platform. Users come to you to understand their
market: what competitors are doing, how audiences react, where rankings and
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}
</agent_identity>

View file

@ -1,8 +1,23 @@
<agent_identity>
You are **SurfSense's main agent**. Your job is to answer the user using their
shared team knowledge base, lightweight web research, persistent memory, and
**specialist subagents** invoked via the `task` tool. You are an orchestrator
— most non-trivial work belongs on a specialist.
You are **SurfSense's main agent**, the orchestrator of an open-source
competitive intelligence platform. This team comes to you to understand its
market: what competitors are doing, how audiences react, where rankings and
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}

View file

@ -1,5 +1,11 @@
<knowledge_base_first>
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
source for anything about their own uploaded files, documents, and notes —
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>`),
- the user's connected apps via `task(mcp_discovery, ...)` (Slack, Jira,
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.
For questions about the user's own files and notes, dispatch

View file

@ -1,4 +1,5 @@
<reminder>
Concise · KB-grounded · delegation-first · one `task` per turn · no direct
filesystem · persist memory when durable facts appear.
Concise · grounded in this turn's specialist data, never stale general
knowledge · delegation-first · no direct filesystem · persist memory when
durable facts appear.
</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
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
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
@ -113,6 +124,20 @@ user: "What did Maya say about the Q2 roadmap in Slack last week?"
timestamp.")
</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>
user: "What's the current USD/INR rate?"
→ 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 pg_trgm"))
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()

View file

@ -173,6 +173,37 @@ def apply_publication(conn: Connection) -> None:
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]:
rows = conn.execute(
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) {
return { ...buildUpdatePayload(formForPayload), status: automation.status };
}
const { workspace_id: _ignored, ...rest } = buildCreatePayload(
formForPayload,
workspaceId
);
const { workspace_id: _ignored, ...rest } = buildCreatePayload(formForPayload, workspaceId);
return rest;
}

View file

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

View file

@ -13,7 +13,7 @@ import {
modelConnectionsAtom,
modelRolesAtom,
} 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 { LayoutDataProvider } from "@/components/layout";
import { OnboardingTour } from "@/components/onboarding-tour";

View file

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

View file

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

View file

@ -6,7 +6,7 @@ import { AlertTriangle, Info, ShieldCheck, Trash2 } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner";
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 {
AlertDialog,

View file

@ -13,9 +13,9 @@ import {
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
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 { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
export function DesktopContent() {
const api = useElectronAPI();

View file

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

View file

@ -11,7 +11,5 @@ export default function UserSettingsLayout({
}) {
const { workspace_id } = use(params);
return (
<UserSettingsLayoutShell workspaceId={workspace_id}>{children}</UserSettingsLayoutShell>
);
return <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 hrefFor = (tab: SearchSpaceSettingsTab) =>
`/dashboard/${workspaceId}/search-space-settings/${tab}`;
`/dashboard/${workspaceId}/workspace-settings/${tab}`;
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">

View file

@ -6,5 +6,5 @@ export default async function SearchSpaceSettingsPage({
params: Promise<{ workspace_id: string }>;
}) {
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 { useTranslations } from "next-intl";
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 { Button } from "@/components/ui/button";
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 { Spinner } from "@/components/ui/spinner";
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";
type ShortcutKey = "generalAssist" | "quickAsk" | "screenshotAssist";

View file

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

View file

@ -2,7 +2,7 @@ import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query";
import { agentToolsApiService } from "@/lib/apis/agent-tools-api.service";
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) => ({
queryKey: cacheKeys.agentTools.all(),
@ -42,7 +42,7 @@ const hydratedForAtom = atom<string | null>(null);
*/
export const disabledToolsAtom = atom(
(get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
const hydratedFor = get(hydratedForAtom);
if (searchSpaceId && hydratedFor !== searchSpaceId) {
return loadDisabledTools(searchSpaceId);
@ -50,7 +50,7 @@ export const disabledToolsAtom = atom(
return get(disabledToolsBaseAtom);
},
(get, set, update: string[] | ((prev: string[]) => string[])) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
const prev = get(disabledToolsBaseAtom);
const next = typeof update === "function" ? update(prev) : update;
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.
*/
export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
if (!searchSpaceId) return;
const stored = loadDisabledTools(searchSpaceId);
set(disabledToolsBaseAtom, stored);
@ -75,7 +75,7 @@ export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
/** Toggle a single tool's enabled/disabled state */
export const toggleToolAtom = atom(null, (get, set, toolName: string) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
const current = get(disabledToolsBaseAtom);
const next = current.includes(toolName)
? current.filter((t) => t !== toolName)

View file

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

View file

@ -1,5 +1,5 @@
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 { cacheKeys } from "@/lib/query-client/cache-keys";
@ -11,7 +11,7 @@ const DEFAULT_LIMIT = 50;
const DEFAULT_OFFSET = 0;
export const automationsListAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
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 automationsApiService.listAutomations({
search_space_id: Number(searchSpaceId),
workspace_id: Number(searchSpaceId),
limit: DEFAULT_LIMIT,
offset: DEFAULT_OFFSET,
});

View file

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

View file

@ -1,10 +1,10 @@
import { atomWithQuery } from "jotai-tanstack-query";
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
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) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
queryKey: cacheKeys.connectors.all(searchSpaceId!),
@ -13,7 +13,7 @@ export const connectorsAtom = atomWithQuery((get) => {
queryFn: async () => {
return connectorsApiService.getConnectors({
queryParams: {
search_space_id: searchSpaceId!,
workspace_id: searchSpaceId!,
},
});
},

View file

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

View file

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

View file

@ -1,10 +1,10 @@
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 { cacheKeys } from "@/lib/query-client/cache-keys";
export const invitesAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
queryKey: cacheKeys.invites.all(searchSpaceId?.toString() ?? ""),
@ -15,7 +15,7 @@ export const invitesAtom = atomWithQuery((get) => {
return [];
}
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 { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import type {
CreateLogRequest,
DeleteLogRequest,
@ -13,7 +13,7 @@ import { queryClient } from "@/lib/query-client/client";
* Create Log Mutation
*/
export const createLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId,
@ -29,7 +29,7 @@ export const createLogMutationAtom = atomWithMutation((get) => {
* Update Log Mutation
*/
export const updateLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId,
@ -45,7 +45,7 @@ export const updateLogMutationAtom = atomWithMutation((get) => {
* Delete Log Mutation
*/
export const deleteLogMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
enabled: !!searchSpaceId,

View file

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

View file

@ -1,10 +1,10 @@
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 { cacheKeys } from "@/lib/query-client/cache-keys";
export const membersAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
queryKey: cacheKeys.members.all(searchSpaceId?.toString() ?? ""),
@ -16,14 +16,14 @@ export const membersAtom = atomWithQuery((get) => {
return [];
}
return membersApiService.getMembers({
search_space_id: Number(searchSpaceId),
workspace_id: Number(searchSpaceId),
});
},
};
});
export const myAccessAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
queryKey: cacheKeys.members.myAccess(searchSpaceId?.toString() ?? ""),
@ -34,7 +34,7 @@ export const myAccessAtom = atomWithQuery((get) => {
return null;
}
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 { cacheKeys } from "@/lib/query-client/cache-keys";
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) {
queryClient.invalidateQueries({
@ -40,14 +40,14 @@ function upsertModelConnection(searchSpaceId: number, connection: ConnectionRead
}
export const createModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-connections", "create"],
mutationFn: (request: ConnectionCreateRequest) =>
modelConnectionsApiService.createConnection(request),
onSuccess: (connection: ConnectionRead, request: ConnectionCreateRequest) => {
const resolvedSearchSpaceId = Number(
request.search_space_id ?? connection.search_space_id ?? searchSpaceId
request.workspace_id ?? connection.workspace_id ?? searchSpaceId
);
toast.success("Connection created");
if (resolvedSearchSpaceId > 0) {
@ -60,7 +60,7 @@ export const createModelConnectionMutationAtom = atomWithMutation((get) => {
});
export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-connections", "update"],
mutationFn: ({ id, data }: { id: number; data: ConnectionUpdateRequest }) =>
@ -74,7 +74,7 @@ export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
});
export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-connections", "delete"],
mutationFn: (id: number) => modelConnectionsApiService.deleteConnection(id),
@ -87,7 +87,7 @@ export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
});
export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-connections", "verify"],
mutationFn: (id: number) => modelConnectionsApiService.verifyConnection(id),
@ -110,7 +110,7 @@ export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
});
export const discoverConnectionModelsMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-connections", "discover"],
mutationFn: (id: number) => modelConnectionsApiService.discoverModels(id),
@ -149,7 +149,7 @@ export const testPreviewModelMutationAtom = atomWithMutation(() => {
});
export const addManualModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["models", "add-manual"],
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelCreateRequest }) =>
@ -163,7 +163,7 @@ export const addManualModelMutationAtom = atomWithMutation((get) => {
});
export const updateModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["models", "update"],
mutationFn: ({ id, data }: { id: number; data: ModelUpdateRequest }) =>
@ -174,7 +174,7 @@ export const updateModelMutationAtom = atomWithMutation((get) => {
});
export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["models", "bulk-update"],
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelsBulkUpdateRequest }) =>
@ -185,7 +185,7 @@ export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
});
export const testModelMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["models", "test"],
mutationFn: (id: number) => modelConnectionsApiService.testModel(id),
@ -199,7 +199,7 @@ export const testModelMutationAtom = atomWithMutation((get) => {
});
export const updateModelRolesMutationAtom = atomWithMutation((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
mutationKey: ["model-roles", "update"],
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 { isAuthenticated } from "@/lib/auth-utils";
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(() => ({
queryKey: cacheKeys.modelConnections.global(),
@ -26,7 +26,7 @@ export const modelProvidersAtom = atomWithQuery(() => ({
}));
export const modelConnectionsAtom = atomWithQuery((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
queryKey: cacheKeys.modelConnections.all(searchSpaceId),
enabled: !!searchSpaceId,
@ -36,7 +36,7 @@ export const modelConnectionsAtom = atomWithQuery((get) => {
});
export const modelRolesAtom = atomWithQuery((get) => {
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
return {
queryKey: cacheKeys.modelConnections.roles(searchSpaceId),
enabled: !!searchSpaceId,

View file

@ -1,10 +1,10 @@
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 { cacheKeys } from "@/lib/query-client/cache-keys";
export const publicChatSnapshotsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const searchSpaceId = get(activeWorkspaceIdAtom);
return {
queryKey: cacheKeys.publicChatSnapshots.bySearchSpace(Number(searchSpaceId) || 0),
@ -15,7 +15,7 @@ export const publicChatSnapshotsAtom = atomWithQuery((get) => {
return { snapshots: [] };
}
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) => {
toast.success("Role created successfully");
queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
});
},
onError: () => {
@ -39,13 +39,10 @@ export const updateRoleMutationAtom = atomWithMutation(() => {
onSuccess: (_: UpdateRoleResponse, request: UpdateRoleRequest) => {
toast.success("Role updated successfully");
queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
});
queryClient.invalidateQueries({
queryKey: cacheKeys.roles.byId(
request.search_space_id.toString(),
request.role_id.toString()
),
queryKey: cacheKeys.roles.byId(request.workspace_id.toString(), request.role_id.toString()),
});
},
onError: () => {
@ -63,7 +60,7 @@ export const deleteRoleMutationAtom = atomWithMutation(() => {
onSuccess: (_: DeleteRoleResponse, request: DeleteRoleRequest) => {
toast.success("Role deleted successfully");
queryClient.invalidateQueries({
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
});
},
onError: () => {

View file

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

View file

@ -1,10 +1,10 @@
import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query";
import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import type { GetSearchSpacesRequest } from "@/contracts/types/workspace.types";
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
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"]>({
skip: 0,

View file

@ -29,7 +29,7 @@ import {
globalModelConnectionsAtom,
modelConnectionsAtom,
} 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 {
CitationMetadataProvider,
useAllCitationMetadata,
@ -491,7 +491,7 @@ export const AssistantMessage: FC = () => {
const commentPanelRef = useRef<HTMLDivElement>(null);
const commentTriggerRef = useRef<HTMLButtonElement>(null);
const messageId = useAuiState(({ message }) => message?.id);
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const dbMessageId = parseMessageId(messageId);
const commentsEnabled = useAtomValue(commentsEnabledAtom);

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { createPortal } from "react-dom";
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 { Tabs, TabsContent } from "@/components/ui/tabs";
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
@ -36,7 +36,7 @@ interface ConnectorIndicatorProps {
export const ConnectorIndicator = forwardRef<ConnectorIndicatorHandle, ConnectorIndicatorProps>(
(_props, ref) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
// Real-time document type counts via Zero (updates instantly as docs are indexed)
const documentTypeCounts = useZeroDocumentTypeCounts(searchSpaceId);

View file

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

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { ArrowLeft, Info, RefreshCw } from "lucide-react";
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
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 { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
@ -78,7 +78,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
onConfigChange,
onNameChange,
}) => {
const searchSpaceIdAtom = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceIdAtom = useAtomValue(activeWorkspaceIdAtom);
const isAuthExpired = connector.config?.auth_expired === true;
const reauthEndpoint = getReauthEndpoint(connector);
const [reauthing, setReauthing] = useState(false);

View file

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

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { ArrowLeft, Plus, RefreshCw, Server } from "lucide-react";
import { type FC, useCallback, useState } from "react";
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 { Spinner } from "@/components/ui/spinner";
import { EnumConnectorName } from "@/contracts/enums/connector";
@ -44,7 +44,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
isConnecting = false,
addButtonText,
}) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const [reauthingId, setReauthingId] = useState<number | null>(null);
const [confirmDisconnectId, setConfirmDisconnectId] = 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",
content: videoUrls,
search_space_id: parseInt(searchSpaceId, 10),
workspace_id: parseInt(searchSpaceId, 10),
},
{
onSuccess: () => {

View file

@ -10,7 +10,7 @@ import {
useRef,
useState,
} 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 {
Dialog,
@ -90,7 +90,7 @@ const DocumentUploadPopupContent: FC<{
isOpen: boolean;
onOpenChange: (open: boolean) => void;
}> = ({ isOpen, onOpenChange }) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
if (!searchSpaceId) return null;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,7 +27,7 @@ interface MemoryReadResponse {
function getMemoryPath(scope: MemoryScope, searchSpaceId?: number | null) {
if (scope === "user") return "/api/v1/users/me/memory";
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) {

View file

@ -13,10 +13,10 @@ import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
import { announcementsDialogAtom } from "@/atoms/layout/dialogs.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 { 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 { AnnouncementSpotlight } from "@/components/announcements/AnnouncementSpotlight";
import { AnnouncementsDialog } from "@/components/announcements/AnnouncementsDialog";
@ -47,7 +47,7 @@ import { useInbox } from "@/hooks/use-inbox";
import { useIsMobile } from "@/hooks/use-mobile";
import { useArchiveThread, useDeleteThread, useRenameThread } from "@/hooks/use-thread-mutations";
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 { fetchThreads } from "@/lib/chat/thread-persistence";
import { resetUser, trackLogout } from "@/lib/posthog/events";
@ -397,7 +397,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const handleSearchSpaceSettings = useCallback(
(space: SearchSpace) => {
router.push(`/dashboard/${space.id}/search-space-settings`);
router.push(`/dashboard/${space.id}/workspace-settings`);
},
[router]
);
@ -595,7 +595,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
);
const handleSettings = useCallback(() => {
router.push(`/dashboard/${searchSpaceId}/search-space-settings`);
router.push(`/dashboard/${searchSpaceId}/workspace-settings`);
}, [router, searchSpaceId]);
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)
const isChatPage = pathname?.includes("/new-chat") ?? false;
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 isAutomationsPage = pathname?.includes("/automations") === true;
const useWorkspacePanel =

View file

@ -7,7 +7,7 @@ import { useTranslations } from "next-intl";
import { useState } from "react";
import { useForm } from "react-hook-form";
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 {
Dialog,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -165,7 +165,7 @@ export function InboxSidebarContent({
const router = useRouter();
const params = useParams();
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);
@ -207,7 +207,7 @@ export function InboxSidebarContent({
queryFn: () =>
notificationsApiService.getNotifications({
queryParams: {
search_space_id: searchSpaceId ?? undefined,
workspace_id: searchSpaceId ?? undefined,
type: searchTypeFilter,
search: debouncedSearch.trim(),
limit: 50,
@ -363,7 +363,7 @@ export function InboxSidebarContent({
if (item.type === "new_mention") {
if (isNewMentionMetadata(item.metadata)) {
const searchSpaceId = item.search_space_id;
const searchSpaceId = item.workspace_id;
const threadId = item.metadata.thread_id;
const commentId = item.metadata.comment_id;
@ -381,7 +381,7 @@ export function InboxSidebarContent({
}
} else if (item.type === "comment_reply") {
if (isCommentReplyMetadata(item.metadata)) {
const searchSpaceId = item.search_space_id;
const searchSpaceId = item.workspace_id;
const threadId = item.metadata.thread_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 { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
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";
interface MobileSidebarProps {

View file

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

View file

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

View file

@ -51,7 +51,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
// Use Jotai atom for visibility (single source of truth)
const currentThreadState = useAtomValue(currentThreadAtom);
const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.search_space_id ?? 0);
const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.workspace_id ?? 0);
// Snapshot creation mutation
const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue(
@ -139,9 +139,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
variant="ghost"
size="icon"
onClick={() =>
router.push(
`/dashboard/${thread.search_space_id}/search-space-settings/public-links`
)
router.push(`/dashboard/${thread.workspace_id}/workspace-settings/public-links`)
}
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(
() => ({
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
page: 0,
page_size: PAGE_SIZE,
...(isSearchValid ? { title: debouncedSearch.trim() } : {}),
@ -362,7 +362,7 @@ export const DocumentMentionPicker = forwardRef<
try {
const queryParams = {
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
page: nextPage,
page_size: PAGE_SIZE,
...(isSearchValid ? { title: debouncedSearch.trim() } : {}),

View file

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

View file

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

View file

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

View file

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

View file

@ -28,7 +28,7 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
});
// 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) {
const message = error instanceof Error ? error.message : "Failed to copy chat";
toast.error(message);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,13 +5,13 @@ import { useAtomValue } from "jotai";
import { AlertTriangle, Info } from "lucide-react";
import { useEffect, useState } from "react";
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 { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
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 { Spinner } from "../ui/spinner";

View file

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

View file

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

View file

@ -9,7 +9,7 @@ import {
AutomationModelFields,
type AutomationModelSelection,
} 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 { TextShimmerLoader } from "@/components/prompt-kit/loader";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
@ -27,7 +27,7 @@ import {
} from "@/lib/posthog/events";
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
@ -35,7 +35,7 @@ const editArgsSchema = automationCreateRequest.omit({ search_space_id: true });
// ----------------------------------------------------------------------------
type AutomationCreateContext = {
search_space_id?: number;
workspace_id?: number;
};
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
// 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
// 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
// here, not snapshotted from the search space), so Approve dispatches an
// `edit` decision carrying `definition.models`.
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const eligibleModels = useAutomationEligibleModels();
const [modelSelection, setModelSelection] = useState<AutomationModelSelection>({
chatModelId: 0,
@ -156,7 +156,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
const plan = Array.isArray(baseDefinition.plan) ? baseDefinition.plan : [];
const triggers = Array.isArray(baseArgs.triggers) ? baseArgs.triggers : [];
trackAutomationChatApproved({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
edited: pendingEdits !== null,
task_count: plan.length,
trigger_type:
@ -191,7 +191,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
if (phase !== "pending" || !canReject || isEditing) return;
setRejected();
trackAutomationChatRejected({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
});
onDecision({ type: "reject", message: "User rejected the automation draft." });
}, [phase, canReject, isEditing, setRejected, onDecision, searchSpaceId]);
@ -268,7 +268,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
setPendingEdits(parsed);
setIsEditing(false);
trackAutomationChatDraftEdited({
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
});
}}
onCancel={() => setIsEditing(false)}
@ -385,7 +385,7 @@ function JsonEditor({ initialValue, onSave, onCancel }: JsonEditorProps) {
// ----------------------------------------------------------------------------
function SavedCard({ result }: { result: SavedResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false);
useEffect(() => {
if (tracked.current) return;
@ -393,7 +393,7 @@ function SavedCard({ result }: { result: SavedResult }) {
trackAutomationChatCreateSucceeded({
automation_id: result.automation_id,
name: result.name,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
});
}, [result.automation_id, result.name, searchSpaceId]);
@ -429,7 +429,7 @@ function SavedCard({ result }: { result: SavedResult }) {
}
function InvalidCard({ result }: { result: InvalidResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false);
useEffect(() => {
if (tracked.current) return;
@ -437,7 +437,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
trackAutomationChatCreateFailed({
reason: "invalid",
issue_count: result.issues.length,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
});
}, [result.issues.length, searchSpaceId]);
@ -464,7 +464,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
}
function ErrorCard({ result }: { result: ErrorResult }) {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
const tracked = useRef(false);
useEffect(() => {
if (tracked.current) return;
@ -472,7 +472,7 @@ function ErrorCard({ result }: { result: ErrorResult }) {
trackAutomationChatCreateFailed({
reason: "error",
message: result.message,
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
});
}, [result.message, searchSpaceId]);
@ -531,7 +531,7 @@ export const CreateAutomationToolUI = ({
* Project raw args into the shape ``AutomationDraftPreview`` expects.
*
* 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.
*/
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.
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.
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.
## Writing New Tests

View file

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

View file

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

View file

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

View file

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

View file

@ -46,7 +46,7 @@ export const document = z.object({
unique_identifier_hash: z.string().nullable(),
created_at: z.string(),
updated_at: z.string().nullable(),
search_space_id: z.number(),
workspace_id: z.number(),
created_by_id: z.string().nullable().optional(),
created_by_name: 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({
queryParams: paginationQueryParams
.extend({
search_space_id: z.number().or(z.string()).optional(),
workspace_id: z.number().or(z.string()).optional(),
document_types: z.array(documentTypeEnum).optional(),
sort_by: documentSortByEnum.optional(),
sort_order: sortOrderEnum.optional(),
@ -112,7 +112,7 @@ export const getDocumentResponse = document;
* Create documents
*/
export const createDocumentRequest = document
.pick({ document_type: true, search_space_id: true })
.pick({ document_type: true, workspace_id: true })
.extend({
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({
files: z.array(z.instanceof(File)),
search_space_id: z.number(),
workspace_id: z.number(),
use_vision_llm: z.boolean().default(false),
processing_mode: processingModeEnum.default("basic"),
});
@ -148,7 +148,7 @@ export const uploadDocumentResponse = z.object({
*/
export const getDocumentsStatusRequest = z.object({
queryParams: z.object({
search_space_id: z.number(),
workspace_id: z.number(),
document_ids: z.array(z.number()).min(1),
}),
});
@ -175,7 +175,7 @@ export const getDocumentsStatusResponse = z.object({
export const searchDocumentsRequest = z.object({
queryParams: paginationQueryParams
.extend({
search_space_id: z.number().or(z.string()).optional(),
workspace_id: z.number().or(z.string()).optional(),
document_types: z.array(documentTypeEnum).optional(),
title: z.string().optional(),
})
@ -201,7 +201,7 @@ export const documentTitleRead = z.object({
export const searchDocumentTitlesRequest = z.object({
queryParams: z.object({
search_space_id: z.number(),
workspace_id: z.number(),
title: z.string().optional(),
page: z.number().optional(),
page_size: z.number().optional(),
@ -219,7 +219,7 @@ export const searchDocumentTitlesResponse = z.object({
export const getDocumentTypeCountsRequest = z.object({
queryParams: z
.object({
search_space_id: z.number().or(z.string()).optional(),
workspace_id: z.number().or(z.string()).optional(),
})
.nullish(),
});
@ -266,7 +266,7 @@ export const getDocumentChunksResponse = z.object({
*/
export const updateDocumentRequest = z.object({
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;

View file

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

View file

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

View file

@ -152,7 +152,7 @@ export const inboxItemMetadata = z.union([
export const inboxItem = z.object({
id: z.number(),
user_id: z.string(),
search_space_id: z.number().nullable(),
workspace_id: z.number().nullable(),
type: inboxItemTypeEnum,
title: z.string(),
message: z.string(),
@ -210,7 +210,7 @@ export type NotificationCategory = z.infer<typeof notificationCategory>;
*/
export const getNotificationsRequest = z.object({
queryParams: z.object({
search_space_id: z.number().optional(),
workspace_id: z.number().optional(),
type: inboxItemTypeEnum.optional(),
category: notificationCategory.optional(),
source_type: z.string().optional(),
@ -260,7 +260,7 @@ export const markAllNotificationsReadResponse = z.object({
* Request schema for getting unread count
*/
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(),
name: z.string().max(100).nullable().optional(),
invite_code: z.string(),
search_space_id: z.number(),
workspace_id: z.number(),
created_by_id: z.string().nullable(),
role_id: z.number().nullable(),
expires_at: z.string().nullable(),
@ -20,7 +20,7 @@ export const invite = z.object({
* Create invite
*/
export const createInviteRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
data: z.object({
name: z.string().max(100).optional(),
role_id: z.number().nullable().optional(),
@ -35,7 +35,7 @@ export const createInviteResponse = invite;
* Get invites
*/
export const getInvitesRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
});
export const getInvitesResponse = z.array(invite);
@ -44,7 +44,7 @@ export const getInvitesResponse = z.array(invite);
* Update invite
*/
export const updateInviteRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
invite_id: z.number(),
data: z.object({
name: z.string().max(100).optional(),
@ -61,7 +61,7 @@ export const updateInviteResponse = invite;
* Delete invite
*/
export const deleteInviteRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
invite_id: z.number(),
});
@ -77,7 +77,7 @@ export const getInviteInfoRequest = z.object({
});
export const getInviteInfoResponse = z.object({
search_space_name: z.string(),
workspace_name: z.string(),
role_name: z.string().nullable(),
is_valid: z.boolean(),
message: z.string().nullable(),
@ -92,8 +92,8 @@ export const acceptInviteRequest = z.object({
export const acceptInviteResponse = z.object({
message: z.string(),
search_space_id: z.number(),
search_space_name: z.string(),
workspace_id: z.number(),
workspace_name: z.string(),
role_name: z.string().nullable(),
});

View file

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

View file

@ -37,7 +37,7 @@ export const mcpConnectorRead = z.object({
name: z.string(),
connector_type: z.literal("MCP_CONNECTOR"),
server_config: mcpServerConfig,
search_space_id: z.number(),
workspace_id: z.number(),
user_id: z.string(),
created_at: z.string(),
updated_at: z.string(),
@ -49,7 +49,7 @@ export const mcpConnectorRead = z.object({
export const createMCPConnectorRequest = z.object({
data: mcpConnectorCreate,
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({
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({
id: z.number(),
user_id: z.string(),
search_space_id: z.number(),
workspace_id: z.number(),
role_id: z.number().nullable(),
is_owner: z.boolean(),
joined_at: z.string(),
@ -21,7 +21,7 @@ export const membership = z.object({
* Get members
*/
export const getMembersRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
});
export const getMembersResponse = z.array(membership);
@ -30,7 +30,7 @@ export const getMembersResponse = z.array(membership);
* Update membership
*/
export const updateMembershipRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
membership_id: z.number(),
data: z.object({
role_id: z.number().nullable(),
@ -43,7 +43,7 @@ export const updateMembershipResponse = membership;
* Delete membership
*/
export const deleteMembershipRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
membership_id: z.number(),
});
@ -55,7 +55,7 @@ export const deleteMembershipResponse = z.object({
* Leave search space
*/
export const leaveSearchSpaceRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
});
export const leaveSearchSpaceResponse = z.object({
@ -66,12 +66,12 @@ export const leaveSearchSpaceResponse = z.object({
* Get my access
*/
export const getMyAccessRequest = z.object({
search_space_id: z.number(),
workspace_id: z.number(),
});
export const getMyAccessResponse = z.object({
search_space_name: z.string(),
search_space_id: z.number(),
workspace_name: z.string(),
workspace_id: z.number(),
is_owner: z.boolean(),
permissions: z.array(z.string()),
role_name: z.string().nullable(),

View file

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

View file

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

View file

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

View file

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

View file

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

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