From ee02d61996e201e54707e40cacdd1c4cd2048a04 Mon Sep 17 00:00:00 2001 From: Akhilesh Sudhakar <55130408+akhisud3195@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:37:44 +0400 Subject: [PATCH 01/10] Add options to hide panes (#244) * Add multiple pane views * Fix rendering issues * Fix copilot apply selection pane error and remove old debug logs * Make top bar more compact * Fix copilot generation from starting prompt * Fix panel resizing issues * Fix content preservation upon hiding panes * Added changes on top of pane_layouts branch to remove example agent from the workflow editor * Grey out options for changing pane layout during zero agents state * Add zero-agent state for playground and publish buttons * Fix pane sizes and bugs --------- Co-authored-by: tusharmagar --- apps/rowboat/app/lib/project_templates.ts | 17 +- .../app/projects/[projectId]/copilot/app.tsx | 78 +- .../copilot/components/actions.tsx | 4 +- .../copilot/components/messages.tsx | 16 +- .../[projectId]/copilot/use-copilot.tsx | 27 +- .../projects/[projectId]/playground/app.tsx | 87 ++- .../workflow/components/TopBar.tsx | 233 ++++-- .../[projectId]/workflow/workflow_editor.tsx | 709 +++++++++++++----- .../components/common/compose-box-copilot.tsx | 2 +- .../components/common/panel-common.tsx | 9 +- .../components/common/section-card.tsx | 7 +- apps/rowboat/components/ui/progress-bar.tsx | 23 +- .../application/lib/agents-runtime/agents.ts | 42 ++ .../lib/copilot/copilot_multi_agent.ts | 4 +- .../lib/copilot/current_workflow.ts | 2 +- .../lib/copilot/example_multi_agent_1.ts | 40 +- 16 files changed, 900 insertions(+), 400 deletions(-) diff --git a/apps/rowboat/app/lib/project_templates.ts b/apps/rowboat/app/lib/project_templates.ts index be28137a..3226d1c6 100644 --- a/apps/rowboat/app/lib/project_templates.ts +++ b/apps/rowboat/app/lib/project_templates.ts @@ -12,21 +12,8 @@ function buildTemplates(): { [key: string]: z.infer } { templates['default'] = { name: 'Blank Template', description: 'A blank canvas to build your agents.', - startAgent: "Example Agent", - agents: [ - { - name: "Example Agent", - type: "conversation", - description: "An example agent", - instructions: "## πŸ§‘β€ Role:\nYou are an helpful customer support assistant\n\n---\n## βš™οΈ Steps to Follow:\n1. Ask the user what they would like help with\n2. Ask the user for their email address and let them know someone will contact them soon.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Asking the user their issue\n- Getting their email\n\n❌ Out of Scope:\n- Questions unrelated to customer support\n- If a question is out of scope, politely inform the user and avoid providing an answer.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- ask user their issue\n\n❌ Don'ts:\n- don't ask user any other detail than email", - model: DEFAULT_MODEL, - toggleAble: true, - ragReturnType: "chunks", - ragK: 3, - controlType: "retain", - outputVisibility: "user_facing", - }, - ], + startAgent: "", + agents: [], prompts: [], tools: [ { diff --git a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx index 2a1eddff..ca765952 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx @@ -48,6 +48,8 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message isInitialState = false, dataSources, }, ref) { + + const [messages, setMessages] = useState[]>([]); const [discardContext, setDiscardContext] = useState(false); const [isLastInteracted, setIsLastInteracted] = useState(isInitialState); @@ -95,17 +97,7 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message onMessagesChange?.(messages); }, [messages, onMessagesChange]); - // Check for initial prompt in local storage and send it - useEffect(() => { - const prompt = localStorage.getItem(`project_prompt_${projectId}`); - if (prompt && messages.length === 0) { - localStorage.removeItem(`project_prompt_${projectId}`); - setMessages([{ - role: 'user', - content: prompt - }]); - } - }, [projectId, messages.length]); + // Removed localStorage auto-start. Initial prompts are sent by parent via ref. // Reset discardContext when chatContext changes useEffect(() => { @@ -134,15 +126,19 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message const currentStart = startRef.current; const currentCancel = cancelRef.current; - currentStart(messages, (finalResponse: string) => { - setMessages(prev => [ - ...prev, - { - role: 'assistant', - content: finalResponse - } - ]); - }); + if (currentStart) { + currentStart(messages, (finalResponse: string) => { + setMessages(prev => [ + ...prev, + { + role: 'assistant', + content: finalResponse + } + ]); + }); + } else { + // startRef not yet ready; no-op + } return () => currentCancel(); }, [messages, responseError]); @@ -269,7 +265,7 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message toolQuery={toolQuery} /> -
+
{responseError && (

{responseError}

@@ -322,8 +318,8 @@ export const Copilot = forwardRef<{ handleUserMessage: (message: string) => void dispatch: (action: WorkflowDispatch) => void; isInitialState?: boolean; dataSources?: z.infer[]; - activePanel: 'playground' | 'copilot'; - onTogglePanel: () => void; + activePanel?: 'playground' | 'copilot'; + onTogglePanel?: () => void; }>(({ projectId, workflow, @@ -334,6 +330,13 @@ export const Copilot = forwardRef<{ handleUserMessage: (message: string) => void activePanel, onTogglePanel, }, ref) => { + console.log('πŸŽͺ Copilot wrapper component mounted:', { + projectId, + isInitialState, + activePanel, + chatContextType: chatContext?.type + }); + const [copilotKey, setCopilotKey] = useState(0); const [showCopySuccess, setShowCopySuccess] = useState(false); const [messages, setMessages] = useState[]>([]); @@ -369,34 +372,7 @@ export const Copilot = forwardRef<{ handleUserMessage: (message: string) => void -
- - -
-
- } + title={
Skipper
} subtitle="Build your assistant" rightActions={
diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx index 62eb2237..55f950ae 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx @@ -67,14 +67,14 @@ export function Action({ switch (action.config_type) { case 'agent': dispatch({ - type: 'update_agent', + type: 'update_agent_no_select', name: action.name, agent: changes }); break; case 'tool': dispatch({ - type: 'update_tool', + type: 'update_tool_no_select', name: action.name, tool: changes }); diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx index 08db50cd..6ca63979 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx @@ -222,7 +222,8 @@ function AssistantMessage({ agent: { name: action.name, ...action.config_changes - } + }, + fromCopilot: true }); break; } @@ -236,7 +237,8 @@ function AssistantMessage({ tool: { name: action.name, ...action.config_changes - } + }, + fromCopilot: true }); break; } @@ -246,7 +248,8 @@ function AssistantMessage({ prompt: { name: action.name, ...action.config_changes - } + }, + fromCopilot: true }); break; case 'pipeline': @@ -255,7 +258,8 @@ function AssistantMessage({ pipeline: { name: action.name, ...action.config_changes - } + }, + fromCopilot: true }); break; } @@ -263,14 +267,14 @@ function AssistantMessage({ switch (action.config_type) { case 'agent': dispatch({ - type: 'update_agent', + type: 'update_agent_no_select', name: action.name, agent: action.config_changes }); break; case 'tool': dispatch({ - type: 'update_tool', + type: 'update_tool_no_select', name: action.name, tool: action.config_changes }); diff --git a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx index fb29ce10..dd11bfbf 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx @@ -38,6 +38,7 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop const [billingError, setBillingError] = useState(null); const cancelRef = useRef<() => void>(() => { }); const responseRef = useRef(''); + const inFlightRef = useRef(false); function clearError() { setError(null); @@ -51,7 +52,19 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop messages: z.infer[], onDone: (finalResponse: string) => void, ) => { - if (!messages.length || messages.at(-1)?.role !== 'user') return; + + + if (!messages.length || messages.at(-1)?.role !== 'user') { + + return; + } + + // Prevent duplicate/concurrent starts (e.g., StrictMode double effects or remounts) + if (inFlightRef.current) { + + return; + } + inFlightRef.current = true; setStreamingResponse(''); responseRef.current = ''; @@ -61,16 +74,23 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop setLoading(true); try { + // Wait 2 rAF frames to let layout stabilize (avoids StrictMode/remount race on initial load) + await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))); + const res = await getCopilotResponseStream(projectId, messages, workflow, context || null, dataSources); + // Check for billing error if ('billingError' in res) { + setLoading(false); setError(res.billingError); setBillingError(res.billingError); + inFlightRef.current = false; return; } + const eventSource = new EventSource(`/api/copilot-stream-response/${res.streamId}`); eventSource.onmessage = (event) => { @@ -102,24 +122,29 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop eventSource.close(); setLoading(false); onDone(responseRef.current); + inFlightRef.current = false; }); eventSource.onerror = () => { eventSource.close(); setError('Streaming failed'); setLoading(false); + inFlightRef.current = false; }; cancelRef.current = () => eventSource.close(); } catch (err) { + console.error('❌ Error in useCopilot.start:', err); setError('Failed to initiate stream'); setLoading(false); + inFlightRef.current = false; } }, [projectId, workflow, context, dataSources]); const cancel = useCallback(() => { cancelRef.current?.(); setLoading(false); + inFlightRef.current = false; }, []); return { diff --git a/apps/rowboat/app/projects/[projectId]/playground/app.tsx b/apps/rowboat/app/projects/[projectId]/playground/app.tsx index 29e230c2..8429f205 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/app.tsx @@ -28,8 +28,8 @@ export function App({ onPanelClick?: () => void; triggerCopilotChat?: (message: string) => void; isLiveWorkflow: boolean; - activePanel: 'playground' | 'copilot'; - onTogglePanel: () => void; + activePanel?: 'playground' | 'copilot'; + onTogglePanel?: () => void; onMessageSent?: () => void; }) { const [counter, setCounter] = useState(0); @@ -56,6 +56,8 @@ export function App({ } }, []); + const hasAgents = (workflow?.agents?.length || 0) > 0; + return ( <> -
- - -
+
+ + Chat
} - subtitle="Chat with your assistant" - rightActions={ + subtitle={hasAgents ? "Chat with your assistant" : "Create an agent to start chatting"} + rightActions={hasAgents ? (
- } + ) : ( + // Preserve header height when there are zero agents +
+ )} onClick={onPanelClick} >
- { getCopyContentRef.current = fn; }} - showDebugMessages={showDebugMessages} - triggerCopilotChat={triggerCopilotChat} - isLiveWorkflow={isLiveWorkflow} - onMessageSent={onMessageSent} - /> + {hasAgents ? ( + { getCopyContentRef.current = fn; }} + showDebugMessages={showDebugMessages} + triggerCopilotChat={triggerCopilotChat} + isLiveWorkflow={isLiveWorkflow} + onMessageSent={onMessageSent} + /> + ) : ( +
+
+
+ +
+
Create an agent to start chatting
+
Skipper can build agents for you!
+
+ +
+
+
+ )}
diff --git a/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx b/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx index 412b8a59..b17704c9 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx @@ -1,6 +1,6 @@ "use client"; import React from "react"; -import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Spinner, Tooltip, Input } from "@heroui/react"; +import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Spinner, Tooltip, Input, ButtonGroup } from "@heroui/react"; import { Button as CustomButton } from "@/components/ui/button"; import { RadioIcon, RedoIcon, UndoIcon, RocketIcon, PenLine, AlertTriangle, DownloadIcon, SettingsIcon, ChevronDownIcon, ZapIcon, Clock, Plug, MessageCircleIcon, ShareIcon } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; @@ -18,6 +18,7 @@ interface TopBarProps { canUndo: boolean; canRedo: boolean; activePanel: 'playground' | 'copilot'; + viewMode: "two_agents_chat" | "two_agents_skipper" | "two_chat_skipper" | "three_all"; hasAgentInstructionChanges: boolean; hasPlaygroundTested: boolean; hasPublished: boolean; @@ -29,6 +30,8 @@ interface TopBarProps { onChangeMode: (mode: 'draft' | 'live') => void; onRevertToLive: () => void; onTogglePanel: () => void; + onSetViewMode: (mode: "two_agents_chat" | "two_agents_skipper" | "two_chat_skipper" | "three_all") => void; + hasAgents?: boolean; onUseAssistantClick: () => void; onStartNewChatAndFocus: () => void; onStartBuildTour?: () => void; @@ -52,6 +55,7 @@ export function TopBar({ canUndo, canRedo, activePanel, + viewMode, hasAgentInstructionChanges, hasPlaygroundTested, hasPublished, @@ -63,6 +67,8 @@ export function TopBar({ onChangeMode, onRevertToLive, onTogglePanel, + onSetViewMode, + hasAgents = true, onUseAssistantClick, onStartNewChatAndFocus, onStartBuildTour, @@ -121,19 +127,24 @@ export function TopBar({ />
- {/* Show divider and CTA only in live view */} + {/* Show divider and mode indicator */} {isLive &&
} {isLive ? ( - ) : null} + ) : ( +
+ + Draft +
+ )}
{/* Progress Bar - Center */} @@ -163,31 +174,106 @@ export function TopBar({
} - {!isLive && <> + {!isLive &&
- + - + - } +
} + {/* View controls (hidden in live mode) */} + {!isLive && (
+ {(() => { + // Current visibility booleans + const showAgents = viewMode !== "two_chat_skipper"; + const showChat = viewMode !== "two_agents_skipper"; + const showSkipper = viewMode !== "two_agents_chat"; + + // Determine selected radio option + type RadioKey = 'show-all' | 'hide-agents' | 'hide-chat' | 'hide-skipper'; + let selectedKey: RadioKey = 'show-all'; + if (!(showAgents && showChat && showSkipper)) { + if (!showAgents) selectedKey = 'hide-agents'; + else if (!showChat) selectedKey = 'hide-chat'; + else if (!showSkipper) selectedKey = 'hide-skipper'; + } + + // Map radio selection to viewMode + const setByKey = (key: RadioKey) => { + switch (key) { + case 'show-all': + onSetViewMode('three_all'); + break; + case 'hide-agents': + onSetViewMode('two_chat_skipper'); + break; + case 'hide-chat': + onSetViewMode('two_agents_skipper'); + break; + case 'hide-skipper': + onSetViewMode('two_agents_chat'); + break; + } + }; + + // Disable rules + // When there are zero agents, allow only Show All and Hide Chat + const zeroAgents = !hasAgents; + const disableShowAll = false; // always allow switching to 3-pane view + const disableHideAgents = zeroAgents; // cannot hide agents if none exist + const disableHideChat = false; // allow hide chat even with zero agents (default) + const disableHideSkipper = zeroAgents; // keep skipper visible when no agents + + return ( + + + + + { + const key = Array.from(keys as Set)[0] as RadioKey; + const zeroAgents = !hasAgents; + // Allow only permitted options when zero agents + if (zeroAgents && key !== 'show-all' && key !== 'hide-chat') return; + if (key === 'hide-chat' && disableHideChat) return; + setByKey(key); + }}> + }>Show All + }>Hide Agents + }>Hide Chat + }>Hide Skipper + + + ); + })()} +
)} + {/* Deploy CTA - always visible */}
{isLive ? ( @@ -196,13 +282,13 @@ export function TopBar({ @@ -239,7 +325,6 @@ export function TopBar({ - {/* Live workflow label moved here */}
{publishing && }
@@ -270,7 +355,7 @@ export function TopBar({ - - - - - - } - onPress={() => onChangeMode('live')} - > - View live version - - } - onPress={onRevertToLive} - className="text-red-600 dark:text-red-400" - > - Reset to live version - - - + {(!hasAgents) ? ( + + + + + + ) : ( + + )} + {hasAgents ? ( + + + + + + } + onPress={() => onChangeMode('live')} + > + View live version + + } + onPress={onRevertToLive} + className="text-red-600 dark:text-red-400" + > + Reset to live version + + + + ) : ( + + + + + + )}
- {/* Moved draft/live labels and download button here */}
{publishing && } {isLive &&
@@ -357,7 +474,7 @@ export function TopBar({ From 168b01ca70d0dea80972dcd1ad232df400423f18 Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Fri, 12 Sep 2025 11:44:54 +0400 Subject: [PATCH 03/10] Add auto-publish flow --- .../app/projects/[projectId]/workflow/app.tsx | 24 +- .../workflow/components/TopBar.tsx | 432 ++++++++++++------ .../[projectId]/workflow/workflow_editor.tsx | 21 +- 3 files changed, 331 insertions(+), 146 deletions(-) diff --git a/apps/rowboat/app/projects/[projectId]/workflow/app.tsx b/apps/rowboat/app/projects/[projectId]/workflow/app.tsx index ab719c3f..9d7a4c8b 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/app.tsx @@ -37,16 +37,32 @@ export function App({ const stored = window.localStorage.getItem(`workflow_mode_${initialProjectData.id}`); return stored === 'live' || stored === 'draft' ? stored : 'draft'; }); + const [autoPublishEnabled, setAutoPublishEnabled] = useState(() => { + if (typeof window === 'undefined') return true; // Default to auto-publish + const stored = window.localStorage.getItem(`auto_publish_${initialProjectData.id}`); + return stored !== null ? stored === 'true' : true; + }); const [project, setProject] = useState>(initialProjectData); const [dataSources, setDataSources] = useState[]>(initialDataSources); const [loading, setLoading] = useState(false); console.log('workflow app.tsx render'); + const handleToggleAutoPublish = (enabled: boolean) => { + setAutoPublishEnabled(enabled); + if (typeof window !== 'undefined') { + window.localStorage.setItem(`auto_publish_${initialProjectData.id}`, enabled.toString()); + } + }; + // choose which workflow to display - let workflow: z.infer | undefined = project?.draftWorkflow; - if (mode == 'live') { - workflow = project?.liveWorkflow; + let workflow: z.infer | undefined; + if (autoPublishEnabled) { + // In auto-publish mode, always use draft (since they're synced) + workflow = project?.draftWorkflow; + } else { + // Manual mode: use current logic + workflow = mode === 'live' ? project?.liveWorkflow : project?.draftWorkflow; } const reloadData = useCallback(async () => { @@ -132,6 +148,8 @@ export function App({ {!loading && project && workflow && (dataSources !== null) && Promise; publishing: boolean; isLive: boolean; + autoPublishEnabled: boolean; + onToggleAutoPublish: (enabled: boolean) => void; showCopySuccess: boolean; showBuildModeBanner: boolean; canUndo: boolean; @@ -50,6 +52,8 @@ export function TopBar({ onProjectNameCommit, publishing, isLive, + autoPublishEnabled, + onToggleAutoPublish, showCopySuccess, showBuildModeBanner, canUndo, @@ -82,6 +86,15 @@ export function TopBar({ const router = useRouter(); const params = useParams(); const projectId = typeof (params as any).projectId === 'string' ? (params as any).projectId : (params as any).projectId?.[0]; + + // Share modal state + const { isOpen: isShareModalOpen, onOpen: onShareModalOpen, onClose: onShareModalClose } = useDisclosure(); + + const handleShareClick = () => { + onShareWorkflow(); // Call the original share function to generate URL + onShareModalOpen(); // Open the modal + }; + // Progress bar steps with completion logic and current step detection const step1Complete = hasAgentInstructionChanges; const step2Complete = hasPlaygroundTested && hasAgentInstructionChanges; @@ -99,9 +112,10 @@ export function TopBar({ ]; return ( + <>
-
+
{/* Project Name Editor */}
- {/* Show divider and mode indicator */} - {isLive &&
} - {isLive ? ( + {/* Mode pill and auto-publish checkbox */} +
+ + {/* Mode pill */} +
+ + + {autoPublishEnabled ? 'Live ' : (isLive ? 'Live ' : 'Draft')} + +
+ + {/* Auto-publish checkbox or Switch to draft button */} + {!autoPublishEnabled && isLive ? (
@@ -274,10 +305,11 @@ export function TopBar({ })()}
)} - {/* Deploy CTA - always visible */} + {/* Deploy CTA - conditional based on auto-publish mode */}
- {isLive ? ( + {autoPublishEnabled ? ( <> + {/* Auto-publish mode: Show Use Assistant button */} + + + + + + } + onPress={onDownloadJSON} + > + Download JSON + + +
- - - - {shareUrl && ( - - - - )} - - -
) : ( - <> -
- {(!hasAgents) ? ( - - - - - - ) : ( - - )} - {hasAgents ? ( + // Manual publish mode: Show current publish/live logic + isLive ? ( + <> - + } - onPress={() => onChangeMode('live')} + key="chat" + startContent={} + onPress={() => { + onUseAssistantClick(); + onStartNewChatAndFocus(); + }} > - View live version + Chat with Assistant } - onPress={onRevertToLive} - className="text-red-600 dark:text-red-400" + key="api-sdk" + startContent={} + onPress={() => { + onUseAssistantClick(); + if (projectId) { router.push(`/projects/${projectId}/config`); } + }} > - Reset to live version + API & SDK Settings + + } + onPress={() => { + onUseAssistantClick(); + if (projectId) { router.push(`/projects/${projectId}/manage-triggers`); } + }} + > + Manage Triggers - ) : ( - - + +
+ {publishing && } +
- - - )} -
- -
- {publishing && } - {isLive &&
- - Live workflow -
} - {!isLive &&
- - Draft workflow -
} - - + + + } + onPress={onDownloadJSON} + > + Download JSON + + + +
+
+ ) : ( + // Draft mode in manual publish: Show publish button + <> +
+ {(!hasAgents) ? ( + + + + + + ) : ( + - - {shareUrl && ( - - + Publish + + )} + {hasAgents ? ( + + + + + + } + onPress={() => onChangeMode('live')} + > + View live version + + } + onPress={onRevertToLive} + className="text-red-600 dark:text-red-400" + > + Reset to live version + + + + ) : ( + + + + )} - - - -
- +
+ +
+ {publishing && } +
+ + + + + + + } + onPress={onDownloadJSON} + > + Download JSON + + + +
+
+ + ) )}
+ + {/* Share Modal */} + + + + Share Assistant + + +
+

+ Share this assistant with others using the URL below: +

+ {shareUrl ? ( +
+ + +
+ ) : ( +
+ + + Generating share URL... + +
+ )} +
+
+ + + +
+
+ ); } diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index ccfaed3b..526694cb 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -960,6 +960,8 @@ export function WorkflowEditor({ projectConfig, eligibleModels, isLive, + autoPublishEnabled, + onToggleAutoPublish, onChangeMode, onRevertToLive, onProjectToolsUpdated, @@ -978,6 +980,8 @@ export function WorkflowEditor({ projectConfig: z.infer; eligibleModels: z.infer | "*"; isLive: boolean; + autoPublishEnabled: boolean; + onToggleAutoPublish: (enabled: boolean) => void; onChangeMode: (mode: 'draft' | 'live') => void; onRevertToLive: () => void; onProjectToolsUpdated?: () => void; @@ -1604,10 +1608,17 @@ export function WorkflowEditor({ saveQueue.current = []; try { - if (isLive) { - return; - } else { + if (autoPublishEnabled) { + // Auto-publish mode: save to both draft and live await saveWorkflow(projectId, workflowToSave); + await publishWorkflow(projectId, workflowToSave); + } else { + // Manual mode: current logic + if (isLive) { + return; + } else { + await saveWorkflow(projectId, workflowToSave); + } } } finally { saving.current = false; @@ -1617,7 +1628,7 @@ export function WorkflowEditor({ dispatch({ type: "set_saving", saving: false }); } } - }, [isLive, projectId]); + }, [autoPublishEnabled, isLive, projectId]); useEffect(() => { if (state.present.pendingChanges && state.present.workflow) { @@ -1863,6 +1874,8 @@ export function WorkflowEditor({ onProjectNameCommit={handleProjectNameCommit} publishing={state.present.publishing} isLive={isLive} + autoPublishEnabled={autoPublishEnabled} + onToggleAutoPublish={onToggleAutoPublish} showCopySuccess={showCopySuccess} showBuildModeBanner={showBuildModeBanner} canUndo={state.currentIndex > 0} From 1d3e3bb76e569785c61e3e5041214b5566f442c8 Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Fri, 12 Sep 2025 13:20:28 +0400 Subject: [PATCH 04/10] Fix content disappearance upon viewmode toggle --- .../app/projects/[projectId]/workflow/workflow_editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index 526694cb..eff2b58c 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -2019,7 +2019,7 @@ export function WorkflowEditor({ )} ) : ( - + {/* Agents (Entity List) column */} {(viewMode !== 'two_chat_skipper') && ( Date: Fri, 12 Sep 2025 13:28:17 +0400 Subject: [PATCH 05/10] Fix indentation of user-facing agent messages --- .../app/projects/[projectId]/playground/components/messages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx index ed84db8f..17beed72 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx @@ -179,7 +179,7 @@ function AssistantMessage({ /> )}
-
+
From 3dc7a6783ba06316170b1a0491ab095b0ba6736b Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Fri, 12 Sep 2025 13:33:39 +0400 Subject: [PATCH 06/10] Add typing ellipsis to playground --- .../playground/components/messages.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx index 17beed72..ec5a8c14 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx @@ -225,15 +225,14 @@ function AssistantMessage({ ); } -function AssistantMessageLoading() { +function TypingIndicator() { return ( -
-
-
- - Generating... +
+
+
+
+
+
@@ -948,7 +947,7 @@ export function Messages({ } return null; })} - {loadingAssistantResponse && } + {loadingAssistantResponse && }
); } From a68ae56787171da6c83645fad805de38c4b8bce0 Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Fri, 12 Sep 2025 13:42:37 +0400 Subject: [PATCH 07/10] Fix copilot compose box showing in agent instructions expanded view --- .../playground/components/messages.tsx | 16 +++++++++++++--- .../[projectId]/workflow/workflow_editor.tsx | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx index ec5a8c14..dd837e96 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/components/messages.tsx @@ -2,6 +2,7 @@ import { Spinner } from "@heroui/react"; import { useMemo, useState } from "react"; import z from "zod"; +import Image from "next/image"; import { Workflow } from "@/app/lib/types/workflow_types"; import { WorkflowTool } from "@/app/lib/types/workflow_types"; import MarkdownContent from "@/app/lib/components/markdown-content"; @@ -200,10 +201,13 @@ function AssistantMessage({ > - {`Image {img.truncated && (
@@ -525,10 +529,13 @@ function ClientToolCall({ > - {`Tool {img.truncated && (
@@ -617,10 +624,13 @@ function ClientToolCall({
{imagePreviews.map((img, i) => (
- {`Tool {img.truncated && (
diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index eff2b58c..83500c70 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -2103,7 +2103,7 @@ export function WorkflowEditor({ /> {/* Config overlay above Playground when selection open */} {state.present.selection && viewMode !== 'two_agents_skipper' && ( -
+
{state.present.selection?.type === "agent" && agent.name === state.present.selection!.name)}-${configKey}`} @@ -2209,7 +2209,7 @@ export function WorkflowEditor({ /> {/* Config overlay above Copilot when agents + skipper layout is active */} {state.present.selection && viewMode === 'two_agents_skipper' && ( -
+
{state.present.selection?.type === "agent" && agent.name === state.present.selection!.name)}-${configKey}`} From 64a6cffe9cb5e16294bd441c9211c708fd97ff5c Mon Sep 17 00:00:00 2001 From: arkml Date: Fri, 12 Sep 2025 16:11:34 +0530 Subject: [PATCH 08/10] Copilot2 (#246) * added customer support example * fixed twitter sentiment prebuilt card * make variable section open by default --- .../lib/prebuilt-cards/twitter-sentiment.json | 2 +- .../[projectId]/workflow/entity_list.tsx | 2 +- .../lib/copilot/copilot_multi_agent.ts | 1 + .../lib/copilot/example_multi_agent_1.ts | 180 ++++++++++++++++++ 4 files changed, 183 insertions(+), 2 deletions(-) diff --git a/apps/rowboat/app/lib/prebuilt-cards/twitter-sentiment.json b/apps/rowboat/app/lib/prebuilt-cards/twitter-sentiment.json index 03f78daa..51d6fb76 100644 --- a/apps/rowboat/app/lib/prebuilt-cards/twitter-sentiment.json +++ b/apps/rowboat/app/lib/prebuilt-cards/twitter-sentiment.json @@ -5,7 +5,7 @@ "type": "pipeline", "description": "Searches Twitter for tweets about a specified keywords.", "disabled": false, - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nSearch Twitter for tweets about a given keyword within a specified time window.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the keywords, `start_time`, and `end_time` from the parent agent.\n2. Use the [@tool:Search full archive of tweets](#mention) tool with each keyword as the query, and the provided `start_time` and `end_time`.\n3. Return the text of the tweets to the next agent in the pipeline.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Searching Twitter for tweets within a given time period.\n\n❌ Out of Scope:\n- Analyzing sentiment.\n- Interacting with the user directly.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Return only the tweet text.\n- Ensure `start_time` and `end_time` are correctly passed to the tool.\n\n🚫 Don'ts:\n- Do not perform sentiment analysis.\n- Do not interact with the user directly.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\nSearch Twitter for tweets about a given keyword within a specified time window.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the keywords. Use [@variable:ResultCount](#mention) for the Twitter search and [@variable:LookbackInHours](#mention) to search Twitter.\n2. Use the [@tool:Search full archive of tweets](#mention) tool with each keyword as the query, and the provided `start_time` and `end_time`.\n3. Return the text of the tweets to the next agent in the pipeline.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Searching Twitter for tweets within a given time period.\n\n❌ Out of Scope:\n- Analyzing sentiment.\n- Interacting with the user directly.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Return only the tweet text.\n- Ensure `start_time` and `end_time` are correctly passed to the tool.\n\n🚫 Don'ts:\n- Do not perform sentiment analysis.\n- Do not interact with the user directly.", "model": "google/gemini-2.5-flash", "locked": false, "toggleAble": true, diff --git a/apps/rowboat/app/projects/[projectId]/workflow/entity_list.tsx b/apps/rowboat/app/projects/[projectId]/workflow/entity_list.tsx index 27e7f02e..c8538636 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/entity_list.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/entity_list.tsx @@ -565,7 +565,7 @@ export const EntityList = forwardRef< agents: true, tools: true, data: true, - prompts: false + prompts: true }); // Default sizes when panels are expanded diff --git a/apps/rowboat/src/application/lib/copilot/copilot_multi_agent.ts b/apps/rowboat/src/application/lib/copilot/copilot_multi_agent.ts index aecaa2e4..c3b9d196 100644 --- a/apps/rowboat/src/application/lib/copilot/copilot_multi_agent.ts +++ b/apps/rowboat/src/application/lib/copilot/copilot_multi_agent.ts @@ -54,6 +54,7 @@ Agents fall into two main types: 1. Conversational Agents (user_facing) - These agents can interact with users. - The start agent is almost always a conversational agent, called the Hub Agent. It orchestrates the overall workflow and directs task execution. +- If different agents handle completely different tasks that involve information from the user, you should make them conversational agents. - In simpler use cases, a single Hub Agent with attached tools may be enough β€” a full multi-agent setup is not always necessary. - Core responsibilities: - Break down the user's query into subtasks diff --git a/apps/rowboat/src/application/lib/copilot/example_multi_agent_1.ts b/apps/rowboat/src/application/lib/copilot/example_multi_agent_1.ts index 14401ffe..32952654 100644 --- a/apps/rowboat/src/application/lib/copilot/example_multi_agent_1.ts +++ b/apps/rowboat/src/application/lib/copilot/example_multi_agent_1.ts @@ -1102,4 +1102,184 @@ Yes, I can delete the Meeting Prep Pipeline. --- +### Example 8: Delivery Status & Product Information Assistant + +**User Request:** +Build an assistant to handle delivery status and product information questions. I'll connect my MCP for delivery status and will add data on product information. + +**Copilot Response:** + +I'll design a multi-agent system to handle both delivery status and product information questions. Here’s my plan: +Plan & Agent Decomposition: +1. Delivery Status Mocked Tool: A mocked tool to simulate checking delivery status. +2. Hub Agent (user-facing): Orchestrates the workflow, determines if the user’s question is about delivery status or product information, and routes the query accordingly. +3. Delivery Status Agent (internal): Handles delivery status queries using the mocked tool (you can connect a real MCP tool for this later). +4. Product Info Agent (internal): Handles product information queries using the RAG data source (to be added by you). + +Next Steps: +- I’ll create the agents and set the Hub Agent as the start agent. +- Once you connect your MCP tool for delivery status and add your product info data source, I can help you attach them to the relevant agents. + +--- + +### 1. Add Required Tools + +I'll add a mocked tool for getting delivery status. You can connect a real MCP server for this later: + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Find event +{ + "change_description": "Added mock delivery status tool for simulating delivery status checks.", + "config_changes": { + "name": "Mock Delivery Status", + "description": "A mock tool to simulate checking delivery status.", + "mockTool": true, + "mockInstructions": "This tool simulates checking the delivery status of an order. It will always return a predefined delivery status message.", + "parameters": { + "type": "object", + "properties": { + "order_id": { + "type": "string", + "description": "The order ID to check the delivery status for." + } + }, + "required": [ + "order_id" + ] + } + } +} +\`\`\` + +--- + +### 2. Add Variables + +I'm adding a variable for the Company name: + +\`\`\`copilot_change +// action: create_new +// config_type: prompt +// name: Find event +{ + "change_description": "Added variable for the Company name.", + "config_changes": { + "name": "Company name", + "type": "base_prompt", + "prompt": "" + } +} +\`\`\` + +I'm adding another variable for the assistant name: + +\`\`\`copilot_change +// action: create_new +// config_type: prompt +// name: Find event +{ + "change_description": "Added variable for the Assistant name.", + "config_changes": { + "name": "Assistant name", + "type": "base_prompt", + "prompt": "" + } + } +\`\`\` + +--- + +### 3. Create Agents + +#### a. Hub Agent (Meeting Assistant Hub) +I'll create the hub agent: + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Product & Delivery Assistant +{ + "change_description": "Created the hub agent.", + "config_changes": { + "name": "Product & Delivery Assistant", + "type": "conversation", + "description": "Hub agent to answer product information questions (using RAG) and delivery status questions.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are the hub agent responsible for orchestrating responses to product information and delivery status questions.\n\n---\n## βš™οΈ Steps to Follow:\n1. Greet the user and ask how you can help. Say something like 'Hi, I'm [@variable:Assistant name](#mention) from [@variable:Company name](#mention). How can I help you today?'\n2. Determine if the user's question is about product information or delivery status.\n3. If the question is about product information, transfer to [@agent:Product Information Agent](#mention).\n4. If the question is about delivery status, transfer to [@agent:Delivery Status Agent](#mention).\n5. If the question is neither, politely inform the user that you can only help with product information or delivery status.\n6. Return the final answer to the user.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Routing product information questions.\n- Routing delivery status questions.\n\n❌ Out of Scope:\n- Directly answering product or delivery questions.\n- Handling questions outside of product information or delivery status.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Clearly identify the type of user query.\n- Route to the correct agent.\n\n🚫 Don'ts:\n- Do not attempt to answer questions directly.\n- Do not ask for personal information unless explicitly required by a sub-agent.\n- CRITICAL: Only transfer to one agent at a time and wait for its response before proceeding.\n\n", + "model": "google/gemini-2.5-flash", + "toggleAble": true, + "ragReturnType": "chunks", + "ragK": 3, + "outputVisibility": "user_facing", + "controlType": "retain" + } +} +\`\`\` + +#### b. Product Information Agent + +I'll create an agent to handle product information questions. You can later connect you data sources to provide the knowledge: + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Product Information Agent + { + "change_description": "Created the product information agent.", + "config_changes": { + "name": "Product Information Agent", + "type": "conversation", + "description": "Answers product information questions using RAG data sources.", + "disabled": false, + "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are an internal agent that answers product information questions using RAG data sources. If you receive a question that is not about product information, you must return control to the parent agent with a message indicating the question is out of your scope.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the product information question from the parent agent.\n2. Determine if the question is about product information.\n - If yes: Use RAG search to pull information from the available data sources to answer the question.\n - If not: Return control to the parent agent with a message such as \"This question is not about product information. Returning to parent agent.\"\n3. Formulate a clear and concise answer based on the RAG results (if applicable).\n4. If question is out of scope call [@agent:Product & Delivery Assistant](#mention) \n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Answering product information questions using RAG.\n- Returning control to parent if the question is out of scope.\n\n❌ Out of Scope:\n- Handling delivery status questions.\n- Interacting directly with the user.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Use RAG search to find relevant information for product questions.\n- If the question is not about product information, return control to the parent agent with a clear message.\n\n🚫 Don'ts:\n- Do not answer questions outside of product information.\n- Do not interact with the user directly.\n- Do not ignore out-of-scope questions; always return to parent.\n", + "examples": "\n", + "model": "google/gemini-2.5-flash", + "locked": false, + "toggleAble": true, + "ragReturnType": "chunks", + "ragK": 3, + "outputVisibility": "user_facing", + "controlType": "retain", + "maxCallsPerParentAgent": 3 + } +} +\`\`\` + +#### c. Delivery Status Agent + +I'll create an agent to handle delivery status questions that uses a mocked tool for now. You can later connect your MCP tool to it. + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Delivery Status Agent + { + "change_description": "Created the delivery status agent.", + "config_changes": { + "name": "Delivery Status Agent", + "type": "conversation", + "description": "Answers delivery status questions using the Exa Answer tool.", + "disabled": false, + "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are an internal agent that answers delivery status questions. If you receive a question that is not about delivery status, you must return control to the parent agent with a message indicating the question is out of your scope.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the delivery status question from the parent agent.\n2. Determine if the question is about delivery status.\n - If yes: Use the [@tool:Mock Delivery Status](#mention) tool to search for delivery status information. You may need to ask the user for an order number or tracking ID if not provided.\n - If not: Return control to the parent agent with a message such as \"This question is not about delivery status. Returning to parent agent.\"\n3. Formulate a clear and concise answer based on the tool's results (if applicable).\n4. If question is out of scope call [@agent:Product & Delivery Assistant](#mention) \n---\n## 🎯 Scope:\nβœ… In Scope:\n- Answering delivery status questions using the Exa Answer tool.\n- Returning control to parent if the question is out of scope.\n\n❌ Out of Scope:\n- Handling product information questions.\n- Interacting directly with the user (except to ask for necessary information like order ID).\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Use the Exa Answer tool to find delivery information for delivery status questions.\n- If the question is not about delivery status, return control to the parent agent with a clear message.\n- Ask for order details if needed.\n\n🚫 Don'ts:\n- Do not answer questions outside of delivery status.\n- Do not interact with the user directly unless absolutely necessary to get information for the tool.\n- Do not ignore out-of-scope questions; always return to parent.\n", + "examples": "\n", + "model": "gpt-4.1", + "locked": false, + "toggleAble": true, + "ragReturnType": "chunks", + "ragK": 3, + "outputVisibility": "user_facing", + "controlType": "retain", + "maxCallsPerParentAgent": 3 + } +} +\`\`\` + +--- + +Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent or adjust the workflow as needed. Would you like to customize any step or add more details? + +--- + + `; \ No newline at end of file From 458302fc434214b00306cba091f28e16bf4cf7e6 Mon Sep 17 00:00:00 2001 From: arkml Date: Fri, 12 Sep 2025 17:36:57 +0530 Subject: [PATCH 09/10] Small improvements 4 (#247) * removed Publish from progress bar * removed publish tour related code * make chat correctly in tours * fixed use step completion --- .../workflow/components/TopBar.tsx | 14 +++---- .../[projectId]/workflow/workflow_editor.tsx | 42 ++++++------------- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx b/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx index 3fa50223..312dc7b4 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/components/TopBar.tsx @@ -38,7 +38,6 @@ interface TopBarProps { onStartNewChatAndFocus: () => void; onStartBuildTour?: () => void; onStartTestTour?: () => void; - onStartPublishTour?: () => void; onStartUseTour?: () => void; onShareWorkflow: () => void; shareUrl: string | null; @@ -77,7 +76,6 @@ export function TopBar({ onStartNewChatAndFocus, onStartBuildTour, onStartTestTour, - onStartPublishTour, onStartUseTour, onShareWorkflow, shareUrl, @@ -98,16 +96,17 @@ export function TopBar({ // Progress bar steps with completion logic and current step detection const step1Complete = hasAgentInstructionChanges; const step2Complete = hasPlaygroundTested && hasAgentInstructionChanges; - const step3Complete = hasPublished && hasPlaygroundTested && hasAgentInstructionChanges; - const step4Complete = hasClickedUse && hasPublished && hasPlaygroundTested && hasAgentInstructionChanges; + // Keep publish as a prerequisite for Use completion, but remove it from the visual steps + // Mark "Use" complete as soon as a Use Assistant option is clicked + const step4Complete = hasClickedUse; - // Determine current step (first incomplete step) - const currentStep = !step1Complete ? 1 : !step2Complete ? 2 : !step3Complete ? 3 : !step4Complete ? 4 : null; + // Determine current step (first incomplete visual step: 1 -> 2 -> 4) + const currentStep = !step1Complete ? 1 : !step2Complete ? 2 : !step4Complete ? 4 : null; const progressSteps: ProgressStep[] = [ { id: 1, label: "Build: Ask the copilot to create your assistant. Add tools and connect data sources.", completed: step1Complete, isCurrent: currentStep === 1 }, { id: 2, label: "Test: Test out your assistant by chatting with it. Use 'Fix' and 'Explain' to improve it.", completed: step2Complete, isCurrent: currentStep === 2 }, - { id: 3, label: "Publish: Make it live with the Publish button. You can always switch back to draft.", completed: step3Complete, isCurrent: currentStep === 3 }, + // Removed the 'Publish' step from the progress bar { id: 4, label: "Use: Click the 'Use Assistant' button to chat, set triggers (like emails), or connect via API.", completed: step4Complete, isCurrent: currentStep === 4 }, ]; @@ -185,7 +184,6 @@ export function TopBar({ onStepClick={(step) => { if (step.id === 1 && onStartBuildTour) onStartBuildTour(); if (step.id === 2 && onStartTestTour) onStartTestTour(); - if (step.id === 3 && onStartPublishTour) onStartPublishTour(); if (step.id === 4 && onStartUseTour) onStartUseTour(); }} /> diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index 83500c70..72f1e94d 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -1077,7 +1077,6 @@ export function WorkflowEditor({ const [showTour, setShowTour] = useState(true); const [showBuildTour, setShowBuildTour] = useState(false); const [showTestTour, setShowTestTour] = useState(false); - const [showPublishTour, setShowPublishTour] = useState(false); const [showUseTour, setShowUseTour] = useState(false); // Centralized mode transition handler @@ -1192,10 +1191,8 @@ export function WorkflowEditor({ // Ensure chat is visible and collapse left panel setActivePanel('playground'); setViewMode((prev: ViewMode) => prev); - updateViewMode( - viewMode === 'three_all' ? 'three_all' : - (viewMode === 'two_agents_skipper' ? 'two_agents_chat' : 'two_chat_skipper') - ); + // Expand Chat to full view: hide Copilot panel and collapse Agents panel + updateViewMode('two_agents_chat'); setIsLeftPanelCollapsed(true); }, [updateViewMode, viewMode]); @@ -1917,18 +1914,6 @@ export function WorkflowEditor({ }); }); }} - onStartPublishTour={() => { - // Switch to 3-pane first to ensure elements are visible - updateViewMode('three_all'); - if (isLive) { - handleModeTransition('draft', 'switch_draft'); - } - requestAnimationFrame(() => { - requestAnimationFrame(() => { - setShowPublishTour(true); - }); - }); - }} onStartUseTour={() => { updateViewMode('three_all'); requestAnimationFrame(() => { @@ -2317,7 +2302,11 @@ export function WorkflowEditor({ { target: 'copilot', title: 'Step 2/2', content: 'Ask Copilot to improve your agents based on test results. Use "Fix" and "Explain" to iterate quickly.' }, ]} onStepChange={(index) => { - if (index === 0) setActivePanel('playground'); + if (index === 0) { + // Ensure Chat is focused and any middle-pane detail overlay is dismissed + setActivePanel('playground'); + dispatch({ type: 'unselect_agent' }); + } if (index === 1) setActivePanel('copilot'); }} onComplete={() => setShowTestTour(false)} @@ -2335,21 +2324,16 @@ export function WorkflowEditor({ { target: 'conversations', title: 'Step 5/5', content: 'Conversations: see all past interactions in one place, including manual chats, trigger activity, and API calls.' }, ]} onStepChange={(index) => { - if (index === 0) setActivePanel('playground'); + if (index === 0) { + // Ensure Chat is focused and any middle-pane detail overlay is dismissed + setActivePanel('playground'); + dispatch({ type: 'unselect_agent' }); + } }} onComplete={() => setShowUseTour(false)} /> )} - {showPublishTour && ( - setShowPublishTour(false)} - /> - )} + {/* Revert to Live Confirmation Modal */} From 3c4e0473b93e42cd5fab69fa23e287911a9d7d7d Mon Sep 17 00:00:00 2001 From: Tushar <47842976+tusharmagar@users.noreply.github.com> Date: Fri, 12 Sep 2025 18:32:23 +0530 Subject: [PATCH 10/10] fix: remove no-agent message that was incorrectly triggering for pipeline start agents (#245) - Removed 'Hi! To get started with chatting with your Assistant please create an agent first!' message feature - Pipelines can now be set as start agents without triggering this error message - Removed checks from emitGreetingTurn() and streamResponse() functions - System now handles all agent types properly as start agents --- .../application/lib/agents-runtime/agents.ts | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/apps/rowboat/src/application/lib/agents-runtime/agents.ts b/apps/rowboat/src/application/lib/agents-runtime/agents.ts index d6cc0b2a..a974dc4f 100644 --- a/apps/rowboat/src/application/lib/agents-runtime/agents.ts +++ b/apps/rowboat/src/application/lib/agents-runtime/agents.ts @@ -379,20 +379,6 @@ function mapConfig(workflow: z.infer): { } async function* emitGreetingTurn(logger: PrefixLogger, workflow: z.infer): AsyncIterable> { - // Check if there are no agents or no start agent - if (workflow.agents.length === 0 || !workflow.startAgent) { - logger.log(`no agents available, emitting no-agent message`); - - // emit no-agent message - yield* emitEvent(logger, { - role: 'assistant', - content: 'Hi! To get started with chatting with your Assistant please create an agent first!', - agentName: 'System', - responseType: 'external', - }); - return; - } - // find the greeting prompt const prompt = workflow.prompts.find(p => p.type === 'greeting')?.prompt || 'How can I help you today?'; logger.log(`greeting turn: ${prompt}`); @@ -1288,20 +1274,6 @@ export async function* streamResponse( logger.log(`start agent: ${workflow.startAgent}`); logger.log(`=== END CONFIGURATION ===`); - // Check if there are no agents or no start agent - if (Object.keys(agentConfig).length === 0 || !workflow.startAgent) { - logger.log(`no agents available, emitting no-agent message`); - - // emit no-agent message - yield* emitEvent(logger, { - role: 'assistant', - content: 'Hi! To get started with chatting with your Assistant please create an agent first!', - agentName: 'System', - responseType: 'external', - }); - return; - } - const stack: string[] = []; logger.log(`initialized stack: ${JSON.stringify(stack)}`); @@ -1324,20 +1296,6 @@ export async function* streamResponse( const startOfTurnAgentName = getStartOfTurnAgentName(logger, messages, agentConfig, pipelineConfig, workflow); logger.log(`🎯 START AGENT DECISION: ${startOfTurnAgentName}`); - // Additional safety check - if startOfTurnAgentName is empty or invalid, return no-agent message - if (!startOfTurnAgentName || !agentConfig[startOfTurnAgentName]) { - logger.log(`invalid start agent name: ${startOfTurnAgentName}, emitting no-agent message`); - - // emit no-agent message - yield* emitEvent(logger, { - role: 'assistant', - content: 'Hi! To get started with chatting with your Assistant please create an agent first!', - agentName: 'System', - responseType: 'external', - }); - return; - } - let agentName: string | null = startOfTurnAgentName; // start the turn loop