diff --git a/apps/sim/app/api/admin/mothership/route.ts b/apps/sim/app/api/admin/mothership/route.ts
index a3a022f9d82..a5eb0245de1 100644
--- a/apps/sim/app/api/admin/mothership/route.ts
+++ b/apps/sim/app/api/admin/mothership/route.ts
@@ -3,7 +3,7 @@ import { settings, user } from '@sim/db/schema'
import { getErrorMessage } from '@sim/utils/errors'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
-import { adminMothershipQuerySchema } from '@/lib/api/contracts/mothership-tasks'
+import { adminMothershipQuerySchema } from '@/lib/api/contracts/mothership-chats'
import { mothershipEnvironmentSchema } from '@/lib/api/contracts/user'
import { searchParamsToObject, validationErrorResponse } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
diff --git a/apps/sim/app/api/copilot/chat/delete/route.test.ts b/apps/sim/app/api/copilot/chat/delete/route.test.ts
index ccabaf7c521..a84de630726 100644
--- a/apps/sim/app/api/copilot/chat/delete/route.test.ts
+++ b/apps/sim/app/api/copilot/chat/delete/route.test.ts
@@ -19,8 +19,8 @@ vi.mock('@/lib/copilot/chat/lifecycle', () => ({
getAccessibleCopilotChatAuth: mockGetAccessibleCopilotChatAuth,
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: { publishStatusChanged: vi.fn() },
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: { publishStatusChanged: vi.fn() },
}))
import { DELETE } from './route'
diff --git a/apps/sim/app/api/copilot/chat/delete/route.ts b/apps/sim/app/api/copilot/chat/delete/route.ts
index fd06735a91f..ccd3ba7334a 100644
--- a/apps/sim/app/api/copilot/chat/delete/route.ts
+++ b/apps/sim/app/api/copilot/chat/delete/route.ts
@@ -7,7 +7,7 @@ import { deleteCopilotChatContract } from '@/lib/api/contracts/copilot'
import { parseRequest } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { getAccessibleCopilotChatAuth } from '@/lib/copilot/chat/lifecycle'
-import { taskPubSub } from '@/lib/copilot/tasks'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
const logger = createLogger('DeleteChatAPI')
@@ -47,7 +47,7 @@ export const DELETE = withRouteHandler(async (request: NextRequest) => {
logger.info('Chat deleted', { chatId: parsed.chatId })
if (deleted.workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: deleted.workspaceId,
chatId: parsed.chatId,
type: 'deleted',
diff --git a/apps/sim/app/api/copilot/chat/rename/route.ts b/apps/sim/app/api/copilot/chat/rename/route.ts
index 6886f568be6..5022fbfce74 100644
--- a/apps/sim/app/api/copilot/chat/rename/route.ts
+++ b/apps/sim/app/api/copilot/chat/rename/route.ts
@@ -7,7 +7,7 @@ import { renameCopilotChatContract } from '@/lib/api/contracts/copilot'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { getAccessibleCopilotChatAuth } from '@/lib/copilot/chat/lifecycle'
-import { taskPubSub } from '@/lib/copilot/tasks'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
const logger = createLogger('RenameChatAPI')
@@ -49,7 +49,7 @@ export const PATCH = withRouteHandler(async (request: NextRequest) => {
logger.info('Chat renamed', { chatId, title })
if (updated.workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: updated.workspaceId,
chatId,
type: 'renamed',
diff --git a/apps/sim/app/api/copilot/chat/stop/route.test.ts b/apps/sim/app/api/copilot/chat/stop/route.test.ts
index 452131f21e1..e734cd9cde4 100644
--- a/apps/sim/app/api/copilot/chat/stop/route.test.ts
+++ b/apps/sim/app/api/copilot/chat/stop/route.test.ts
@@ -16,8 +16,8 @@ vi.mock('@/lib/copilot/chat/messages-store', () => ({
appendCopilotChatMessages: mockAppendCopilotChatMessages,
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: {
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: {
publishStatusChanged: mockPublishStatusChanged,
},
}))
diff --git a/apps/sim/app/api/copilot/chat/stop/route.ts b/apps/sim/app/api/copilot/chat/stop/route.ts
index 05d7303d94c..91f17dbcff3 100644
--- a/apps/sim/app/api/copilot/chat/stop/route.ts
+++ b/apps/sim/app/api/copilot/chat/stop/route.ts
@@ -10,6 +10,7 @@ import {
withStoppedContentBlock,
} from '@/lib/copilot/chat/persisted-message'
import { finalizeAssistantTurn } from '@/lib/copilot/chat/terminal-state'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import {
CopilotChatFinalizeOutcome,
CopilotStopOutcome,
@@ -17,7 +18,6 @@ import {
import { TraceAttr } from '@/lib/copilot/generated/trace-attributes-v1'
import { TraceSpan } from '@/lib/copilot/generated/trace-spans-v1'
import { withIncomingGoSpan } from '@/lib/copilot/request/otel'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
const logger = createLogger('CopilotChatStopAPI')
@@ -83,7 +83,7 @@ export const POST = withRouteHandler((req: NextRequest) =>
result.updated || result.outcome === CopilotChatFinalizeOutcome.AssistantAlreadyPersisted
if (shouldPublishCompleted && result.workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: result.workspaceId,
chatId,
type: 'completed',
diff --git a/apps/sim/app/api/copilot/chats/route.ts b/apps/sim/app/api/copilot/chats/route.ts
index 05e4c7773db..c72bfa1c8ba 100644
--- a/apps/sim/app/api/copilot/chats/route.ts
+++ b/apps/sim/app/api/copilot/chats/route.ts
@@ -7,6 +7,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { createWorkflowCopilotChatContract } from '@/lib/api/contracts/copilot'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { resolveOrCreateChat } from '@/lib/copilot/chat/lifecycle'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import {
authenticateCopilotRequestSessionOnly,
createBadRequestResponse,
@@ -14,7 +15,6 @@ import {
createInternalServerErrorResponse,
createUnauthorizedResponse,
} from '@/lib/copilot/request/http'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import {
assertActiveWorkspaceAccess,
@@ -138,7 +138,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
return createInternalServerErrorResponse('Failed to create chat')
}
- taskPubSub?.publishStatusChanged({ workspaceId, chatId: result.chatId, type: 'created' })
+ chatPubSub?.publishStatusChanged({ workspaceId, chatId: result.chatId, type: 'created' })
return NextResponse.json({ success: true, id: result.chatId })
} catch (error) {
diff --git a/apps/sim/app/api/mothership/chat/abort/route.ts b/apps/sim/app/api/mothership/chat/abort/route.ts
index 4b62fa4ebd3..c505fd9ab28 100644
--- a/apps/sim/app/api/mothership/chat/abort/route.ts
+++ b/apps/sim/app/api/mothership/chat/abort/route.ts
@@ -1,5 +1,5 @@
import type { NextRequest } from 'next/server'
-import { mothershipChatAbortEnvelopeSchema } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipChatAbortEnvelopeSchema } from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { POST as copilotAbortPost } from '@/app/api/copilot/chat/abort/route'
diff --git a/apps/sim/app/api/mothership/chat/resources/route.ts b/apps/sim/app/api/mothership/chat/resources/route.ts
index 169f16e4148..d377f421b22 100644
--- a/apps/sim/app/api/mothership/chat/resources/route.ts
+++ b/apps/sim/app/api/mothership/chat/resources/route.ts
@@ -1,5 +1,5 @@
import type { NextRequest, NextResponse } from 'next/server'
-import { mothershipChatResourceEnvelopeSchema } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipChatResourceEnvelopeSchema } from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import {
diff --git a/apps/sim/app/api/mothership/chat/route.ts b/apps/sim/app/api/mothership/chat/route.ts
index f6167b7c3e0..6351971fd8c 100644
--- a/apps/sim/app/api/mothership/chat/route.ts
+++ b/apps/sim/app/api/mothership/chat/route.ts
@@ -2,7 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import {
mothershipChatGetQuerySchema,
mothershipChatPostEnvelopeSchema,
-} from '@/lib/api/contracts/mothership-tasks'
+} from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { handleUnifiedChatPost, maxDuration } from '@/lib/copilot/chat/post'
diff --git a/apps/sim/app/api/mothership/chat/stop/route.ts b/apps/sim/app/api/mothership/chat/stop/route.ts
index 5ffd63dd1fb..7ec211ce4f8 100644
--- a/apps/sim/app/api/mothership/chat/stop/route.ts
+++ b/apps/sim/app/api/mothership/chat/stop/route.ts
@@ -1,5 +1,5 @@
import type { NextRequest } from 'next/server'
-import { mothershipChatStopEnvelopeSchema } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipChatStopEnvelopeSchema } from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { POST as copilotStopPost } from '@/app/api/copilot/chat/stop/route'
diff --git a/apps/sim/app/api/mothership/chat/stream/route.ts b/apps/sim/app/api/mothership/chat/stream/route.ts
index 4707d5dbb16..9734d56c126 100644
--- a/apps/sim/app/api/mothership/chat/stream/route.ts
+++ b/apps/sim/app/api/mothership/chat/stream/route.ts
@@ -1,5 +1,5 @@
import type { NextRequest } from 'next/server'
-import { mothershipChatStreamQuerySchema } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipChatStreamQuerySchema } from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
import { GET as copilotStreamGet, maxDuration } from '@/app/api/copilot/chat/stream/route'
diff --git a/apps/sim/app/api/mothership/chats/[chatId]/fork/route.ts b/apps/sim/app/api/mothership/chats/[chatId]/fork/route.ts
index ed2bd5b59e9..44b63c7f163 100644
--- a/apps/sim/app/api/mothership/chats/[chatId]/fork/route.ts
+++ b/apps/sim/app/api/mothership/chats/[chatId]/fork/route.ts
@@ -4,10 +4,11 @@ import { createLogger } from '@sim/logger'
import { generateId } from '@sim/utils/id'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
-import { forkMothershipChatContract } from '@/lib/api/contracts/mothership-tasks'
+import { forkMothershipChatContract } from '@/lib/api/contracts/mothership-chats'
import { parseRequest } from '@/lib/api/server'
import { loadCopilotChatMessages } from '@/lib/copilot/chat/lifecycle'
import { appendCopilotChatMessages } from '@/lib/copilot/chat/messages-store'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { fetchGo } from '@/lib/copilot/request/go/fetch'
import {
authenticateCopilotRequestSessionOnly,
@@ -19,7 +20,6 @@ import {
} from '@/lib/copilot/request/http'
import type { MothershipResource } from '@/lib/copilot/resources/types'
import { getMothershipBaseURL, getMothershipSourceEnvHeaders } from '@/lib/copilot/server/agent-url'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { env } from '@/lib/core/config/env'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
@@ -154,7 +154,7 @@ export const POST = withRouteHandler(
}
if (newChat.workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: newChat.workspaceId,
chatId: newId,
type: 'created',
diff --git a/apps/sim/app/api/mothership/chats/[chatId]/route.test.ts b/apps/sim/app/api/mothership/chats/[chatId]/route.test.ts
index 7e96d476220..e46b2eb1314 100644
--- a/apps/sim/app/api/mothership/chats/[chatId]/route.test.ts
+++ b/apps/sim/app/api/mothership/chats/[chatId]/route.test.ts
@@ -79,8 +79,8 @@ vi.mock('@/lib/copilot/chat/persisted-message', () => ({
normalizeMessage: (m: unknown) => m,
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: { publishStatusChanged: vi.fn() },
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: { publishStatusChanged: vi.fn() },
}))
vi.mock('@/lib/posthog/server', () => ({
diff --git a/apps/sim/app/api/mothership/chats/[chatId]/route.ts b/apps/sim/app/api/mothership/chats/[chatId]/route.ts
index 2d7c20c1b1f..2274a9582c7 100644
--- a/apps/sim/app/api/mothership/chats/[chatId]/route.ts
+++ b/apps/sim/app/api/mothership/chats/[chatId]/route.ts
@@ -8,7 +8,7 @@ import {
deleteMothershipChatContract,
getMothershipChatContract,
updateMothershipChatContract,
-} from '@/lib/api/contracts/mothership-tasks'
+} from '@/lib/api/contracts/mothership-chats'
import { parseRequest } from '@/lib/api/server'
import { getLatestRunForStream } from '@/lib/copilot/async-runs/repository'
import { buildEffectiveChatTranscript } from '@/lib/copilot/chat/effective-transcript'
@@ -18,6 +18,7 @@ import {
} from '@/lib/copilot/chat/lifecycle'
import { normalizeMessage } from '@/lib/copilot/chat/persisted-message'
import { reconcileChatStreamMarkers } from '@/lib/copilot/chat/stream-liveness'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import {
authenticateCopilotRequestSessionOnly,
createInternalServerErrorResponse,
@@ -27,7 +28,6 @@ import type { FilePreviewSession } from '@/lib/copilot/request/session'
import { readEvents } from '@/lib/copilot/request/session/buffer'
import { readFilePreviewSessions } from '@/lib/copilot/request/session/file-preview-session'
import { type StreamBatchEvent, toStreamBatchEvent } from '@/lib/copilot/request/session/types'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
@@ -185,7 +185,7 @@ export const PATCH = withRouteHandler(
if (updatedChat.workspaceId) {
if (title !== undefined) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: updatedChat.workspaceId,
chatId,
type: 'renamed',
@@ -264,7 +264,7 @@ export const DELETE = withRouteHandler(
}
if (deletedChat.workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: deletedChat.workspaceId,
chatId,
type: 'deleted',
diff --git a/apps/sim/app/api/mothership/chats/read/route.ts b/apps/sim/app/api/mothership/chats/read/route.ts
index 2973d1bbe89..2cda04a65f2 100644
--- a/apps/sim/app/api/mothership/chats/read/route.ts
+++ b/apps/sim/app/api/mothership/chats/read/route.ts
@@ -3,7 +3,7 @@ import { copilotChats } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
-import { markMothershipChatReadContract } from '@/lib/api/contracts/mothership-tasks'
+import { markMothershipChatReadContract } from '@/lib/api/contracts/mothership-chats'
import { parseRequest } from '@/lib/api/server'
import {
authenticateCopilotRequestSessionOnly,
diff --git a/apps/sim/app/api/mothership/chats/route.test.ts b/apps/sim/app/api/mothership/chats/route.test.ts
index 5851d1b45df..f23d33034ea 100644
--- a/apps/sim/app/api/mothership/chats/route.test.ts
+++ b/apps/sim/app/api/mothership/chats/route.test.ts
@@ -47,8 +47,8 @@ vi.mock('@/lib/copilot/chat/stream-liveness', () => ({
reconcileChatStreamMarkers: mockReconcileChatStreamMarkers,
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: { publishStatusChanged: vi.fn() },
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: { publishStatusChanged: vi.fn() },
}))
vi.mock('@/lib/posthog/server', () => ({
diff --git a/apps/sim/app/api/mothership/chats/route.ts b/apps/sim/app/api/mothership/chats/route.ts
index c5610da215d..b29abfa514d 100644
--- a/apps/sim/app/api/mothership/chats/route.ts
+++ b/apps/sim/app/api/mothership/chats/route.ts
@@ -6,16 +6,16 @@ import { type NextRequest, NextResponse } from 'next/server'
import {
createMothershipChatContract,
listMothershipChatsContract,
-} from '@/lib/api/contracts/mothership-tasks'
+} from '@/lib/api/contracts/mothership-chats'
import { parseRequest } from '@/lib/api/server'
import { reconcileChatStreamMarkers } from '@/lib/copilot/chat/stream-liveness'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import {
authenticateCopilotRequestSessionOnly,
createForbiddenResponse,
createInternalServerErrorResponse,
createUnauthorizedResponse,
} from '@/lib/copilot/request/http'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
import {
@@ -111,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
})
.returning({ id: copilotChats.id })
- taskPubSub?.publishStatusChanged({ workspaceId, chatId: chat.id, type: 'created' })
+ chatPubSub?.publishStatusChanged({ workspaceId, chatId: chat.id, type: 'created' })
captureServerEvent(
userId,
diff --git a/apps/sim/app/api/mothership/events/route.ts b/apps/sim/app/api/mothership/events/route.ts
index bb3e1f278c8..5509358254d 100644
--- a/apps/sim/app/api/mothership/events/route.ts
+++ b/apps/sim/app/api/mothership/events/route.ts
@@ -8,9 +8,9 @@
*/
import type { NextRequest } from 'next/server'
-import { mothershipEventsQuerySchema } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipEventsQuerySchema } from '@/lib/api/contracts/mothership-chats'
import { validationErrorResponse } from '@/lib/api/server'
-import { taskPubSub } from '@/lib/copilot/tasks'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { createWorkspaceSSE } from '@/lib/events/sse-endpoint'
@@ -21,8 +21,8 @@ const mothershipEventsHandler = createWorkspaceSSE({
subscriptions: [
{
subscribe: (workspaceId, send) => {
- if (!taskPubSub) return () => {}
- return taskPubSub.onStatusChanged((event) => {
+ if (!chatPubSub) return () => {}
+ return chatPubSub.onStatusChanged((event) => {
if (event.workspaceId !== workspaceId) return
send('task_status', {
chatId: event.chatId,
diff --git a/apps/sim/app/api/mothership/execute/route.ts b/apps/sim/app/api/mothership/execute/route.ts
index a3550718b92..43a0bdfd965 100644
--- a/apps/sim/app/api/mothership/execute/route.ts
+++ b/apps/sim/app/api/mothership/execute/route.ts
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
import { getErrorMessage, toError } from '@sim/utils/errors'
import { generateId } from '@sim/utils/id'
import { type NextRequest, NextResponse } from 'next/server'
-import { mothershipExecuteContract } from '@/lib/api/contracts/mothership-tasks'
+import { mothershipExecuteContract } from '@/lib/api/contracts/mothership-chats'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { buildIntegrationToolSchemas } from '@/lib/copilot/chat/payload'
diff --git a/apps/sim/app/workspace/[workspaceId]/task/[taskId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/chat/[chatId]/layout.tsx
similarity index 62%
rename from apps/sim/app/workspace/[workspaceId]/task/[taskId]/layout.tsx
rename to apps/sim/app/workspace/[workspaceId]/chat/[chatId]/layout.tsx
index b317512345e..6dc5b2e20bd 100644
--- a/apps/sim/app/workspace/[workspaceId]/task/[taskId]/layout.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/chat/[chatId]/layout.tsx
@@ -1,3 +1,3 @@
-export default function TaskLayout({ children }: { children: React.ReactNode }) {
+export default function ChatLayout({ children }: { children: React.ReactNode }) {
return
{children}
}
diff --git a/apps/sim/app/workspace/[workspaceId]/task/[taskId]/page.tsx b/apps/sim/app/workspace/[workspaceId]/chat/[chatId]/page.tsx
similarity index 68%
rename from apps/sim/app/workspace/[workspaceId]/task/[taskId]/page.tsx
rename to apps/sim/app/workspace/[workspaceId]/chat/[chatId]/page.tsx
index b22949113b3..7bd786290ad 100644
--- a/apps/sim/app/workspace/[workspaceId]/task/[taskId]/page.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/chat/[chatId]/page.tsx
@@ -6,24 +6,24 @@ export const metadata: Metadata = {
title: 'Chat',
}
-interface TaskPageProps {
+interface ChatPageProps {
params: Promise<{
workspaceId: string
- taskId: string
+ chatId: string
}>
searchParams: Promise<{ resource?: string }>
}
-export default async function TaskPage({ params, searchParams }: TaskPageProps) {
- const [{ taskId }, { resource }, session] = await Promise.all([
+export default async function ChatPage({ params, searchParams }: ChatPageProps) {
+ const [{ chatId }, { resource }, session] = await Promise.all([
params,
searchParams,
getSession(),
])
return (
(null)
const requestIdTimeoutRef = useRef(null)
const submitFeedback = useSubmitCopilotFeedback()
- const forkTask = useForkTask(params.workspaceId)
+ const forkChat = useForkMothershipChat(params.workspaceId)
useEffect(() => {
return () => {
@@ -151,11 +151,11 @@ export const MessageActions = memo(function MessageActions({
}
const handleFork = async () => {
- if (!chatId || !messageId || forkTask.isPending) return
+ if (!chatId || !messageId || forkChat.isPending) return
try {
- const result = await forkTask.mutateAsync({ chatId, upToMessageId: messageId })
- useFolderStore.getState().clearTaskSelection()
- router.push(`/workspace/${params.workspaceId}/task/${result.id}`)
+ const result = await forkChat.mutateAsync({ chatId, upToMessageId: messageId })
+ useFolderStore.getState().clearChatSelection()
+ router.push(`/workspace/${params.workspaceId}/chat/${result.id}`)
} catch {
toast.error('Failed to fork chat')
}
@@ -223,8 +223,8 @@ export const MessageActions = memo(function MessageActions({
type='button'
aria-label='Fork from here'
onClick={handleFork}
- disabled={forkTask.isPending}
- className={cn(BUTTON_CLASS, forkTask.isPending && 'cursor-not-allowed opacity-50')}
+ disabled={forkChat.isPending}
+ className={cn(BUTTON_CLASS, forkChat.isPending && 'cursor-not-allowed opacity-50')}
>
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
index 59f8c4063fa..889ac5ada6b 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
@@ -29,8 +29,8 @@ import { listIntegrations } from '@/blocks/integration-matcher'
import { useFolders } from '@/hooks/queries/folders'
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
import { useLogsList } from '@/hooks/queries/logs'
+import { useMothershipChats } from '@/hooks/queries/mothership-chats'
import { useTablesList } from '@/hooks/queries/tables'
-import { useTasks } from '@/hooks/queries/tasks'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useWorkspaceFileFolders } from '@/hooks/queries/workspace-file-folders'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
@@ -76,7 +76,7 @@ export function useAvailableResources(
const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
const { data: folders = [] } = useFolders(workspaceId)
const { data: fileFolders = [] } = useWorkspaceFileFolders(workspaceId)
- const { data: tasks = [] } = useTasks(workspaceId)
+ const { data: tasks = [] } = useMothershipChats(workspaceId)
const { data: logsData } = useLogsList(workspaceId, LOG_DROPDOWN_FILTERS)
const logs = useMemo(() => (logsData?.pages ?? []).flatMap((page) => page.logs), [logsData])
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
index fa6be31bce3..d41ba2a8770 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
@@ -22,8 +22,8 @@ import type {
import { getBareIconStyle, type StyleableIcon } from '@/blocks/icon-color'
import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
import { logKeys } from '@/hooks/queries/logs'
+import { mothershipChatKeys } from '@/hooks/queries/mothership-chats'
import { tableKeys } from '@/hooks/queries/tables'
-import { taskKeys } from '@/hooks/queries/tasks'
import { folderKeys } from '@/hooks/queries/utils/folder-keys'
import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workflow-lists'
import { workspaceFileFolderKeys } from '@/hooks/queries/workspace-file-folders'
@@ -239,7 +239,7 @@ const RESOURCE_INVALIDATORS: Record<
qc.invalidateQueries({ queryKey: workspaceFileFolderKeys.workspaceLists(wId) })
},
task: (qc, wId) => {
- qc.invalidateQueries({ queryKey: taskKeys.list(wId) })
+ qc.invalidateQueries({ queryKey: mothershipChatKeys.list(wId) })
},
log: (qc, _wId, id) => {
qc.invalidateQueries({ queryKey: logKeys.details() })
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
index 2c3a2210f73..c8411552407 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
@@ -29,12 +29,12 @@ import type {
} from '@/app/workspace/[workspaceId]/home/types'
import { useFolders } from '@/hooks/queries/folders'
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
-import { useTablesList } from '@/hooks/queries/tables'
import {
useAddChatResource,
useRemoveChatResource,
useReorderChatResources,
-} from '@/hooks/queries/tasks'
+} from '@/hooks/queries/mothership-chats'
+import { useTablesList } from '@/hooks/queries/tables'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx
index 396c214f807..b3bef698d2f 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx
@@ -19,7 +19,10 @@ import {
} from '@/lib/mothership/events'
import { captureEvent } from '@/lib/posthog/client'
import { persistImportedWorkflow } from '@/lib/workflows/operations/import-export'
-import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks'
+import {
+ useMarkMothershipChatRead,
+ useMothershipChatHistory,
+} from '@/hooks/queries/mothership-chats'
import { useOAuthReturnRouter } from '@/hooks/use-oauth-return'
import type { ChatContext } from '@/stores/panel'
import {
@@ -113,8 +116,8 @@ export function Home({ chatId, userName, userId, initialResourceId = null }: Hom
const wasSendingRef = useRef(false)
- const { isPending: isChatHistoryPending } = useChatHistory(chatId)
- const { mutate: markRead } = useMarkTaskRead(workspaceId)
+ const { isPending: isChatHistoryPending } = useMothershipChatHistory(chatId)
+ const { mutate: markRead } = useMarkMothershipChatRead(workspaceId)
const { mothershipRef, handleResizePointerDown, clearWidth } = useMothershipResize()
diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
index 41284baac19..e2ca729c64c 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
@@ -10,7 +10,7 @@ import {
addMothershipChatResourceContract,
removeMothershipChatResourceContract,
reorderMothershipChatResourcesContract,
-} from '@/lib/api/contracts/mothership-tasks'
+} from '@/lib/api/contracts/mothership-chats'
import { cancelWorkflowExecutionContract } from '@/lib/api/contracts/workflows'
import { getMothershipAttachmentPreviewUrl } from '@/lib/copilot/chat/attachment-preview'
import { toDisplayMessage } from '@/lib/copilot/chat/display-message'
@@ -118,11 +118,11 @@ import {
} from '@/app/workspace/[workspaceId]/home/hooks/use-file-preview-sessions'
import { deploymentKeys } from '@/hooks/queries/deployments'
import {
- fetchChatHistory,
- type TaskChatHistory,
- taskKeys,
- useChatHistory,
-} from '@/hooks/queries/tasks'
+ fetchMothershipChatHistory,
+ type MothershipChatHistory,
+ mothershipChatKeys,
+ useMothershipChatHistory,
+} from '@/hooks/queries/mothership-chats'
import { getFolderMap } from '@/hooks/queries/utils/folder-cache'
import { folderKeys } from '@/hooks/queries/utils/folder-keys'
import { invalidateWorkflowSelectors } from '@/hooks/queries/utils/invalidate-workflow-lists'
@@ -1095,7 +1095,7 @@ function markMessageStopped(message: PersistedMessage): PersistedMessage {
})
}
-function buildChatHistoryHydrationKey(chatHistory: TaskChatHistory): string {
+function buildChatHistoryHydrationKey(chatHistory: MothershipChatHistory): string {
const resourceKey = chatHistory.resources
.map((resource) => `${resource.type}:${resource.id}:${resource.title}`)
.join('|')
@@ -1604,18 +1604,21 @@ export function useChat(
[queryClient, workspaceId]
)
- const upsertTaskChatHistory = useCallback(
- (chatId: string, updater: (current: TaskChatHistory) => TaskChatHistory) => {
- queryClient.setQueryData(taskKeys.detail(chatId), (current) => {
- const base: TaskChatHistory = current ?? {
- id: chatId,
- title: null,
- messages: [],
- activeStreamId: null,
- resources: resourcesRef.current,
+ const upsertChatHistory = useCallback(
+ (chatId: string, updater: (current: MothershipChatHistory) => MothershipChatHistory) => {
+ queryClient.setQueryData(
+ mothershipChatKeys.detail(chatId),
+ (current) => {
+ const base: MothershipChatHistory = current ?? {
+ id: chatId,
+ title: null,
+ messages: [],
+ activeStreamId: null,
+ resources: resourcesRef.current,
+ }
+ return updater(base)
}
- return updater(base)
- })
+ )
},
[queryClient]
)
@@ -1851,7 +1854,7 @@ export function useChat(
const abortControllerRef = useRef(null)
const streamReaderRef = useRef | null>(null)
const chatIdRef = useRef(initialChatId)
- /** Panel/task selection — drives createNewChat + request chatId; may differ from chatIdRef while a stream is still finishing. */
+ /** Panel/chat selection — drives createNewChat + request chatId; may differ from chatIdRef while a stream is still finishing. */
const selectedChatIdRef = useRef(initialChatId)
selectedChatIdRef.current = initialChatId
const appliedChatHistoryKeyRef = useRef(undefined)
@@ -1923,12 +1926,14 @@ export function useChat(
streamId: string,
assistantId: string,
afterCursor: string,
- options?: { targetChatId?: string; chatHistory?: TaskChatHistory }
+ options?: { targetChatId?: string; chatHistory?: MothershipChatHistory }
): ReconnectReplaySelection => {
const cachedHistory =
options?.chatHistory ??
(options?.targetChatId
- ? queryClient.getQueryData(taskKeys.detail(options.targetChatId))
+ ? queryClient.getQueryData(
+ mothershipChatKeys.detail(options.targetChatId)
+ )
: undefined)
const cachedLiveAssistant = cachedHistory?.messages.find(
(message) => message.id === assistantId
@@ -2065,17 +2070,17 @@ export function useChat(
!workflowIdRef.current &&
typeof window !== 'undefined'
) {
- window.history.replaceState(null, '', `/workspace/${workspaceId}/task/${chatId}`)
+ window.history.replaceState(null, '', `/workspace/${workspaceId}/chat/${chatId}`)
}
if (options?.invalidateList) {
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
}
flushPendingResources(chatId)
},
[flushPendingResources, queryClient, workspaceId]
)
- const { data: chatHistory } = useChatHistory(resolvedChatId)
+ const { data: chatHistory } = useMothershipChatHistory(resolvedChatId)
const messages = useMemo(() => {
const source = chatHistory?.messages.map(toDisplayMessage) ?? pendingMessages
return source.map((m) => restoreRevealedSimKeysForMessage(m, revealedSimKeysRef.current))
@@ -2290,7 +2295,7 @@ export function useChat(
clearActiveTurn()
setTransportIdle()
if (abandonedChatId) {
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(abandonedChatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(abandonedChatId) })
}
} else {
setResolvedChatId(initialChatId)
@@ -2750,7 +2755,7 @@ export function useChat(
contentBlocks: blocks,
...(streamRequestId ? { requestId: streamRequestId } : {}),
})
- upsertTaskChatHistory(activeChatId, (current) => {
+ upsertChatHistory(activeChatId, (current) => {
const streamId = streamIdRef.current ?? current.activeStreamId ?? assistantId
const terminalPersistedAssistantExists =
current.activeStreamId !== streamId &&
@@ -2877,7 +2882,7 @@ export function useChat(
setResolvedChatId(payloadChatId)
}
queryClient.invalidateQueries({
- queryKey: taskKeys.list(workspaceId),
+ queryKey: mothershipChatKeys.list(workspaceId),
})
if (isNewChat) {
const userMsg = pendingUserMsgRef.current
@@ -2891,27 +2896,30 @@ export function useChat(
contentBlocks: streamingBlocksRef.current,
})
const seededMessages = [userMsg, assistantMessage]
- queryClient.setQueryData(taskKeys.detail(payloadChatId), {
- id: payloadChatId,
- title: null,
- messages: seededMessages,
- activeStreamId,
- resources: resourcesRef.current,
- })
+ queryClient.setQueryData(
+ mothershipChatKeys.detail(payloadChatId),
+ {
+ id: payloadChatId,
+ title: null,
+ messages: seededMessages,
+ activeStreamId,
+ resources: resourcesRef.current,
+ }
+ )
}
setPendingMessages([])
if (!workflowIdRef.current) {
window.history.replaceState(
null,
'',
- `/workspace/${workspaceId}/task/${payloadChatId}`
+ `/workspace/${workspaceId}/chat/${payloadChatId}`
)
}
}
}
if (payload.kind === MothershipStreamV1SessionKind.title) {
queryClient.invalidateQueries({
- queryKey: taskKeys.list(workspaceId),
+ queryKey: mothershipChatKeys.list(workspaceId),
})
onTitleUpdateRef.current?.()
}
@@ -3641,7 +3649,7 @@ export function useChat(
workspaceId,
router,
queryClient,
- upsertTaskChatHistory,
+ upsertChatHistory,
addResource,
removeResource,
applyPreviewSessionUpdate,
@@ -3657,16 +3665,18 @@ export function useChat(
chatId: string,
signal?: AbortSignal
): Promise<{ loaded: boolean; streamId: string | null }> => {
- const cached = queryClient.getQueryData(taskKeys.detail(chatId))
+ const cached = queryClient.getQueryData(
+ mothershipChatKeys.detail(chatId)
+ )
try {
const fetchSignal = combineAbortSignals(
signal,
createTimeoutSignal(CHAT_HISTORY_RECOVERY_TIMEOUT_MS)
)
- const history = await fetchChatHistory(chatId, fetchSignal)
+ const history = await fetchMothershipChatHistory(chatId, fetchSignal)
if (signal?.aborted || fetchSignal?.aborted) return { loaded: false, streamId: null }
- queryClient.setQueryData(taskKeys.detail(chatId), history)
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), history)
return { loaded: true, streamId: history.activeStreamId ?? null }
} catch (error) {
logger.warn('Failed to load chat history while recovering stream', {
@@ -4185,7 +4195,9 @@ export function useChat(
selectedChatIdRef.current === startingSelectedChatId &&
!recoveryController.signal.aborted
- const cached = queryClient.getQueryData(taskKeys.detail(chatId))
+ const cached = queryClient.getQueryData(
+ mothershipChatKeys.detail(chatId)
+ )
const fallbackStreamId =
streamIdRef.current ?? activeTurnRef.current?.userMessageId ?? cached?.activeStreamId
const loadedStream = await getActiveStreamIdForChat(chatId, recoveryController.signal)
@@ -4428,10 +4440,10 @@ export function useChat(
const activeChatId = options?.targetChatId ?? chatIdRef.current
if (options?.includeDetail !== false && activeChatId) {
queryClient.invalidateQueries({
- queryKey: taskKeys.detail(activeChatId),
+ queryKey: mothershipChatKeys.detail(activeChatId),
})
}
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
},
[workspaceId, queryClient]
)
@@ -4475,7 +4487,8 @@ export function useChat(
const id = generateId()
const handoffChatId = selectedChatIdRef.current ?? chatIdRef.current
const cachedActiveStreamId = handoffChatId
- ? queryClient.getQueryData(taskKeys.detail(handoffChatId))?.activeStreamId
+ ? queryClient.getQueryData(mothershipChatKeys.detail(handoffChatId))
+ ?.activeStreamId
: undefined
const supersededStreamId =
streamIdRef.current ||
@@ -4631,7 +4644,7 @@ export function useChat(
}
if (requestChatId) {
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(requestChatId) })
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.detail(requestChatId) })
}
const applyOptimisticSend = () => {
@@ -4641,7 +4654,7 @@ export function useChat(
contentBlocks: [],
})
if (requestChatId) {
- upsertTaskChatHistory(requestChatId, (current) => ({
+ upsertChatHistory(requestChatId, (current) => ({
...current,
resources: current.resources.filter((resource) => resource.id !== 'streaming-file'),
messages: [
@@ -4664,7 +4677,7 @@ export function useChat(
const rollbackOptimisticSend = () => {
if (requestChatId) {
- upsertTaskChatHistory(requestChatId, (current) => ({
+ upsertChatHistory(requestChatId, (current) => ({
...current,
messages: current.messages.filter(
(persistedMessage) =>
@@ -4725,7 +4738,9 @@ export function useChat(
throw new Error('Queued message was restored because the selected chat changed.')
}
if (requestChatId) {
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(requestChatId) })
+ await queryClient.cancelQueries({
+ queryKey: mothershipChatKeys.detail(requestChatId),
+ })
}
applyOptimisticSend()
} catch (err) {
@@ -4921,7 +4936,7 @@ export function useChat(
[
workspaceId,
queryClient,
- upsertTaskChatHistory,
+ upsertChatHistory,
processSSEStream,
finalize,
resumeOrFinalize,
@@ -5237,7 +5252,8 @@ export function useChat(
streamIdRef.current ||
activeTurnRef.current?.userMessageId ||
(activeChatId
- ? queryClient.getQueryData(taskKeys.detail(activeChatId))?.activeStreamId
+ ? queryClient.getQueryData(mothershipChatKeys.detail(activeChatId))
+ ?.activeStreamId
: undefined) ||
undefined
@@ -5283,8 +5299,8 @@ export function useChat(
try {
if (activeChatId) {
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(activeChatId) })
- upsertTaskChatHistory(activeChatId, (current) => ({
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.detail(activeChatId) })
+ upsertChatHistory(activeChatId, (current) => ({
...current,
messages: current.messages.map((message) =>
activeAssistantMessageId && message.id === activeAssistantMessageId
@@ -5482,7 +5498,7 @@ export function useChat(
queryClient,
resolveChatIdForStream,
resetEphemeralPreviewState,
- upsertTaskChatHistory,
+ upsertChatHistory,
adoptResolvedChatId,
clearActiveTurn,
setTransportIdle,
@@ -5653,8 +5669,9 @@ export function useChat(
? (() => {
const handoffChatId = selectedChatIdRef.current ?? chatIdRef.current
const cachedActiveStreamId = handoffChatId
- ? queryClient.getQueryData(taskKeys.detail(handoffChatId))
- ?.activeStreamId
+ ? queryClient.getQueryData(
+ mothershipChatKeys.detail(handoffChatId)
+ )?.activeStreamId
: undefined
return {
id: msg.id,
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx
index aa543348625..7f87e271258 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx
@@ -58,7 +58,7 @@ export function InboxTaskList() {
const handleTaskClick = useCallback(
(task: InboxTaskItem) => {
if (task.chatId && (task.status === 'completed' || task.status === 'failed')) {
- router.push(`/workspace/${workspaceId}/task/${task.chatId}`)
+ router.push(`/workspace/${workspaceId}/chat/${task.chatId}`)
}
},
[workspaceId, router]
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx
index 53200176b0c..7cb4d877c4a 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx
@@ -130,8 +130,8 @@ interface CollapsedSidebarMenuProps {
}
}
-interface CollapsedTaskFlyoutItemProps {
- task: { id: string; href: string; name: string; isActive?: boolean; isUnread?: boolean }
+interface CollapsedChatFlyoutItemProps {
+ chat: { id: string; href: string; name: string; isActive?: boolean; isUnread?: boolean }
isCurrentRoute: boolean
isMenuOpen?: boolean
isEditing?: boolean
@@ -141,9 +141,9 @@ interface CollapsedTaskFlyoutItemProps {
onEditValueChange?: (value: string) => void
onEditKeyDown?: (e: React.KeyboardEvent) => void
onEditBlur?: () => void
- onContextMenu?: (e: ReactMouseEvent, taskId: string) => void
+ onContextMenu?: (e: ReactMouseEvent, chatId: string) => void
onMorePointerDown?: () => void
- onMoreClick?: (e: ReactMouseEvent, taskId: string) => void
+ onMoreClick?: (e: ReactMouseEvent, chatId: string) => void
}
interface CollapsedWorkflowFlyoutItemProps {
@@ -213,8 +213,8 @@ export function CollapsedSidebarMenu({
)
}
-export function CollapsedTaskFlyoutItem({
- task,
+export function CollapsedChatFlyoutItem({
+ chat,
isCurrentRoute,
isMenuOpen = false,
isEditing = false,
@@ -227,16 +227,16 @@ export function CollapsedTaskFlyoutItem({
onContextMenu,
onMorePointerDown,
onMoreClick,
-}: CollapsedTaskFlyoutItemProps) {
- const showActions = task.id !== 'new' && onMoreClick
+}: CollapsedChatFlyoutItemProps) {
+ const showActions = chat.id !== 'new' && onMoreClick
if (isEditing) {
return (
onEditValueChange?.(e.target.value)}
onKeyDown={onEditKeyDown}
onBlur={onEditBlur}
@@ -265,7 +265,7 @@ export function CollapsedTaskFlyoutItem({
onMoreClick?.(e, task.id)}
+ onClick={(e) => onMoreClick?.(e, chat.id)}
className={cn(isMenuOpen && 'opacity-100')}
>
@@ -274,15 +274,15 @@ export function CollapsedTaskFlyoutItem({
}
>
onContextMenu(e, task.id) : undefined
+ chat.id !== 'new' && onContextMenu ? (e) => onContextMenu(e, chat.id) : undefined
}
>
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/index.ts
index 5c67ffe801d..addc565dcff 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/index.ts
@@ -1,7 +1,7 @@
export {
+ CollapsedChatFlyoutItem,
CollapsedFileFolderItems,
CollapsedFolderItems,
CollapsedSidebarMenu,
- CollapsedTaskFlyoutItem,
CollapsedWorkflowFlyoutItem,
} from './collapsed-sidebar-menu'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts
index b7c02cae7b3..23e217cbb84 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts
@@ -1,8 +1,8 @@
export {
+ CollapsedChatFlyoutItem,
CollapsedFileFolderItems,
CollapsedFolderItems,
CollapsedSidebarMenu,
- CollapsedTaskFlyoutItem,
CollapsedWorkflowFlyoutItem,
} from './collapsed-sidebar-menu'
export { FileList } from './file-list'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/index.ts
index 96ee9a6b04a..18f0ffa3025 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/index.ts
@@ -9,6 +9,7 @@ export {
} from './command-items'
export {
BlocksGroup,
+ ChatsGroup,
ConnectedAccountsGroup,
DocsGroup,
FilesGroup,
@@ -16,7 +17,6 @@ export {
KnowledgeBasesGroup,
PagesGroup,
TablesGroup,
- TasksGroup,
ToolOpsGroup,
ToolsGroup,
TriggersGroup,
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/index.ts
index 53ec8d7a095..7790ac37907 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/index.ts
@@ -1,5 +1,6 @@
export {
BlocksGroup,
+ ChatsGroup,
ConnectedAccountsGroup,
DocsGroup,
FilesGroup,
@@ -7,7 +8,6 @@ export {
KnowledgeBasesGroup,
PagesGroup,
TablesGroup,
- TasksGroup,
ToolOpsGroup,
ToolsGroup,
TriggersGroup,
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/search-groups.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/search-groups.tsx
index 82941a35075..b46f3eb4520 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/search-groups.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/search-groups/search-groups.tsx
@@ -182,7 +182,7 @@ export const WorkflowsGroup = memo(function WorkflowsGroup({
)
})
-export const TasksGroup = memo(function TasksGroup({
+export const ChatsGroup = memo(function ChatsGroup({
items,
onSelect,
}: {
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx
index be338972aef..7137191b934 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx
@@ -35,6 +35,7 @@ import type {
} from '@/stores/modals/search/types'
import {
BlocksGroup,
+ ChatsGroup,
ConnectedAccountsGroup,
DocsGroup,
FilesGroup,
@@ -42,7 +43,6 @@ import {
KnowledgeBasesGroup,
PagesGroup,
TablesGroup,
- TasksGroup,
ToolOpsGroup,
ToolsGroup,
TriggersGroup,
@@ -67,7 +67,7 @@ export function SearchModal({
onOpenChange,
workflows = [],
workspaces = [],
- tasks = [],
+ chats = [],
tables = [],
files = [],
knowledgeBases = [],
@@ -298,9 +298,9 @@ export function SearchModal({
[workspaceId]
)
- const handleTaskSelect = useCallback(
- (task: TaskItem) => {
- routerRef.current.push(task.href)
+ const handleChatSelect = useCallback(
+ (chat: TaskItem) => {
+ routerRef.current.push(chat.href)
captureEvent(posthogRef.current, 'search_result_selected', {
result_type: 'task',
query_length: deferredSearchRef.current.length,
@@ -481,9 +481,9 @@ export function SearchModal({
() => filterAndSort(workflows, (w) => `${w.name} workflow-${w.id}`, deferredSearch),
[workflows, deferredSearch]
)
- const filteredTasks = useMemo(
- () => filterAndSort(tasks, (t) => `${t.name} task-${t.id}`, deferredSearch),
- [tasks, deferredSearch]
+ const filteredChats = useMemo(
+ () => filterAndSort(chats, (t) => `${t.name} task-${t.id}`, deferredSearch),
+ [chats, deferredSearch]
)
const filteredWorkspaces = useMemo(
() => filterAndSort(workspaces, (w) => `${w.name} workspace-${w.id}`, deferredSearch),
@@ -569,7 +569,7 @@ export function SearchModal({
-
+
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils.ts
index 8653d8e549c..5baacf36d80 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils.ts
@@ -51,7 +51,7 @@ export interface SearchModalProps {
onOpenChange: (open: boolean) => void
workflows?: WorkflowItem[]
workspaces?: WorkspaceItem[]
- tasks?: TaskItem[]
+ chats?: TaskItem[]
tables?: TaskItem[]
files?: FileItem[]
knowledgeBases?: TaskItem[]
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/index.ts
index fc467147942..58c9f83092a 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/index.ts
@@ -1,4 +1,5 @@
export { useAutoScroll } from './use-auto-scroll'
+export { useChatSelection } from './use-chat-selection'
export { useContextMenu } from './use-context-menu'
export { type DropIndicator, useDragDrop } from './use-drag-drop'
export { useFlyoutInlineRename } from './use-flyout-inline-rename'
@@ -14,7 +15,6 @@ export {
useSidebarDragContextValue,
} from './use-sidebar-drag-context'
export { useSidebarResize } from './use-sidebar-resize'
-export { useTaskSelection } from './use-task-selection'
export { useWorkflowOperations } from './use-workflow-operations'
export { useWorkflowSelection } from './use-workflow-selection'
export { useWorkspaceLogoUpload } from './use-workspace-logo-upload'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-chat-selection.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-chat-selection.ts
new file mode 100644
index 00000000000..baaf03c4b70
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-chat-selection.ts
@@ -0,0 +1,44 @@
+import { useCallback } from 'react'
+import { useFolderStore } from '@/stores/folders/store'
+
+interface UseChatSelectionProps {
+ /**
+ * Flat array of all chat IDs in display order
+ */
+ chatIds: string[]
+}
+
+/**
+ * Hook for managing chat selection with support for single and range selection.
+ * Handles shift-click for range selection.
+ * cmd/ctrl+click is handled by the browser (opens in new tab) and never reaches this handler.
+ * Uses the last selected chat as the anchor point for range selections.
+ * Selecting chats clears workflow/folder selections and vice versa.
+ */
+export function useChatSelection({ chatIds }: UseChatSelectionProps) {
+ const selectedChats = useFolderStore((s) => s.selectedChats)
+
+ const handleChatClick = useCallback(
+ (chatId: string, shiftKey: boolean) => {
+ const {
+ selectChatOnly,
+ selectChatRange,
+ toggleChatSelection,
+ lastSelectedChatId: anchor,
+ } = useFolderStore.getState()
+ if (shiftKey && anchor && anchor !== chatId) {
+ selectChatRange(chatIds, anchor, chatId)
+ } else if (shiftKey) {
+ toggleChatSelection(chatId)
+ } else {
+ selectChatOnly(chatId)
+ }
+ },
+ [chatIds]
+ )
+
+ return {
+ selectedChats,
+ handleChatClick,
+ }
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-task-selection.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-task-selection.ts
deleted file mode 100644
index 41dd7d0b806..00000000000
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-task-selection.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { useCallback } from 'react'
-import { useFolderStore } from '@/stores/folders/store'
-
-interface UseTaskSelectionProps {
- /**
- * Flat array of all task IDs in display order
- */
- taskIds: string[]
-}
-
-/**
- * Hook for managing task selection with support for single and range selection.
- * Handles shift-click for range selection.
- * cmd/ctrl+click is handled by the browser (opens in new tab) and never reaches this handler.
- * Uses the last selected task as the anchor point for range selections.
- * Selecting tasks clears workflow/folder selections and vice versa.
- */
-export function useTaskSelection({ taskIds }: UseTaskSelectionProps) {
- const selectedTasks = useFolderStore((s) => s.selectedTasks)
-
- const handleTaskClick = useCallback(
- (taskId: string, shiftKey: boolean) => {
- const {
- selectTaskOnly,
- selectTaskRange,
- toggleTaskSelection,
- lastSelectedTaskId: anchor,
- } = useFolderStore.getState()
- if (shiftKey && anchor && anchor !== taskId) {
- selectTaskRange(taskIds, anchor, taskId)
- } else if (shiftKey) {
- toggleTaskSelection(taskId)
- } else {
- selectTaskOnly(taskId)
- }
- },
- [taskIds]
- )
-
- return {
- selectedTasks,
- handleTaskClick,
- }
-}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
index cec487a0fc9..d9a0a563938 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
@@ -48,9 +48,9 @@ import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/provide
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils'
import {
+ CollapsedChatFlyoutItem,
CollapsedFolderItems,
CollapsedSidebarMenu,
- CollapsedTaskFlyoutItem,
CollapsedWorkflowFlyoutItem,
HelpModal,
NavItemContextMenu,
@@ -70,12 +70,12 @@ import {
SIDEBAR_SECTION_GAP_CLASS,
} from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
import {
+ useChatSelection,
useContextMenu,
useFlyoutInlineRename,
useFolderOperations,
useHoverMenu,
useSidebarResize,
- useTaskSelection,
useWorkflowOperations,
useWorkspaceLogoUpload,
useWorkspaceManagement,
@@ -89,23 +89,23 @@ import { useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
import { useWorkspaceCredentials } from '@/hooks/queries/credentials'
import { useFolderMap, useFolders } from '@/hooks/queries/folders'
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
-import { useTablesList } from '@/hooks/queries/tables'
import {
- useCreateTask,
- useDeleteTask,
- useDeleteTasks,
- useMarkTaskRead,
- useMarkTaskUnread,
- useRenameTask,
- useSetTaskPinned,
- useTasks,
-} from '@/hooks/queries/tasks'
+ useCreateMothershipChat,
+ useDeleteMothershipChat,
+ useDeleteMothershipChats,
+ useMarkMothershipChatRead,
+ useMarkMothershipChatUnread,
+ useMothershipChats,
+ useRenameMothershipChat,
+ useSetMothershipChatPinned,
+} from '@/hooks/queries/mothership-chats'
+import { useTablesList } from '@/hooks/queries/tables'
import { useUpdateWorkflow } from '@/hooks/queries/workflows'
import type { Workspace } from '@/hooks/queries/workspace'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
+import { useMothershipChatEvents } from '@/hooks/use-mothership-chat-events'
import { usePermissionConfig } from '@/hooks/use-permission-config'
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
-import { useTaskEvents } from '@/hooks/use-task-events'
import { SIDEBAR_WIDTH } from '@/stores/constants'
import { useFolderStore } from '@/stores/folders/store'
import { useSearchModalStore } from '@/stores/modals/search/store'
@@ -145,8 +145,8 @@ function SidebarItemSkeleton() {
)
}
-const SidebarTaskItem = memo(function SidebarTaskItem({
- task,
+const SidebarChatItem = memo(function SidebarChatItem({
+ chat,
isCurrentRoute,
isSelected,
isActive,
@@ -159,7 +159,7 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
onMorePointerDown,
onMoreClick,
}: {
- task: { id: string; href: string; name: string }
+ chat: { id: string; href: string; name: string }
isCurrentRoute: boolean
isSelected: boolean
isActive: boolean
@@ -167,10 +167,10 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
isPinned: boolean
isMenuOpen: boolean
showCollapsedTooltips: boolean
- onMultiSelectClick: (taskId: string, shiftKey: boolean) => void
- onContextMenu: (e: React.MouseEvent, taskId: string) => void
+ onMultiSelectClick: (chatId: string, shiftKey: boolean) => void
+ onContextMenu: (e: React.MouseEvent, chatId: string) => void
onMorePointerDown: () => void
- onMoreClick: (e: React.MouseEvent, taskId: string) => void
+ onMoreClick: (e: React.MouseEvent, chatId: string) => void
}) {
const dragGhostRef = useRef(null)
@@ -178,9 +178,9 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
e.dataTransfer.effectAllowed = 'copyMove'
e.dataTransfer.setData(
SIM_RESOURCES_DRAG_TYPE,
- JSON.stringify([{ type: 'task', id: task.id, title: task.name }])
+ JSON.stringify([{ type: 'task', id: chat.id, title: chat.name }])
)
- const ghost = createSidebarDragGhost(task.name, { kind: 'task' })
+ const ghost = createSidebarDragGhost(chat.name, { kind: 'task' })
void ghost.offsetHeight
e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2)
dragGhostRef.current = ghost
@@ -194,9 +194,9 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
}
return (
-
+
onContextMenu(e, task.id)}
+ onContextMenu={(e) => onContextMenu(e, chat.id)}
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
- {task.name}
- {task.id !== 'new' && (
+ {chat.name}
+ {chat.id !== 'new' && (
{(isActive || (!isCurrentRoute && isUnread)) && (
{
e.preventDefault()
e.stopPropagation()
- onMoreClick(e, task.id)
+ onMoreClick(e, chat.id)
}}
className={cn(
'absolute inset-0 flex items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100',
@@ -589,77 +589,77 @@ export const Sidebar = memo(function Sidebar() {
}
}, [activeNavItemHref])
- const createTaskMutation = useCreateTask(workspaceId)
- const deleteTaskMutation = useDeleteTask(workspaceId)
- const deleteTasksMutation = useDeleteTasks(workspaceId)
- const markTaskReadMutation = useMarkTaskRead(workspaceId)
- const markTaskUnreadMutation = useMarkTaskUnread(workspaceId)
- const renameTaskMutation = useRenameTask(workspaceId)
- const setTaskPinnedMutation = useSetTaskPinned(workspaceId)
- const tasksHover = useHoverMenu()
+ const createChatMutation = useCreateMothershipChat(workspaceId)
+ const deleteChatMutation = useDeleteMothershipChat(workspaceId)
+ const deleteChatsMutation = useDeleteMothershipChats(workspaceId)
+ const markChatReadMutation = useMarkMothershipChatRead(workspaceId)
+ const markChatUnreadMutation = useMarkMothershipChatUnread(workspaceId)
+ const renameChatMutation = useRenameMothershipChat(workspaceId)
+ const setChatPinnedMutation = useSetMothershipChatPinned(workspaceId)
+ const chatsHover = useHoverMenu()
const workflowsHover = useHoverMenu()
const {
- isOpen: isTaskContextMenuOpen,
- position: taskContextMenuPosition,
- menuRef: taskMenuRef,
- handleContextMenu: handleTaskContextMenuBase,
- closeMenu: closeTaskContextMenu,
- preventDismiss: preventTaskDismiss,
+ isOpen: isChatContextMenuOpen,
+ position: chatContextMenuPosition,
+ menuRef: chatMenuRef,
+ handleContextMenu: handleChatContextMenuBase,
+ closeMenu: closeChatContextMenu,
+ preventDismiss: preventChatDismiss,
} = useContextMenu()
- const isCreatingTaskRef = useRef(false)
- const contextMenuSelectionRef = useRef<{ taskIds: string[]; names: string[] }>({
- taskIds: [],
+ const isCreatingChatRef = useRef(false)
+ const contextMenuSelectionRef = useRef<{ chatIds: string[]; names: string[] }>({
+ chatIds: [],
names: [],
})
- const [menuOpenTaskId, setMenuOpenTaskId] = useState(null)
+ const [menuOpenChatId, setMenuOpenChatId] = useState(null)
useEffect(() => {
- if (!isTaskContextMenuOpen) setMenuOpenTaskId(null)
- }, [isTaskContextMenuOpen])
+ if (!isChatContextMenuOpen) setMenuOpenChatId(null)
+ }, [isChatContextMenuOpen])
- const captureTaskSelection = useCallback((taskId: string) => {
- const { selectedTasks, selectTaskOnly } = useFolderStore.getState()
- if (selectedTasks.size > 0 && selectedTasks.has(taskId)) {
+ const captureChatSelection = useCallback((chatId: string) => {
+ const { selectedChats, selectChatOnly } = useFolderStore.getState()
+ if (selectedChats.size > 0 && selectedChats.has(chatId)) {
contextMenuSelectionRef.current = {
- taskIds: Array.from(selectedTasks),
+ chatIds: Array.from(selectedChats),
names: [],
}
} else {
- selectTaskOnly(taskId)
- contextMenuSelectionRef.current = { taskIds: [taskId], names: [] }
+ selectChatOnly(chatId)
+ contextMenuSelectionRef.current = { chatIds: [chatId], names: [] }
}
}, [])
- const handleTaskContextMenu = useCallback(
- (e: React.MouseEvent, taskId: string) => {
- captureTaskSelection(taskId)
- setMenuOpenTaskId(taskId)
- tasksHover.setLocked(true)
- preventTaskDismiss()
- handleTaskContextMenuBase(e)
+ const handleChatContextMenu = useCallback(
+ (e: React.MouseEvent, chatId: string) => {
+ captureChatSelection(chatId)
+ setMenuOpenChatId(chatId)
+ chatsHover.setLocked(true)
+ preventChatDismiss()
+ handleChatContextMenuBase(e)
},
- [captureTaskSelection, handleTaskContextMenuBase, preventTaskDismiss, tasksHover]
+ [captureChatSelection, handleChatContextMenuBase, preventChatDismiss, chatsHover]
)
- const handleTaskMorePointerDown = useCallback(() => {
- if (isTaskContextMenuOpen) {
- preventTaskDismiss()
+ const handleChatMorePointerDown = useCallback(() => {
+ if (isChatContextMenuOpen) {
+ preventChatDismiss()
}
- }, [isTaskContextMenuOpen, preventTaskDismiss])
+ }, [isChatContextMenuOpen, preventChatDismiss])
- const handleTaskMoreClick = useCallback(
- (e: React.MouseEvent, taskId: string) => {
- if (isTaskContextMenuOpen) {
- closeTaskContextMenu()
+ const handleChatMoreClick = useCallback(
+ (e: React.MouseEvent, chatId: string) => {
+ if (isChatContextMenuOpen) {
+ closeChatContextMenu()
return
}
- tasksHover.setLocked(true)
- captureTaskSelection(taskId)
- setMenuOpenTaskId(taskId)
+ chatsHover.setLocked(true)
+ captureChatSelection(chatId)
+ setMenuOpenChatId(chatId)
const rect = e.currentTarget.getBoundingClientRect()
- handleTaskContextMenuBase({
+ handleChatContextMenuBase({
preventDefault: () => {},
stopPropagation: () => {},
clientX: rect.right,
@@ -667,11 +667,11 @@ export const Sidebar = memo(function Sidebar() {
} as React.MouseEvent)
},
[
- isTaskContextMenuOpen,
- closeTaskContextMenu,
- captureTaskSelection,
- handleTaskContextMenuBase,
- tasksHover,
+ isChatContextMenuOpen,
+ closeChatContextMenu,
+ captureChatSelection,
+ handleChatContextMenuBase,
+ chatsHover,
]
)
@@ -793,19 +793,19 @@ export const Sidebar = memo(function Sidebar() {
[navigateToSettings, getSettingsHref, setSidebarWidth]
)
- const { data: fetchedTasks = [], isLoading: tasksLoading } = useTasks(workspaceId)
+ const { data: fetchedChats = [], isLoading: chatsLoading } = useMothershipChats(workspaceId)
- useTaskEvents(workspaceId)
+ useMothershipChatEvents(workspaceId)
- const tasks = useMemo(
+ const chats = useMemo(
() =>
- fetchedTasks
- ? fetchedTasks.map((t) => ({
+ fetchedChats
+ ? fetchedChats.map((t) => ({
...t,
- href: `/workspace/${workspaceId}/task/${t.id}`,
+ href: `/workspace/${workspaceId}/chat/${t.id}`,
}))
: [],
- [fetchedTasks, workspaceId]
+ [fetchedChats, workspaceId]
)
const { data: fetchedTables = [] } = useTablesList(workspaceId)
@@ -849,26 +849,26 @@ export const Sidebar = memo(function Sidebar() {
[fetchedKnowledgeBases, workspaceId, permissionConfig.hideKnowledgeBaseTab]
)
- const taskIds = useMemo(() => tasks.map((t) => t.id), [tasks])
+ const chatIds = useMemo(() => chats.map((t) => t.id), [chats])
- const { selectedTasks, handleTaskClick } = useTaskSelection({ taskIds })
- const hasTaskMultiSelection = selectedTasks.size > 1
+ const { selectedChats, handleChatClick } = useChatSelection({ chatIds })
+ const hasChatMultiSelection = selectedChats.size > 1
- const isMultiTaskContextMenu = contextMenuSelectionRef.current.taskIds.length > 1
- const activeTaskContextMenuItem =
- !isMultiTaskContextMenu && contextMenuSelectionRef.current.taskIds.length === 1
- ? tasks.find((task) => task.id === contextMenuSelectionRef.current.taskIds[0])
+ const isMultiChatContextMenu = contextMenuSelectionRef.current.chatIds.length > 1
+ const activeChatContextMenuItem =
+ !isMultiChatContextMenu && contextMenuSelectionRef.current.chatIds.length === 1
+ ? chats.find((chat) => chat.id === contextMenuSelectionRef.current.chatIds[0])
: null
- const [isTaskDeleteModalOpen, setIsTaskDeleteModalOpen] = useState(false)
+ const [isChatDeleteModalOpen, setIsChatDeleteModalOpen] = useState(false)
- const handleDeleteTask = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const handleDeleteChat = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length === 0) return
- const names = ids.map((id) => tasks.find((t) => t.id === id)?.name).filter(Boolean) as string[]
- contextMenuSelectionRef.current = { taskIds: ids, names }
- setIsTaskDeleteModalOpen(true)
- }, [tasks])
+ const names = ids.map((id) => chats.find((t) => t.id === id)?.name).filter(Boolean) as string[]
+ contextMenuSelectionRef.current = { chatIds: ids, names }
+ setIsChatDeleteModalOpen(true)
+ }, [chats])
const navigateToPage = useCallback(
(path: string) => {
@@ -880,35 +880,35 @@ export const Sidebar = memo(function Sidebar() {
[setSidebarWidth, router]
)
- const handleConfirmDeleteTasks = () => {
- const { taskIds: taskIdsToDelete } = contextMenuSelectionRef.current
- if (taskIdsToDelete.length === 0) return
+ const handleConfirmDeleteChats = () => {
+ const { chatIds: chatIdsToDelete } = contextMenuSelectionRef.current
+ if (chatIdsToDelete.length === 0) return
const currentPath = pathname ?? ''
- const isViewingDeletedTask = taskIdsToDelete.some(
- (id) => currentPath === `/workspace/${workspaceId}/task/${id}`
+ const isViewingDeletedChat = chatIdsToDelete.some(
+ (id) => currentPath === `/workspace/${workspaceId}/chat/${id}`
)
const onDeleteSuccess = () => {
- useFolderStore.getState().clearTaskSelection()
- if (isViewingDeletedTask) {
+ useFolderStore.getState().clearChatSelection()
+ if (isViewingDeletedChat) {
navigateToPage(`/workspace/${workspaceId}/home`)
}
}
- if (taskIdsToDelete.length === 1) {
- deleteTaskMutation.mutate(taskIdsToDelete[0], { onSuccess: onDeleteSuccess })
+ if (chatIdsToDelete.length === 1) {
+ deleteChatMutation.mutate(chatIdsToDelete[0], { onSuccess: onDeleteSuccess })
} else {
- deleteTasksMutation.mutate(taskIdsToDelete, { onSuccess: onDeleteSuccess })
+ deleteChatsMutation.mutate(chatIdsToDelete, { onSuccess: onDeleteSuccess })
}
- setIsTaskDeleteModalOpen(false)
+ setIsChatDeleteModalOpen(false)
}
- const [visibleTaskCount, setVisibleTaskCount] = useState(5)
- const taskFlyoutRename = useFlyoutInlineRename({
+ const [visibleChatCount, setVisibleChatCount] = useState(5)
+ const chatFlyoutRename = useFlyoutInlineRename({
itemType: 'task',
- onSave: async (taskId, name) => {
- await renameTaskMutation.mutateAsync({ chatId: taskId, title: name })
+ onSave: async (chatId, name) => {
+ await renameChatMutation.mutateAsync({ chatId: chatId, title: name })
},
})
@@ -924,49 +924,49 @@ export const Sidebar = memo(function Sidebar() {
})
useEffect(() => {
- tasksHover.setLocked(isTaskContextMenuOpen || !!taskFlyoutRename.editingId)
- }, [isTaskContextMenuOpen, taskFlyoutRename.editingId, tasksHover.setLocked])
+ chatsHover.setLocked(isChatContextMenuOpen || !!chatFlyoutRename.editingId)
+ }, [isChatContextMenuOpen, chatFlyoutRename.editingId, chatsHover.setLocked])
useEffect(() => {
workflowsHover.setLocked(!!workflowFlyoutRename.editingId)
}, [workflowFlyoutRename.editingId, workflowsHover.setLocked])
- const handleTaskOpenInNewTab = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const handleChatOpenInNewTab = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length !== 1) return
- window.open(`/workspace/${workspaceId}/task/${ids[0]}`, '_blank', 'noopener,noreferrer')
+ window.open(`/workspace/${workspaceId}/chat/${ids[0]}`, '_blank', 'noopener,noreferrer')
}, [workspaceId])
- const handleMarkTaskAsRead = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const handleMarkChatAsRead = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length !== 1) return
- markTaskReadMutation.mutate(ids[0])
+ markChatReadMutation.mutate(ids[0])
}, [])
- const handleMarkTaskAsUnread = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const handleMarkChatAsUnread = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length !== 1) return
- markTaskUnreadMutation.mutate(ids[0])
+ markChatUnreadMutation.mutate(ids[0])
}, [])
- const handleStartTaskRename = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const handleStartChatRename = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length !== 1) return
- const taskId = ids[0]
- const task = tasks.find((t) => t.id === taskId)
- if (!task) return
- tasksHover.setLocked(true)
- taskFlyoutRename.startRename({ id: taskId, name: task.name })
- }, [taskFlyoutRename, tasks, tasksHover])
-
- const handleToggleTaskPin = useCallback(() => {
- const { taskIds: ids } = contextMenuSelectionRef.current
+ const chatId = ids[0]
+ const chat = chats.find((t) => t.id === chatId)
+ if (!chat) return
+ chatsHover.setLocked(true)
+ chatFlyoutRename.startRename({ id: chatId, name: chat.name })
+ }, [chatFlyoutRename, chats, chatsHover])
+
+ const handleToggleChatPin = useCallback(() => {
+ const { chatIds: ids } = contextMenuSelectionRef.current
if (ids.length !== 1) return
- const taskId = ids[0]
- const task = tasks.find((t) => t.id === taskId)
- if (!task) return
- setTaskPinnedMutation.mutate({ chatId: taskId, pinned: !task.isPinned })
- }, [tasks, setTaskPinnedMutation])
+ const chatId = ids[0]
+ const chat = chats.find((t) => t.id === chatId)
+ if (!chat) return
+ setChatPinnedMutation.mutate({ chatId: chatId, pinned: !chat.isPinned })
+ }, [chats, setChatPinnedMutation])
const handleCollapsedWorkflowOpenInNewTab = useCallback(
(workflow: { id: string }) => {
@@ -1123,7 +1123,7 @@ export const Sidebar = memo(function Sidebar() {
[workspaces, handleLeaveWorkspace]
)
- const tasksCollapsedIcon =
+ const chatsCollapsedIcon =
const workflowsCollapsedIcon = (
@@ -1134,29 +1134,29 @@ export const Sidebar = memo(function Sidebar() {
onSelect: handleCreateWorkflow,
}
- const handleNewTask = useCallback(async () => {
- if (!workspaceId || isCreatingTaskRef.current) return
- isCreatingTaskRef.current = true
+ const handleNewChat = useCallback(async () => {
+ if (!workspaceId || isCreatingChatRef.current) return
+ isCreatingChatRef.current = true
try {
- const { id } = await createTaskMutation.mutateAsync()
+ const { id } = await createChatMutation.mutateAsync()
useMothershipDraftsStore.getState().clearDraft(`${workspaceId}:new`)
- navigateToPage(`/workspace/${workspaceId}/task/${id}`)
+ navigateToPage(`/workspace/${workspaceId}/chat/${id}`)
} catch {
navigateToPage(`/workspace/${workspaceId}/home`)
} finally {
- isCreatingTaskRef.current = false
+ isCreatingChatRef.current = false
}
}, [workspaceId, navigateToPage])
- const tasksPrimaryAction = {
+ const chatsPrimaryAction = {
label: 'New chat',
- onSelect: handleNewTask,
+ onSelect: handleNewChat,
}
- const handleSeeMoreTasks = useCallback(() => setVisibleTaskCount((prev) => prev + 5), [])
- const handleSeeLessTasks = useCallback(() => setVisibleTaskCount(5), [])
+ const handleSeeMoreChats = useCallback(() => setVisibleChatCount((prev) => prev + 5), [])
+ const handleSeeLessChats = useCallback(() => setVisibleChatCount(5), [])
- const handleCloseTaskDeleteModal = useCallback(() => setIsTaskDeleteModalOpen(false), [])
+ const handleCloseChatDeleteModal = useCallback(() => setIsChatDeleteModalOpen(false), [])
const handleEdgeKeyDown = useCallback(
(e: React.KeyboardEvent) => {
@@ -1175,9 +1175,9 @@ export const Sidebar = memo(function Sidebar() {
captureEvent(posthog, 'docs_opened', { source: 'help_menu' })
}, [posthog])
- const handleTaskRenameBlur = useCallback(
- () => void taskFlyoutRename.saveRename(),
- [taskFlyoutRename.saveRename]
+ const handleChatRenameBlur = useCallback(
+ () => void chatFlyoutRename.saveRename(),
+ [chatFlyoutRename.saveRename]
)
const handleWorkflowRenameBlur = useCallback(
@@ -1244,7 +1244,7 @@ export const Sidebar = memo(function Sidebar() {
{
id: 'add-task',
handler: () => {
- handleNewTask()
+ handleNewChat()
},
},
])
@@ -1338,7 +1338,7 @@ export const Sidebar = memo(function Sidebar() {
)}
>
-
+
Chats
{!isCollapsed && (
@@ -1348,8 +1348,8 @@ export const Sidebar = memo(function Sidebar() {
@@ -1365,76 +1365,76 @@ export const Sidebar = memo(function Sidebar() {
{isCollapsed ? (
- {tasksLoading ? (
+ {chatsLoading ? (
Loading...
- ) : tasks.length === 0 ? (
+ ) : chats.length === 0 ? (
No chats yet
) : (
- tasks.map((task) => (
- (
+
))
)}
) : (
- {tasksLoading ? (
+ {chatsLoading ? (
) : (
<>
- {tasks.length === 0 ? (
+ {chats.length === 0 ? (
No chats yet
) : null}
- {/* `selectTaskOnly` populates `selectedTasks` on every click, so
+ {/* `selectChatOnly` populates `selectedChats` on every click, so
a single entry just means "last clicked" — already conveyed by
`isCurrentRoute`. Highlight from selection only for explicit
multi-selection (size > 1), otherwise it lingers after navigating
- away from a task. */}
- {tasks.slice(0, visibleTaskCount).map((task) => {
- const isCurrentRoute = pathname === task.href
- const isRenaming = taskFlyoutRename.editingId === task.id
+ away from a chat. */}
+ {chats.slice(0, visibleChatCount).map((chat) => {
+ const isCurrentRoute = pathname === chat.href
+ const isRenaming = chatFlyoutRename.editingId === chat.id
const isSelected =
- task.id !== 'new' &&
- hasTaskMultiSelection &&
- selectedTasks.has(task.id)
+ chat.id !== 'new' &&
+ hasChatMultiSelection &&
+ selectedChats.has(chat.id)
if (isRenaming) {
return (
taskFlyoutRename.setValue(e.target.value)}
- onKeyDown={taskFlyoutRename.handleKeyDown}
- onBlur={handleTaskRenameBlur}
+ ref={chatFlyoutRename.inputRef}
+ value={chatFlyoutRename.value}
+ onChange={(e) => chatFlyoutRename.setValue(e.target.value)}
+ onKeyDown={chatFlyoutRename.handleKeyDown}
+ onBlur={handleChatRenameBlur}
className='min-w-0 flex-1 border-none bg-transparent text-[14px] text-[var(--text-body)] outline-none'
/>
@@ -1442,37 +1442,37 @@ export const Sidebar = memo(function Sidebar() {
}
return (
-
)
})}
- {tasks.length > 5 && (
+ {chats.length > 5 && (
)}
>
@@ -1714,36 +1714,36 @@ export const Sidebar = memo(function Sidebar() {
/>
@@ -1772,7 +1772,7 @@ export const Sidebar = memo(function Sidebar() {
onOpenChange={setIsSearchModalOpen}
workflows={searchModalWorkflows}
workspaces={searchModalWorkspaces}
- tasks={tasks}
+ chats={chats}
tables={searchModalTables}
files={searchModalFiles}
knowledgeBases={searchModalKnowledgeBases}
diff --git a/apps/sim/hooks/queries/tasks.test.ts b/apps/sim/hooks/queries/mothership-chats.test.ts
similarity index 90%
rename from apps/sim/hooks/queries/tasks.test.ts
rename to apps/sim/hooks/queries/mothership-chats.test.ts
index 641ede02146..6555d30846d 100644
--- a/apps/sim/hooks/queries/tasks.test.ts
+++ b/apps/sim/hooks/queries/mothership-chats.test.ts
@@ -21,7 +21,11 @@ vi.mock('@tanstack/react-query', () => ({
useMutation: vi.fn((options) => options),
}))
-import { fetchChatHistory, fetchTasks, useAddChatResource } from '@/hooks/queries/tasks'
+import {
+ fetchMothershipChatHistory,
+ fetchMothershipChats,
+ useAddChatResource,
+} from '@/hooks/queries/mothership-chats'
function jsonResponse(body: unknown, init?: ResponseInit): Response {
return new Response(JSON.stringify(body), {
@@ -60,7 +64,7 @@ describe('tasks query boundary parsing', () => {
})
)
- const tasks = await fetchTasks('ws-1')
+ const tasks = await fetchMothershipChats('ws-1')
expect(tasks).toHaveLength(1)
expect(tasks[0]).toEqual(
@@ -92,7 +96,9 @@ describe('tasks query boundary parsing', () => {
})
)
- await expect(fetchTasks('ws-1')).rejects.toThrow('Response failed contract validation')
+ await expect(fetchMothershipChats('ws-1')).rejects.toThrow(
+ 'Response failed contract validation'
+ )
})
it('parses valid mothership chat history responses', async () => {
@@ -114,7 +120,7 @@ describe('tasks query boundary parsing', () => {
})
)
- const history = await fetchChatHistory('chat-1')
+ const history = await fetchMothershipChatHistory('chat-1')
expect(history).toEqual({
id: 'chat-1',
@@ -146,7 +152,7 @@ describe('tasks query boundary parsing', () => {
})
)
- await expect(fetchChatHistory('chat-1')).rejects.toThrow(
+ await expect(fetchMothershipChatHistory('chat-1')).rejects.toThrow(
'Invalid chat response: chat.resources[0].type is invalid'
)
})
diff --git a/apps/sim/hooks/queries/tasks.ts b/apps/sim/hooks/queries/mothership-chats.ts
similarity index 62%
rename from apps/sim/hooks/queries/tasks.ts
rename to apps/sim/hooks/queries/mothership-chats.ts
index 65a1d5c4bea..bc7743804d6 100644
--- a/apps/sim/hooks/queries/tasks.ts
+++ b/apps/sim/hooks/queries/mothership-chats.ts
@@ -14,11 +14,11 @@ import {
forkMothershipChatContract,
getMothershipChatContract,
listMothershipChatsContract,
- type MothershipTask,
+ type MothershipChat,
removeMothershipChatResourceContract,
reorderMothershipChatResourcesContract,
updateMothershipChatContract,
-} from '@/lib/api/contracts/mothership-tasks'
+} from '@/lib/api/contracts/mothership-chats'
import type { PersistedMessage } from '@/lib/copilot/chat/persisted-message'
import { normalizeMessage } from '@/lib/copilot/chat/persisted-message'
import {
@@ -29,7 +29,7 @@ import { isStreamBatchEvent, type StreamBatchEvent } from '@/lib/copilot/request
import { type MothershipResource, MothershipResourceType } from '@/lib/copilot/resources/types'
import { useMothershipQueueStore } from '@/stores/mothership-queue/store'
-export interface TaskMetadata {
+export interface MothershipChatMetadata {
id: string
name: string
updatedAt: Date
@@ -38,7 +38,7 @@ export interface TaskMetadata {
isPinned: boolean
}
-export interface TaskChatHistory {
+export interface MothershipChatHistory {
id: string
title: string | null
messages: PersistedMessage[]
@@ -51,12 +51,13 @@ export interface TaskChatHistory {
} | null
}
-export const taskKeys = {
- all: ['tasks'] as const,
- lists: () => [...taskKeys.all, 'list'] as const,
- list: (workspaceId: string | undefined) => [...taskKeys.lists(), workspaceId ?? ''] as const,
- details: () => [...taskKeys.all, 'detail'] as const,
- detail: (chatId: string | undefined) => [...taskKeys.details(), chatId ?? ''] as const,
+export const mothershipChatKeys = {
+ all: ['mothership-chats'] as const,
+ lists: () => [...mothershipChatKeys.all, 'list'] as const,
+ list: (workspaceId: string | undefined) =>
+ [...mothershipChatKeys.lists(), workspaceId ?? ''] as const,
+ details: () => [...mothershipChatKeys.all, 'detail'] as const,
+ detail: (chatId: string | undefined) => [...mothershipChatKeys.details(), chatId ?? ''] as const,
}
function isRecord(value: unknown): value is Record
{
@@ -80,7 +81,7 @@ function isResourceType(value: unknown): value is MothershipResource['type'] {
)
}
-function parseStreamSnapshot(value: unknown): TaskChatHistory['streamSnapshot'] {
+function parseStreamSnapshot(value: unknown): MothershipChatHistory['streamSnapshot'] {
if (!isRecord(value)) {
return null
}
@@ -140,7 +141,7 @@ function parseResources(value: unknown, context: string): MothershipResource[] {
function parseStrictStreamSnapshot(
value: unknown,
context: string
-): TaskChatHistory['streamSnapshot'] {
+): MothershipChatHistory['streamSnapshot'] {
if (value === undefined || value === null) {
return null
}
@@ -150,7 +151,7 @@ function parseStrictStreamSnapshot(
return snapshot
}
-function parseChatHistory(value: unknown): TaskChatHistory {
+function parseChatHistory(value: unknown): MothershipChatHistory {
const responseContext = 'Invalid chat response'
const chatContext = `${responseContext}: chat`
@@ -185,7 +186,7 @@ function parseChatResourcesResponse(value: unknown): { resources: MothershipReso
}
}
-function mapTask(chat: MothershipTask): TaskMetadata {
+function mapChat(chat: MothershipChat): MothershipChatMetadata {
const updatedAt = new Date(chat.updatedAt)
return {
id: chat.id,
@@ -199,34 +200,34 @@ function mapTask(chat: MothershipTask): TaskMetadata {
}
}
-export async function fetchTasks(
+export async function fetchMothershipChats(
workspaceId: string,
signal?: AbortSignal
-): Promise {
+): Promise {
const data = await requestJson(listMothershipChatsContract, {
query: { workspaceId },
signal,
})
- return data.data.map(mapTask)
+ return data.data.map(mapChat)
}
/**
- * Fetches mothership chat tasks for a workspace.
+ * Fetches mothership chat chats for a workspace.
* These are workspace-scoped conversations from the Home page.
*/
-export function useTasks(workspaceId?: string) {
+export function useMothershipChats(workspaceId?: string) {
return useQuery({
- queryKey: taskKeys.list(workspaceId),
- queryFn: workspaceId ? ({ signal }) => fetchTasks(workspaceId, signal) : skipToken,
+ queryKey: mothershipChatKeys.list(workspaceId),
+ queryFn: workspaceId ? ({ signal }) => fetchMothershipChats(workspaceId, signal) : skipToken,
placeholderData: keepPreviousData,
staleTime: 60 * 1000,
})
}
-export async function fetchChatHistory(
+export async function fetchMothershipChatHistory(
chatId: string,
signal?: AbortSignal
-): Promise {
+): Promise {
try {
const data = await requestJson(getMothershipChatContract, {
params: { chatId },
@@ -254,60 +255,59 @@ export async function fetchChatHistory(
}
/**
- * Fetches chat history for a single task (mothership chat).
- * Used by the task page to load an existing conversation.
+ * Fetches chat history for a single chat (mothership chat).
+ * Used by the chat page to load an existing conversation.
*/
-export function useChatHistory(chatId: string | undefined) {
+export function useMothershipChatHistory(chatId: string | undefined) {
return useQuery({
- queryKey: taskKeys.detail(chatId),
- queryFn: ({ signal }) => fetchChatHistory(chatId!, signal),
- enabled: Boolean(chatId),
+ queryKey: mothershipChatKeys.detail(chatId),
+ queryFn: chatId ? ({ signal }) => fetchMothershipChatHistory(chatId, signal) : skipToken,
staleTime: 30 * 1000,
})
}
-async function deleteTask(chatId: string): Promise {
+async function deleteChat(chatId: string): Promise {
await requestJson(deleteMothershipChatContract, {
params: { chatId },
})
}
/**
- * Deletes a mothership chat task and invalidates the task list.
+ * Deletes a mothership chat chat and invalidates the chat list.
*/
-export function useDeleteTask(workspaceId?: string) {
+export function useDeleteMothershipChat(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
- mutationFn: deleteTask,
+ mutationFn: deleteChat,
onSettled: (_data, _error, chatId) => {
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
- queryClient.removeQueries({ queryKey: taskKeys.detail(chatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
+ queryClient.removeQueries({ queryKey: mothershipChatKeys.detail(chatId) })
useMothershipQueueStore.getState().clearChat(chatId)
},
})
}
/**
- * Deletes multiple mothership chat tasks and invalidates the task list.
+ * Deletes multiple mothership chat chats and invalidates the chat list.
*/
-export function useDeleteTasks(workspaceId?: string) {
+export function useDeleteMothershipChats(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (chatIds: string[]) => {
- await Promise.all(chatIds.map(deleteTask))
+ await Promise.all(chatIds.map(deleteChat))
},
onSettled: (_data, _error, chatIds) => {
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
const queueStore = useMothershipQueueStore.getState()
for (const chatId of chatIds) {
- queryClient.removeQueries({ queryKey: taskKeys.detail(chatId) })
+ queryClient.removeQueries({ queryKey: mothershipChatKeys.detail(chatId) })
queueStore.clearChat(chatId)
}
},
})
}
-async function renameTask({ chatId, title }: { chatId: string; title: string }): Promise {
+async function renameChat({ chatId, title }: { chatId: string; title: string }): Promise {
await requestJson(updateMothershipChatContract, {
params: { chatId },
body: { title },
@@ -315,31 +315,34 @@ async function renameTask({ chatId, title }: { chatId: string; title: string }):
}
/**
- * Renames a mothership chat task with optimistic update.
+ * Renames a mothership chat chat with optimistic update.
*/
-export function useRenameTask(workspaceId?: string) {
+export function useRenameMothershipChat(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
- mutationFn: renameTask,
+ mutationFn: renameChat,
onMutate: async ({ chatId, title }) => {
- await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
- const previousTasks = queryClient.getQueryData(taskKeys.list(workspaceId))
+ const previousChats = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
- queryClient.setQueryData(taskKeys.list(workspaceId), (old) =>
- old?.map((task) => (task.id === chatId ? { ...task, name: title } : task))
+ queryClient.setQueryData(
+ mothershipChatKeys.list(workspaceId),
+ (old) => old?.map((chat) => (chat.id === chatId ? { ...chat, name: title } : chat))
)
- return { previousTasks }
+ return { previousChats }
},
onError: (_err, _variables, context) => {
- if (context?.previousTasks) {
- queryClient.setQueryData(taskKeys.list(workspaceId), context.previousTasks)
+ if (context?.previousChats) {
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), context.previousChats)
}
},
onSettled: (_data, _error, variables) => {
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(variables.chatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(variables.chatId) })
},
})
}
@@ -360,14 +363,16 @@ export function useAddChatResource(chatId?: string) {
mutationFn: addChatResource,
onMutate: async ({ resource }) => {
if (!chatId) return
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(chatId) })
- const previous = queryClient.getQueryData(taskKeys.detail(chatId))
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.detail(chatId) })
+ const previous = queryClient.getQueryData(
+ mothershipChatKeys.detail(chatId)
+ )
if (previous) {
const exists = previous.resources.some(
(r) => r.type === resource.type && r.id === resource.id
)
if (!exists) {
- queryClient.setQueryData(taskKeys.detail(chatId), {
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), {
...previous,
resources: [...previous.resources, resource],
})
@@ -377,12 +382,12 @@ export function useAddChatResource(chatId?: string) {
},
onError: (_err, _variables, context) => {
if (context?.previous && chatId) {
- queryClient.setQueryData(taskKeys.detail(chatId), context.previous)
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), context.previous)
}
},
onSettled: () => {
if (chatId) {
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(chatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(chatId) })
}
},
})
@@ -404,10 +409,12 @@ export function useReorderChatResources(chatId?: string) {
mutationFn: reorderChatResources,
onMutate: async ({ resources }) => {
if (!chatId) return
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(chatId) })
- const previous = queryClient.getQueryData(taskKeys.detail(chatId))
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.detail(chatId) })
+ const previous = queryClient.getQueryData(
+ mothershipChatKeys.detail(chatId)
+ )
if (previous) {
- queryClient.setQueryData(taskKeys.detail(chatId), {
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), {
...previous,
resources,
})
@@ -416,12 +423,12 @@ export function useReorderChatResources(chatId?: string) {
},
onError: (_err, _variables, context) => {
if (context?.previous && chatId) {
- queryClient.setQueryData(taskKeys.detail(chatId), context.previous)
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), context.previous)
}
},
onSettled: () => {
if (chatId) {
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(chatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(chatId) })
}
},
})
@@ -444,11 +451,11 @@ export function useRemoveChatResource(chatId?: string) {
mutationFn: removeChatResource,
onMutate: async ({ resourceType, resourceId }) => {
if (!chatId) return
- await queryClient.cancelQueries({ queryKey: taskKeys.detail(chatId) })
- const removed: TaskChatHistory['resources'] = []
- queryClient.setQueryData(taskKeys.detail(chatId), (prev) => {
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.detail(chatId) })
+ const removed: MothershipChatHistory['resources'] = []
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), (prev) => {
if (!prev) return prev
- const next: TaskChatHistory['resources'] = []
+ const next: MothershipChatHistory['resources'] = []
for (const r of prev.resources) {
if (r.type === resourceType && r.id === resourceId) removed.push(r)
else next.push(r)
@@ -459,26 +466,26 @@ export function useRemoveChatResource(chatId?: string) {
},
onError: (_err, _variables, context) => {
if (!chatId || !context?.removed.length) return
- queryClient.setQueryData(taskKeys.detail(chatId), (prev) =>
+ queryClient.setQueryData(mothershipChatKeys.detail(chatId), (prev) =>
prev ? { ...prev, resources: [...prev.resources, ...context.removed] } : prev
)
},
onSettled: () => {
if (chatId) {
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(chatId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(chatId) })
}
},
})
}
-async function markTaskRead(chatId: string): Promise {
+async function markChatRead(chatId: string): Promise {
await requestJson(updateMothershipChatContract, {
params: { chatId },
body: { isUnread: false },
})
}
-async function markTaskUnread(chatId: string): Promise {
+async function markChatUnread(chatId: string): Promise {
await requestJson(updateMothershipChatContract, {
params: { chatId },
body: { isUnread: true },
@@ -491,81 +498,87 @@ function applyUnreadFlag(
chatId: string,
isUnread: boolean
): void {
- const current = queryClient.getQueryData(taskKeys.list(workspaceId))
+ const current = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
if (!current) return
- queryClient.setQueryData(
- taskKeys.list(workspaceId),
- current.map((task) => (task.id === chatId ? { ...task, isUnread } : task))
+ queryClient.setQueryData(
+ mothershipChatKeys.list(workspaceId),
+ current.map((chat) => (chat.id === chatId ? { ...chat, isUnread } : chat))
)
}
/**
- * Marks a task as read with optimistic update.
+ * Marks a chat as read with optimistic update.
*
* The server only updates `lastSeenAt`, never `updatedAt`, so we deliberately
* do not invalidate the list cache — that would trigger a refetch that can
* reorder the sidebar if any unrelated server-side update landed in between.
*
* If there is no cached list yet (initial fetch still in flight, e.g. on
- * task-page refresh), we skip cancellation entirely so the in-flight fetch
+ * chat-page refresh), we skip cancellation entirely so the in-flight fetch
* can resolve normally — otherwise it would be orphaned and never refetched.
* `onSuccess` then reconciles whichever state the fetch produced.
*/
-export function useMarkTaskRead(workspaceId?: string) {
+export function useMarkMothershipChatRead(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
- mutationFn: markTaskRead,
+ mutationFn: markChatRead,
onMutate: async (chatId) => {
- const previousTasks = queryClient.getQueryData(taskKeys.list(workspaceId))
- if (!previousTasks) return { previousTasks }
+ const previousChats = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
+ if (!previousChats) return { previousChats }
- await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
applyUnreadFlag(queryClient, workspaceId, chatId, false)
- return { previousTasks }
+ return { previousChats }
},
onSuccess: (_data, chatId) => {
applyUnreadFlag(queryClient, workspaceId, chatId, false)
},
onError: (_err, _variables, context) => {
- if (context?.previousTasks) {
- queryClient.setQueryData(taskKeys.list(workspaceId), context.previousTasks)
+ if (context?.previousChats) {
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), context.previousChats)
}
},
})
}
/**
- * Marks a task as unread with optimistic update.
+ * Marks a chat as unread with optimistic update.
*
- * Same rationale as `useMarkTaskRead` — no list invalidation, since the server
+ * Same rationale as `useMarkMothershipChatRead` — no list invalidation, since the server
* only flips `lastSeenAt` and the optimistic update fully reflects the change.
*/
-export function useMarkTaskUnread(workspaceId?: string) {
+export function useMarkMothershipChatUnread(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
- mutationFn: markTaskUnread,
+ mutationFn: markChatUnread,
onMutate: async (chatId) => {
- const previousTasks = queryClient.getQueryData(taskKeys.list(workspaceId))
- if (!previousTasks) return { previousTasks }
+ const previousChats = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
+ if (!previousChats) return { previousChats }
- await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
applyUnreadFlag(queryClient, workspaceId, chatId, true)
- return { previousTasks }
+ return { previousChats }
},
onSuccess: (_data, chatId) => {
applyUnreadFlag(queryClient, workspaceId, chatId, true)
},
onError: (_err, _variables, context) => {
- if (context?.previousTasks) {
- queryClient.setQueryData(taskKeys.list(workspaceId), context.previousTasks)
+ if (context?.previousChats) {
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), context.previousChats)
}
},
})
}
-async function setTaskPinned({
+async function setChatPinned({
chatId,
pinned,
}: {
@@ -579,39 +592,41 @@ async function setTaskPinned({
}
/**
- * Pins or unpins a task with optimistic update. Pinned tasks are sorted to
+ * Pins or unpins a chat with optimistic update. Pinned chats are sorted to
* the top of the list by the server; the optimistic reducer preserves that
- * ordering by partitioning pinned and unpinned tasks while keeping each
+ * ordering by partitioning pinned and unpinned chats while keeping each
* partition in its existing order (server returns desc(updatedAt) within).
*/
-export function useSetTaskPinned(workspaceId?: string) {
+export function useSetMothershipChatPinned(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
- mutationFn: setTaskPinned,
+ mutationFn: setChatPinned,
onMutate: async ({ chatId, pinned }) => {
- await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
- const previousTasks = queryClient.getQueryData(taskKeys.list(workspaceId))
- if (!previousTasks) return { previousTasks: undefined }
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
+ const previousChats = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
+ if (!previousChats) return { previousChats: undefined }
- const updated = previousTasks.map((task) =>
- task.id === chatId ? { ...task, isPinned: pinned } : task
+ const updated = previousChats.map((chat) =>
+ chat.id === chatId ? { ...chat, isPinned: pinned } : chat
)
- const pinnedTasks = updated.filter((task) => task.isPinned)
- const unpinnedTasks = updated.filter((task) => !task.isPinned)
- queryClient.setQueryData(taskKeys.list(workspaceId), [
- ...pinnedTasks,
- ...unpinnedTasks,
+ const pinnedChats = updated.filter((chat) => chat.isPinned)
+ const unpinnedChats = updated.filter((chat) => !chat.isPinned)
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), [
+ ...pinnedChats,
+ ...unpinnedChats,
])
- return { previousTasks }
+ return { previousChats }
},
onError: (_err, _variables, context) => {
- if (context?.previousTasks) {
- queryClient.setQueryData(taskKeys.list(workspaceId), context.previousTasks)
+ if (context?.previousChats) {
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), context.previousChats)
}
},
onSettled: () => {
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
},
})
}
@@ -621,7 +636,7 @@ async function createChat(workspaceId: string): Promise<{ id: string }> {
return { id }
}
-export function useCreateTask(workspaceId?: string) {
+export function useCreateMothershipChat(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: () => {
@@ -630,8 +645,10 @@ export function useCreateTask(workspaceId?: string) {
},
onSuccess: (data) => {
if (!workspaceId) return
- const existing = queryClient.getQueryData(taskKeys.list(workspaceId)) ?? []
- const newTask: TaskMetadata = {
+ const existing =
+ queryClient.getQueryData(mothershipChatKeys.list(workspaceId)) ??
+ []
+ const newChat: MothershipChatMetadata = {
id: data.id,
name: 'New chat',
updatedAt: new Date(),
@@ -639,17 +656,17 @@ export function useCreateTask(workspaceId?: string) {
isUnread: false,
isPinned: false,
}
- const pinnedCount = existing.findIndex((task) => !task.isPinned)
+ const pinnedCount = existing.findIndex((chat) => !chat.isPinned)
const insertAt = pinnedCount === -1 ? existing.length : pinnedCount
- queryClient.setQueryData(taskKeys.list(workspaceId), [
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), [
...existing.slice(0, insertAt),
- newTask,
+ newChat,
...existing.slice(insertAt),
])
},
onSettled: () => {
if (!workspaceId) return
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
},
})
}
@@ -665,18 +682,20 @@ async function forkChat(params: {
return { id: data.id }
}
-export function useForkTask(workspaceId?: string) {
+export function useForkMothershipChat(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: forkChat,
onSuccess: async (data, variables) => {
if (!workspaceId) return
- await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
- const existing = queryClient.getQueryData(taskKeys.list(workspaceId))
+ await queryClient.cancelQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
+ const existing = queryClient.getQueryData(
+ mothershipChatKeys.list(workspaceId)
+ )
if (existing) {
- const sourceTask = existing.find((t) => t.id === variables.chatId)
- const baseName = (sourceTask?.name ?? 'New chat').replace(/^Fork \| /, '')
- const optimisticTask: TaskMetadata = {
+ const sourceChat = existing.find((t) => t.id === variables.chatId)
+ const baseName = (sourceChat?.name ?? 'New chat').replace(/^Fork \| /, '')
+ const optimisticChat: MothershipChatMetadata = {
id: data.id,
name: `Fork | ${baseName}`,
updatedAt: new Date(),
@@ -684,18 +703,18 @@ export function useForkTask(workspaceId?: string) {
isUnread: false,
isPinned: false,
}
- const pinnedCount = existing.findIndex((task) => !task.isPinned)
+ const pinnedCount = existing.findIndex((chat) => !chat.isPinned)
const insertAt = pinnedCount === -1 ? existing.length : pinnedCount
- queryClient.setQueryData(taskKeys.list(workspaceId), [
+ queryClient.setQueryData(mothershipChatKeys.list(workspaceId), [
...existing.slice(0, insertAt),
- optimisticTask,
+ optimisticChat,
...existing.slice(insertAt),
])
}
},
onSettled: () => {
if (!workspaceId) return
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
},
})
}
diff --git a/apps/sim/hooks/use-task-events.test.ts b/apps/sim/hooks/use-mothership-chat-events.test.ts
similarity index 83%
rename from apps/sim/hooks/use-task-events.test.ts
rename to apps/sim/hooks/use-mothership-chat-events.test.ts
index e81edbca6dd..de511c0bfa8 100644
--- a/apps/sim/hooks/use-task-events.test.ts
+++ b/apps/sim/hooks/use-mothership-chat-events.test.ts
@@ -4,10 +4,10 @@
import type { QueryClient } from '@tanstack/react-query'
import { beforeEach, describe, expect, it, vi } from 'vitest'
-import { taskKeys } from '@/hooks/queries/tasks'
-import { handleTaskStatusEvent } from '@/hooks/use-task-events'
+import { mothershipChatKeys } from '@/hooks/queries/mothership-chats'
+import { handleMothershipChatStatusEvent } from '@/hooks/use-mothership-chat-events'
-describe('handleTaskStatusEvent', () => {
+describe('handleMothershipChatStatusEvent', () => {
const queryClient = {
getQueryData: vi.fn(),
invalidateQueries: vi.fn().mockResolvedValue(undefined),
@@ -20,7 +20,7 @@ describe('handleTaskStatusEvent', () => {
})
it('invalidates the task list and detail for completed task events', () => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -32,10 +32,10 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -49,7 +49,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -61,7 +61,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -75,7 +75,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -88,7 +88,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -102,7 +102,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -115,7 +115,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -129,7 +129,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -142,10 +142,10 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -159,7 +159,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -172,10 +172,10 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -189,7 +189,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -202,16 +202,16 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
it('invalidates the task list and detail for metadata-changing task events', () => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -223,16 +223,16 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
it('invalidates the task list and removes detail cache for deleted task events', () => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -244,16 +244,16 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).toHaveBeenCalledTimes(1)
expect(queryClient.removeQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
})
it('invalidates the task list and detail for started task events', () => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -265,10 +265,10 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -282,7 +282,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -294,7 +294,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -308,7 +308,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -321,7 +321,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -335,7 +335,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -348,7 +348,7 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
@@ -362,7 +362,7 @@ describe('handleTaskStatusEvent', () => {
resources: [],
})
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -375,16 +375,16 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.detail('chat-1'),
+ queryKey: mothershipChatKeys.detail('chat-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
it('keeps list invalidation only for unknown task event types', () => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
'ws-1',
JSON.stringify({
@@ -396,13 +396,13 @@ describe('handleTaskStatusEvent', () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(1)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
- queryKey: taskKeys.list('ws-1'),
+ queryKey: mothershipChatKeys.list('ws-1'),
})
expect(queryClient.removeQueries).not.toHaveBeenCalled()
})
it('does not invalidate when task event payload is invalid', () => {
- handleTaskStatusEvent(queryClient, 'ws-1', '{')
+ handleMothershipChatStatusEvent(queryClient, 'ws-1', '{')
expect(queryClient.invalidateQueries).not.toHaveBeenCalled()
expect(queryClient.removeQueries).not.toHaveBeenCalled()
diff --git a/apps/sim/hooks/use-task-events.ts b/apps/sim/hooks/use-mothership-chat-events.ts
similarity index 66%
rename from apps/sim/hooks/use-task-events.ts
rename to apps/sim/hooks/use-mothership-chat-events.ts
index b9a5216dad4..ad8f0d9d3fd 100644
--- a/apps/sim/hooks/use-task-events.ts
+++ b/apps/sim/hooks/use-mothership-chat-events.ts
@@ -3,31 +3,31 @@ import { createLogger } from '@sim/logger'
import type { QueryClient } from '@tanstack/react-query'
import { useQueryClient } from '@tanstack/react-query'
import { getLiveAssistantMessageId } from '@/lib/copilot/chat/effective-transcript'
-import { type TaskChatHistory, taskKeys } from '@/hooks/queries/tasks'
+import { type MothershipChatHistory, mothershipChatKeys } from '@/hooks/queries/mothership-chats'
-const logger = createLogger('TaskEvents')
+const logger = createLogger('MothershipChatEvents')
-const TASK_STATUS_TYPES = ['started', 'completed', 'created', 'deleted', 'renamed'] as const
-type TaskStatusEventType = (typeof TASK_STATUS_TYPES)[number]
-const TASK_STATUS_TYPE_SET = new Set(TASK_STATUS_TYPES)
+const CHAT_STATUS_TYPES = ['started', 'completed', 'created', 'deleted', 'renamed'] as const
+type ChatStatusEventType = (typeof CHAT_STATUS_TYPES)[number]
+const CHAT_STATUS_TYPE_SET = new Set(CHAT_STATUS_TYPES)
-interface TaskStatusEventPayload {
+interface ChatStatusEventPayload {
chatId?: string
- type?: TaskStatusEventType
+ type?: ChatStatusEventType
streamId?: string
}
-const DETAIL_INVALIDATING_TASK_STATUS_TYPES = new Set([
+const DETAIL_INVALIDATING_CHAT_STATUS_TYPES = new Set([
'started',
'completed',
'renamed',
])
-function isTaskStatusEventType(value: unknown): value is TaskStatusEventType {
- return typeof value === 'string' && TASK_STATUS_TYPE_SET.has(value)
+function isChatStatusEventType(value: unknown): value is ChatStatusEventType {
+ return typeof value === 'string' && CHAT_STATUS_TYPE_SET.has(value)
}
-function isLocalOptimisticActiveStream(current: TaskChatHistory | undefined) {
+function isLocalOptimisticActiveStream(current: MothershipChatHistory | undefined) {
if (!current?.activeStreamId) return false
const liveAssistantId = getLiveAssistantMessageId(current.activeStreamId)
return current.messages.some((message) => message.id === liveAssistantId)
@@ -39,7 +39,7 @@ function isLocalOptimisticActiveStream(current: TaskChatHistory | undefined) {
* If either stream is absent from the transcript, callers should refetch
* instead of inferring order from incomplete cache state.
*/
-function hasNewerKnownActiveStream(current: TaskChatHistory | undefined, streamId: string) {
+function hasNewerKnownActiveStream(current: MothershipChatHistory | undefined, streamId: string) {
if (!current?.activeStreamId || current.activeStreamId === streamId) return false
const activeIndex = current.messages.findIndex((message) => message.id === current.activeStreamId)
@@ -50,8 +50,8 @@ function hasNewerKnownActiveStream(current: TaskChatHistory | undefined, streamI
}
function shouldSkipDetailInvalidationForStreamEvent(
- current: TaskChatHistory | undefined,
- payload: TaskStatusEventPayload
+ current: MothershipChatHistory | undefined,
+ payload: ChatStatusEventPayload
) {
if (payload.type !== 'started' && payload.type !== 'completed') return false
if (!current?.activeStreamId) return false
@@ -66,7 +66,7 @@ function shouldSkipDetailInvalidationForStreamEvent(
)
}
-function parseTaskStatusEventPayload(data: unknown): TaskStatusEventPayload | null {
+function parseChatStatusEventPayload(data: unknown): ChatStatusEventPayload | null {
let parsed = data
if (typeof parsed === 'string') {
@@ -85,43 +85,46 @@ function parseTaskStatusEventPayload(data: unknown): TaskStatusEventPayload | nu
return {
...(typeof record.chatId === 'string' ? { chatId: record.chatId } : {}),
- ...(isTaskStatusEventType(record.type) ? { type: record.type } : {}),
+ ...(isChatStatusEventType(record.type) ? { type: record.type } : {}),
...(typeof record.streamId === 'string' ? { streamId: record.streamId } : {}),
}
}
-export function handleTaskStatusEvent(
+export function handleMothershipChatStatusEvent(
queryClient: Pick,
workspaceId: string,
data: unknown
): void {
- const payload = parseTaskStatusEventPayload(data)
+ const payload = parseChatStatusEventPayload(data)
if (!payload) {
logger.warn('Received invalid task_status payload')
return
}
- queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.list(workspaceId) })
if (!payload.chatId) return
if (payload.type === 'deleted') {
- queryClient.removeQueries({ queryKey: taskKeys.detail(payload.chatId) })
+ queryClient.removeQueries({ queryKey: mothershipChatKeys.detail(payload.chatId) })
return
}
if (payload.type === 'started' || payload.type === 'completed') {
- const current = queryClient.getQueryData(taskKeys.detail(payload.chatId))
+ const current = queryClient.getQueryData(
+ mothershipChatKeys.detail(payload.chatId)
+ )
if (shouldSkipDetailInvalidationForStreamEvent(current, payload)) {
return
}
}
- if (payload.type && DETAIL_INVALIDATING_TASK_STATUS_TYPES.has(payload.type)) {
- queryClient.invalidateQueries({ queryKey: taskKeys.detail(payload.chatId) })
+ if (payload.type && DETAIL_INVALIDATING_CHAT_STATUS_TYPES.has(payload.type)) {
+ queryClient.invalidateQueries({ queryKey: mothershipChatKeys.detail(payload.chatId) })
}
}
/**
- * Subscribes to task status SSE events and invalidates task caches on changes.
+ * Subscribes to chat status SSE events and invalidates chat caches on changes.
+ * The SSE event name remains `task_status` for wire compatibility.
*/
-export function useTaskEvents(workspaceId: string | undefined) {
+export function useMothershipChatEvents(workspaceId: string | undefined) {
const queryClient = useQueryClient()
useEffect(() => {
@@ -132,7 +135,7 @@ export function useTaskEvents(workspaceId: string | undefined) {
)
eventSource.addEventListener('task_status', (event) => {
- handleTaskStatusEvent(
+ handleMothershipChatStatusEvent(
queryClient,
workspaceId,
event instanceof MessageEvent ? event.data : undefined
diff --git a/apps/sim/lib/api/contracts/mothership-tasks.ts b/apps/sim/lib/api/contracts/mothership-chats.ts
similarity index 98%
rename from apps/sim/lib/api/contracts/mothership-tasks.ts
rename to apps/sim/lib/api/contracts/mothership-chats.ts
index 6e29964cdbe..2c65c824c2c 100644
--- a/apps/sim/lib/api/contracts/mothership-tasks.ts
+++ b/apps/sim/lib/api/contracts/mothership-chats.ts
@@ -201,7 +201,7 @@ export const removeMothershipChatResourceContract = defineRouteContract({
},
})
-export const mothershipTaskSchema = z.object({
+export const mothershipChatSchema = z.object({
id: z.string(),
title: z.string().nullable(),
updatedAt: dateStringSchema,
@@ -218,7 +218,7 @@ export const listMothershipChatsContract = defineRouteContract({
mode: 'json',
schema: z.object({
success: z.literal(true),
- data: z.array(mothershipTaskSchema),
+ data: z.array(mothershipChatSchema),
}),
},
})
@@ -343,4 +343,4 @@ export const getMothershipChatContract = defineRouteContract({
},
})
-export type MothershipTask = z.infer
+export type MothershipChat = z.infer
diff --git a/apps/sim/lib/copilot/chat-status.ts b/apps/sim/lib/copilot/chat-status.ts
new file mode 100644
index 00000000000..221eb20ac9f
--- /dev/null
+++ b/apps/sim/lib/copilot/chat-status.ts
@@ -0,0 +1,42 @@
+/**
+ * Chat Status Pub/Sub Adapter
+ *
+ * Broadcasts chat status events across processes using Redis Pub/Sub.
+ * Gracefully falls back to process-local EventEmitter when Redis is unavailable.
+ *
+ * The Redis channel and SSE label retain the legacy `task:status_changed`
+ * identifier so live status updates keep flowing across pods during a rolling
+ * deploy (old and new pods must publish/subscribe on the same channel).
+ */
+
+import { createPubSubChannel, type PubSubChannel } from '@/lib/events/pubsub'
+
+interface ChatStatusEvent {
+ workspaceId: string
+ chatId: string
+ type: 'started' | 'completed' | 'created' | 'deleted' | 'renamed'
+ streamId?: string
+}
+
+type ChatPubSubGlobal = typeof globalThis & {
+ _chatStatusChannel?: PubSubChannel | null
+}
+
+const g = globalThis as ChatPubSubGlobal
+
+if (!('_chatStatusChannel' in g)) {
+ g._chatStatusChannel =
+ typeof window !== 'undefined'
+ ? null
+ : createPubSubChannel({ channel: 'task:status_changed', label: 'task' })
+}
+
+const channel = g._chatStatusChannel
+
+export const chatPubSub = channel
+ ? {
+ publishStatusChanged: (event: ChatStatusEvent) => channel.publish(event),
+ onStatusChanged: (handler: (event: ChatStatusEvent) => void) => channel.subscribe(handler),
+ dispose: () => channel.dispose(),
+ }
+ : null
diff --git a/apps/sim/lib/copilot/chat/post.test.ts b/apps/sim/lib/copilot/chat/post.test.ts
index c2271ece05b..e9f4ba41e30 100644
--- a/apps/sim/lib/copilot/chat/post.test.ts
+++ b/apps/sim/lib/copilot/chat/post.test.ts
@@ -92,8 +92,8 @@ vi.mock('@/lib/copilot/chat/messages-store', () => ({
appendCopilotChatMessages,
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: {
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: {
publishStatusChanged: mockPublishStatusChanged,
},
}))
diff --git a/apps/sim/lib/copilot/chat/post.ts b/apps/sim/lib/copilot/chat/post.ts
index 9cf5e6b2d15..f3b8e4a9259 100644
--- a/apps/sim/lib/copilot/chat/post.ts
+++ b/apps/sim/lib/copilot/chat/post.ts
@@ -23,6 +23,7 @@ import {
} from '@/lib/copilot/chat/process-contents'
import { finalizeAssistantTurn } from '@/lib/copilot/chat/terminal-state'
import { generateWorkspaceContext } from '@/lib/copilot/chat/workspace-context'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/constants'
import {
CopilotChatFinalizeOutcome,
@@ -41,7 +42,6 @@ import {
} from '@/lib/copilot/request/session'
import type { ExecutionContext, OrchestratorResult } from '@/lib/copilot/request/types'
import { persistChatResources } from '@/lib/copilot/resources/persistence'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { prepareExecutionContext } from '@/lib/copilot/tools/handlers/context'
import { getEffectiveDecryptedEnv } from '@/lib/environment/utils'
import { getWorkflowById, resolveWorkflowIdForUser } from '@/lib/workflows/utils'
@@ -352,7 +352,7 @@ async function persistUserMessage(params: {
)
if (notifyWorkspaceStatus && updated && workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId,
chatId,
type: 'started',
@@ -451,7 +451,7 @@ function buildOnComplete(params: {
finalization.outcome === CopilotChatFinalizeOutcome.AssistantAlreadyPersisted
if (notifyWorkspaceStatus && workspaceId && shouldPublishCompletion) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId,
chatId,
type: 'completed',
@@ -470,7 +470,7 @@ function buildOnComplete(params: {
})
if (notifyWorkspaceStatus && workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId,
chatId,
type: 'completed',
@@ -502,7 +502,7 @@ function buildOnError(params: {
await finalizeAssistantTurn({ chatId, userMessageId })
if (notifyWorkspaceStatus && workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId,
chatId,
type: 'completed',
diff --git a/apps/sim/lib/copilot/request/lifecycle/start.test.ts b/apps/sim/lib/copilot/request/lifecycle/start.test.ts
index 917c56ba339..e5307fb703d 100644
--- a/apps/sim/lib/copilot/request/lifecycle/start.test.ts
+++ b/apps/sim/lib/copilot/request/lifecycle/start.test.ts
@@ -107,8 +107,8 @@ vi.mock('@sim/db', () => ({
},
}))
-vi.mock('@/lib/copilot/tasks', () => ({
- taskPubSub: null,
+vi.mock('@/lib/copilot/chat-status', () => ({
+ chatPubSub: null,
}))
import { createSSEStream } from './start'
diff --git a/apps/sim/lib/copilot/request/lifecycle/start.ts b/apps/sim/lib/copilot/request/lifecycle/start.ts
index 595efe15f33..69682f0019f 100644
--- a/apps/sim/lib/copilot/request/lifecycle/start.ts
+++ b/apps/sim/lib/copilot/request/lifecycle/start.ts
@@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { eq } from 'drizzle-orm'
import { createRunSegment } from '@/lib/copilot/async-runs/repository'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import {
MothershipStreamV1EventType,
MothershipStreamV1SessionKind,
@@ -40,7 +41,6 @@ import {
import { SSE_RESPONSE_HEADERS } from '@/lib/copilot/request/session/sse'
import { reportTrace, TraceCollector } from '@/lib/copilot/request/trace'
import { getMothershipBaseURL, getMothershipSourceEnvHeaders } from '@/lib/copilot/server/agent-url'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { env } from '@/lib/core/config/env'
export { SSE_RESPONSE_HEADERS }
@@ -476,7 +476,7 @@ function fireTitleGeneration(params: {
payload: { kind: MothershipStreamV1SessionKind.title, title },
})
if (workspaceId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId,
chatId,
type: 'renamed',
diff --git a/apps/sim/lib/copilot/tasks.ts b/apps/sim/lib/copilot/tasks.ts
deleted file mode 100644
index 252b82a61ae..00000000000
--- a/apps/sim/lib/copilot/tasks.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Task Status Pub/Sub Adapter
- *
- * Broadcasts task status events across processes using Redis Pub/Sub.
- * Gracefully falls back to process-local EventEmitter when Redis is unavailable.
- *
- * Channel: `task:status_changed`
- */
-
-import { createPubSubChannel, type PubSubChannel } from '@/lib/events/pubsub'
-
-interface TaskStatusEvent {
- workspaceId: string
- chatId: string
- type: 'started' | 'completed' | 'created' | 'deleted' | 'renamed'
- streamId?: string
-}
-
-type TaskPubSubGlobal = typeof globalThis & {
- _taskStatusChannel?: PubSubChannel | null
-}
-
-const g = globalThis as TaskPubSubGlobal
-
-if (!('_taskStatusChannel' in g)) {
- g._taskStatusChannel =
- typeof window !== 'undefined'
- ? null
- : createPubSubChannel({ channel: 'task:status_changed', label: 'task' })
-}
-
-const channel = g._taskStatusChannel
-
-export const taskPubSub = channel
- ? {
- publishStatusChanged: (event: TaskStatusEvent) => channel.publish(event),
- onStatusChanged: (handler: (event: TaskStatusEvent) => void) => channel.subscribe(handler),
- dispose: () => channel.dispose(),
- }
- : null
diff --git a/apps/sim/lib/copilot/tools/handlers/oauth.ts b/apps/sim/lib/copilot/tools/handlers/oauth.ts
index 15ae0d04554..3919b0ba557 100644
--- a/apps/sim/lib/copilot/tools/handlers/oauth.ts
+++ b/apps/sim/lib/copilot/tools/handlers/oauth.ts
@@ -109,7 +109,7 @@ async function generateOAuthLink(
workflowId && workspaceId
? `${baseUrl}/workspace/${workspaceId}/w/${workflowId}`
: chatId && workspaceId
- ? `${baseUrl}/workspace/${workspaceId}/task/${chatId}`
+ ? `${baseUrl}/workspace/${workspaceId}/chat/${chatId}`
: `${baseUrl}/workspace/${workspaceId}`
if (providerId === 'trello') {
diff --git a/apps/sim/lib/mothership/inbox/executor.ts b/apps/sim/lib/mothership/inbox/executor.ts
index 67ac5296632..871dfa033a7 100644
--- a/apps/sim/lib/mothership/inbox/executor.ts
+++ b/apps/sim/lib/mothership/inbox/executor.ts
@@ -11,10 +11,10 @@ import {
buildPersistedUserMessage,
} from '@/lib/copilot/chat/persisted-message'
import { generateWorkspaceContext } from '@/lib/copilot/chat/workspace-context'
+import { chatPubSub } from '@/lib/copilot/chat-status'
import { runHeadlessCopilotLifecycle } from '@/lib/copilot/request/lifecycle/headless'
import { requestChatTitle } from '@/lib/copilot/request/lifecycle/start'
import type { OrchestratorResult } from '@/lib/copilot/request/types'
-import { taskPubSub } from '@/lib/copilot/tasks'
import { isHosted } from '@/lib/core/config/feature-flags'
import * as agentmail from '@/lib/mothership/inbox/agentmail-client'
import { formatEmailAsMessage } from '@/lib/mothership/inbox/format'
@@ -117,7 +117,7 @@ export async function executeInboxTask(taskId: string): Promise {
.then(async (title) => {
if (title && chatId) {
await db.update(copilotChats).set({ title }).where(eq(copilotChats.id, chatId))
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: ws.id,
chatId,
type: 'renamed',
@@ -128,7 +128,7 @@ export async function executeInboxTask(taskId: string): Promise {
logger.warn('Failed to generate chat title', { chatId, err })
})
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: ws.id,
chatId,
type: 'created',
@@ -138,7 +138,7 @@ export async function executeInboxTask(taskId: string): Promise {
const userMessageId = generateId()
if (chatId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: ws.id,
chatId,
type: 'started',
@@ -260,7 +260,7 @@ export async function executeInboxTask(taskId: string): Promise {
.where(eq(mothershipInboxTask.id, taskId))
if (chatId) {
- taskPubSub?.publishStatusChanged({
+ chatPubSub?.publishStatusChanged({
workspaceId: ws.id,
chatId,
type: 'completed',
diff --git a/apps/sim/lib/mothership/inbox/response.ts b/apps/sim/lib/mothership/inbox/response.ts
index f289ec705ce..1a32f5d6355 100644
--- a/apps/sim/lib/mothership/inbox/response.ts
+++ b/apps/sim/lib/mothership/inbox/response.ts
@@ -32,7 +32,7 @@ export async function sendInboxResponse(
}
const chatUrl = inboxTask.chatId
- ? `${getBaseUrl()}/workspace/${ctx.workspaceId}/task/${inboxTask.chatId}`
+ ? `${getBaseUrl()}/workspace/${ctx.workspaceId}/chat/${inboxTask.chatId}`
: `${getBaseUrl()}/workspace/${ctx.workspaceId}/home`
const text = result.success
diff --git a/apps/sim/next.config.ts b/apps/sim/next.config.ts
index d5b81c9da5c..7addcc8b697 100644
--- a/apps/sim/next.config.ts
+++ b/apps/sim/next.config.ts
@@ -315,6 +315,15 @@ const nextConfig: NextConfig = {
}
)
+ // Legacy chat URL support: the workspace chat route was renamed from
+ // `/workspace/:workspaceId/task/:chatId` to `/workspace/:workspaceId/chat/:chatId`.
+ // Preserve existing bookmarks and deeplinks.
+ redirects.push({
+ source: '/workspace/:workspaceId/task/:chatId',
+ destination: '/workspace/:workspaceId/chat/:chatId',
+ permanent: true,
+ })
+
return redirects
},
async rewrites() {
diff --git a/apps/sim/stores/folders/store.ts b/apps/sim/stores/folders/store.ts
index b3ac643cdee..5f4115418fb 100644
--- a/apps/sim/stores/folders/store.ts
+++ b/apps/sim/stores/folders/store.ts
@@ -9,8 +9,8 @@ interface FolderState {
selectedWorkflows: Set
selectedFolders: Set
lastSelectedFolderId: string | null
- selectedTasks: Set
- lastSelectedTaskId: string | null
+ selectedChats: Set
+ lastSelectedChatId: string | null
toggleExpanded: (folderId: string) => void
setExpanded: (folderId: string, expanded: boolean) => void
@@ -33,15 +33,15 @@ interface FolderState {
selectFolderRange: (folderIds: string[], fromId: string, toId: string) => void
isFolderSelected: (folderId: string) => boolean
- // Task selection actions
- selectTaskOnly: (taskId: string) => void
- toggleTaskSelection: (taskId: string) => void
- selectTaskRange: (taskIds: string[], fromId: string, toId: string) => void
- clearTaskSelection: () => void
- isTaskSelected: (taskId: string) => boolean
+ // Chat selection actions
+ selectChatOnly: (chatId: string) => void
+ toggleChatSelection: (chatId: string) => void
+ selectChatRange: (chatIds: string[], fromId: string, toId: string) => void
+ clearChatSelection: () => void
+ isChatSelected: (chatId: string) => boolean
// Unified selection helpers
- getFullSelection: () => { workflowIds: string[]; folderIds: string[]; taskIds: string[] }
+ getFullSelection: () => { workflowIds: string[]; folderIds: string[]; chatIds: string[] }
hasAnySelection: () => boolean
isMixedSelection: () => boolean
clearAllSelection: () => void
@@ -54,8 +54,8 @@ export const useFolderStore = create()(
selectedWorkflows: new Set(),
selectedFolders: new Set(),
lastSelectedFolderId: null,
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
toggleExpanded: (folderId) =>
set((state) => {
@@ -104,9 +104,9 @@ export const useFolderStore = create()(
}
return {
selectedWorkflows: newSelected,
- ...(state.selectedTasks.size > 0 && {
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ ...(state.selectedChats.size > 0 && {
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
}),
}
}),
@@ -118,8 +118,8 @@ export const useFolderStore = create()(
selectedWorkflows: new Set([workflowId]),
selectedFolders: new Set(),
lastSelectedFolderId: null,
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
}),
selectRange: (workflowIds, fromId, toId) => {
@@ -175,9 +175,9 @@ export const useFolderStore = create()(
return {
selectedFolders: newSelected,
lastSelectedFolderId: newLastSelected,
- ...(state.selectedTasks.size > 0 && {
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ ...(state.selectedChats.size > 0 && {
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
}),
}
}),
@@ -188,8 +188,8 @@ export const useFolderStore = create()(
set({
selectedFolders: new Set([folderId]),
lastSelectedFolderId: folderId,
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
}),
selectFolderRange: (folderIds, fromId, toId) => {
@@ -206,11 +206,11 @@ export const useFolderStore = create()(
isFolderSelected: (folderId) => get().selectedFolders.has(folderId),
- // Task selection actions
- selectTaskOnly: (taskId) =>
+ // Chat selection actions
+ selectChatOnly: (chatId) =>
set((state) => ({
- selectedTasks: new Set([taskId]),
- lastSelectedTaskId: taskId,
+ selectedChats: new Set([chatId]),
+ lastSelectedChatId: chatId,
...(state.selectedWorkflows.size > 0 && { selectedWorkflows: new Set() }),
...(state.selectedFolders.size > 0 && {
selectedFolders: new Set(),
@@ -218,23 +218,23 @@ export const useFolderStore = create()(
}),
})),
- toggleTaskSelection: (taskId) =>
+ toggleChatSelection: (chatId) =>
set((state) => {
- const newSelected = new Set(state.selectedTasks)
+ const newSelected = new Set(state.selectedChats)
let newLastSelected: string | null
- if (newSelected.has(taskId)) {
- newSelected.delete(taskId)
+ if (newSelected.has(chatId)) {
+ newSelected.delete(chatId)
newLastSelected =
- state.lastSelectedTaskId === taskId
+ state.lastSelectedChatId === chatId
? (Array.from(newSelected)[0] ?? null)
- : state.lastSelectedTaskId
+ : state.lastSelectedChatId
} else {
- newSelected.add(taskId)
- newLastSelected = taskId
+ newSelected.add(chatId)
+ newLastSelected = chatId
}
return {
- selectedTasks: newSelected,
- lastSelectedTaskId: newLastSelected,
+ selectedChats: newSelected,
+ lastSelectedChatId: newLastSelected,
...(state.selectedWorkflows.size > 0 && { selectedWorkflows: new Set() }),
...(state.selectedFolders.size > 0 && {
selectedFolders: new Set(),
@@ -243,19 +243,19 @@ export const useFolderStore = create()(
}
}),
- selectTaskRange: (taskIds, fromId, toId) => {
- const fromIndex = taskIds.indexOf(fromId)
- const toIndex = taskIds.indexOf(toId)
+ selectChatRange: (chatIds, fromId, toId) => {
+ const fromIndex = chatIds.indexOf(fromId)
+ const toIndex = chatIds.indexOf(toId)
if (fromIndex === -1 || toIndex === -1) return
const [start, end] = fromIndex < toIndex ? [fromIndex, toIndex] : [toIndex, fromIndex]
- const rangeIds = taskIds.slice(start, end + 1)
+ const rangeIds = chatIds.slice(start, end + 1)
const state = get()
set({
- selectedTasks: new Set(rangeIds),
- lastSelectedTaskId: fromId,
+ selectedChats: new Set(rangeIds),
+ lastSelectedChatId: fromId,
...(state.selectedWorkflows.size > 0 && { selectedWorkflows: new Set() }),
...(state.selectedFolders.size > 0 && {
selectedFolders: new Set(),
@@ -264,21 +264,21 @@ export const useFolderStore = create()(
})
},
- clearTaskSelection: () => set({ selectedTasks: new Set(), lastSelectedTaskId: null }),
+ clearChatSelection: () => set({ selectedChats: new Set(), lastSelectedChatId: null }),
- isTaskSelected: (taskId) => get().selectedTasks.has(taskId),
+ isChatSelected: (chatId) => get().selectedChats.has(chatId),
// Unified selection helpers
getFullSelection: () => ({
workflowIds: Array.from(get().selectedWorkflows),
folderIds: Array.from(get().selectedFolders),
- taskIds: Array.from(get().selectedTasks),
+ chatIds: Array.from(get().selectedChats),
}),
hasAnySelection: () =>
get().selectedWorkflows.size > 0 ||
get().selectedFolders.size > 0 ||
- get().selectedTasks.size > 0,
+ get().selectedChats.size > 0,
isMixedSelection: () => get().selectedWorkflows.size > 0 && get().selectedFolders.size > 0,
@@ -287,8 +287,8 @@ export const useFolderStore = create()(
selectedWorkflows: new Set(),
selectedFolders: new Set(),
lastSelectedFolderId: null,
- selectedTasks: new Set(),
- lastSelectedTaskId: null,
+ selectedChats: new Set(),
+ lastSelectedChatId: null,
}),
}),
{ name: 'folder-store' }