-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat(copilot): preserve mothership chat when opening workflow #4521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from 1 commit
528bcfb
6be5374
e8a692e
bc431ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ import { createLogger } from '@sim/logger' | |
| import { toError } from '@sim/utils/errors' | ||
| import { useQueryClient } from '@tanstack/react-query' | ||
| import { History, Plus } from 'lucide-react' | ||
| import { useParams, useRouter } from 'next/navigation' | ||
| import { useParams, useRouter, useSearchParams } from 'next/navigation' | ||
| import { usePostHog } from 'posthog-js/react' | ||
| import { useShallow } from 'zustand/react/shallow' | ||
| import { | ||
|
|
@@ -118,7 +118,9 @@ interface PanelProps { | |
| export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: PanelProps = {}) { | ||
| const router = useRouter() | ||
| const params = useParams() | ||
| const searchParams = useSearchParams() | ||
| const workspaceId = propWorkspaceId ?? (params.workspaceId as string) | ||
| const urlChatIdParam = searchParams?.get('chatId') ?? null | ||
|
|
||
| const posthog = usePostHog() | ||
| const posthogRef = useRef(posthog) | ||
|
|
@@ -256,6 +258,21 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel | |
| [copilotChatId, copilotChatList] | ||
| ) | ||
|
|
||
| /** | ||
| * A chat is read-only on this workflow page when it doesn't natively | ||
| * belong to the active workflow — currently the case for Mothership | ||
| * chats whose `workflowId` is null but whose `resources` reference this | ||
| * workflow. Continuing the conversation would route through the | ||
| * workflow copilot agent rather than the original Mothership context, | ||
| * so we surface the history without the input. | ||
| */ | ||
| const isCopilotChatReadOnly = useMemo(() => { | ||
| if (!copilotChatId || !activeWorkflowId) return false | ||
| const chat = copilotChatList.find((c) => c.id === copilotChatId) | ||
| if (!chat) return false | ||
| return chat.workflowId !== activeWorkflowId | ||
| }, [copilotChatId, copilotChatList, activeWorkflowId]) | ||
|
|
||
| const queryClient = useQueryClient() | ||
| const loadCopilotChats = useCallback(() => { | ||
| if (!activeWorkflowId) return | ||
|
|
@@ -264,7 +281,10 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel | |
|
|
||
| // Auto-select most recent on first list arrival per workflow, and drop a | ||
| // selection that no longer matches anything in the current list (e.g. the | ||
| // chat was deleted in another tab). | ||
| // chat was deleted in another tab). When a `?chatId=` param is present in | ||
| // the URL (e.g. after clicking "Open Workflow" from a Mothership task), | ||
| // prefer that chat over the most recent so the original conversation is | ||
| // shown right away. | ||
| const autoSelectAttemptedForRef = useRef<Set<string>>(new Set()) | ||
| useEffect(() => { | ||
| if (!activeWorkflowId) return | ||
|
|
@@ -278,8 +298,12 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel | |
| if (autoSelectAttemptedForRef.current.has(activeWorkflowId)) return | ||
| if (copilotChatList.length === 0) return | ||
| autoSelectAttemptedForRef.current.add(activeWorkflowId) | ||
| setCopilotChatId(copilotChatList[0].id) | ||
| }, [copilotChatList, copilotChatId, activeWorkflowId, setCopilotChatId]) | ||
| const preferred = | ||
| urlChatIdParam && copilotChatList.find((c) => c.id === urlChatIdParam) | ||
| ? urlChatIdParam | ||
| : copilotChatList[0].id | ||
| setCopilotChatId(preferred) | ||
| }, [copilotChatList, copilotChatId, activeWorkflowId, setCopilotChatId, urlChatIdParam]) | ||
|
Comment on lines
291
to
+317
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
|
|
||
| useEffect(() => { | ||
| posthogRef.current = posthog | ||
|
|
@@ -444,6 +468,17 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel | |
| setHasHydrated(true) | ||
| }, [setHasHydrated]) | ||
|
|
||
| /** | ||
| * If the workflow page was opened with `?chatId=`, surface the copilot | ||
| * tab so the linked conversation is visible without an extra click. | ||
| */ | ||
| const chatIdParamHandledRef = useRef(false) | ||
| useEffect(() => { | ||
| if (chatIdParamHandledRef.current || !urlChatIdParam) return | ||
| chatIdParamHandledRef.current = true | ||
| setActiveTab('copilot') | ||
| }, [urlChatIdParam, setActiveTab]) | ||
|
|
||
| useEffect(() => { | ||
| const handler = (e: Event) => { | ||
| const message = (e as CustomEvent<{ message: string }>).detail?.message | ||
|
|
@@ -890,6 +925,7 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel | |
| userId={session?.user?.id} | ||
| chatId={copilotResolvedChatId} | ||
| layout='copilot-view' | ||
| readOnly={isCopilotChatReadOnly} | ||
| /> | ||
| </div> | ||
| )} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isCopilotChatReadOnlyreturnsfalsewhenevercopilotChatIdis falsy, which is the case during the window between mount and the first auto-select (i.e., whilecopilotChatListis still loading). When the page opens with?chatId=, the copilot tab is activated immediately bychatIdParamHandledRef, butcopilotChatIdisn't set until the list arrives and the auto-select effect fires. During that gapMothershipChatrenders withreadOnly={false}, briefly showing the input footer before it disappears once the list loads andcopilotChatIdis set to the Mothership chat. This could be prevented by initialisingcopilotChatIddirectly fromurlChatIdParaminstead of waiting for the list, or by lettingisCopilotChatReadOnlyreturntrueoptimistically whenurlChatIdParamis set andcopilotChatIdis still unresolved.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moot now — the read-only behavior was removed in
6be5374in favor of full continuation (panel swapsuseChatto the Mothership branch when the selected chat istype: 'mothership'). There is noreadOnlyflicker because there is noreadOnlypath anymore.