diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatContainer/ChatContainer.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatContainer/ChatContainer.tsx index d9e1d840cd..fa76175d1c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatContainer/ChatContainer.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatContainer/ChatContainer.tsx @@ -6,6 +6,7 @@ import { ChatInput } from "@/components/contextual/Chat/components/ChatInput/Cha import { postV2CreateSession } from "@/app/api/__generated__/endpoints/chat/chat"; import { useState } from "react"; import { parseAsString, useQueryState } from "nuqs"; +import { CopilotChatActionsProvider } from "../CopilotChatActionsProvider/CopilotChatActionsProvider"; export interface ChatContainerProps { messages: UIMessage[]; @@ -45,34 +46,36 @@ export const ChatContainer = ({ } return ( -
-
- {sessionId ? ( - - ) : ( - - )} -
-
- {}} - placeholder="You can search or just ask" - /> + +
+
+ {sessionId ? ( + + ) : ( + + )} +
+
+ {}} + placeholder="You can search or just ask" + /> +
-
+
); }; diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatMessagesContainer/ChatMessagesContainer.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatMessagesContainer/ChatMessagesContainer.tsx index 99f72f245c..8035e190ee 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatMessagesContainer/ChatMessagesContainer.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/ChatMessagesContainer/ChatMessagesContainer.tsx @@ -17,6 +17,8 @@ import { SearchDocsTool } from "../../tools/SearchDocs/SearchDocs"; import { RunBlockTool } from "../../tools/RunBlock/RunBlock"; import { RunAgentTool } from "../../tools/RunAgent/RunAgent"; import { ViewAgentOutputTool } from "../../tools/ViewAgentOutput/ViewAgentOutput"; +import { CreateAgentTool } from "../../tools/CreateAgent/CreateAgent"; +import { EditAgentTool } from "../../tools/EditAgent/EditAgent"; interface ChatMessagesContainerProps { messages: UIMessage[]; @@ -90,12 +92,27 @@ export const ChatMessagesContainer = ({ /> ); case "tool-run_agent": + case "tool-schedule_agent": return ( ); + case "tool-create_agent": + return ( + + ); + case "tool-edit_agent": + return ( + + ); case "tool-view_agent_output": return ( void; + children: React.ReactNode; +} + +export function CopilotChatActionsProvider({ onSend, children }: Props) { + return ( + + {children} + + ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/CopilotChatActionsProvider/useCopilotChatActions.ts b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/CopilotChatActionsProvider/useCopilotChatActions.ts new file mode 100644 index 0000000000..580e4075d6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/components/CopilotChatActionsProvider/useCopilotChatActions.ts @@ -0,0 +1,23 @@ +"use client"; + +import { createContext, useContext } from "react"; + +interface CopilotChatActions { + onSend: (message: string) => void; +} + +const CopilotChatActionsContext = createContext( + null, +); + +export function useCopilotChatActions(): CopilotChatActions { + const ctx = useContext(CopilotChatActionsContext); + if (!ctx) { + throw new Error( + "useCopilotChatActions must be used within CopilotChatActionsProvider", + ); + } + return ctx; +} + +export { CopilotChatActionsContext }; diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/CreateAgent.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/CreateAgent.tsx new file mode 100644 index 0000000000..54209be7dc --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/CreateAgent.tsx @@ -0,0 +1,189 @@ +"use client"; + +import type { ToolUIPart } from "ai"; +import Link from "next/link"; +import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation"; +import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion"; +import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions"; +import { ClarificationQuestionsWidget } from "@/components/contextual/Chat/components/ClarificationQuestionsWidget/ClarificationQuestionsWidget"; +import { + formatMaybeJson, + getAnimationText, + getCreateAgentToolOutput, + StateIcon, + truncateText, + type CreateAgentToolOutput, +} from "./helpers"; + +export interface CreateAgentToolPart { + type: string; + toolCallId: string; + state: ToolUIPart["state"]; + input?: unknown; + output?: unknown; +} + +interface Props { + part: CreateAgentToolPart; +} + +function getAccordionMeta(output: CreateAgentToolOutput): { + badgeText: string; + title: string; + description?: string; +} { + if (output.type === "agent_saved") { + return { badgeText: "Create agent", title: output.agent_name }; + } + if (output.type === "agent_preview") { + return { + badgeText: "Create agent", + title: output.agent_name, + description: `${output.node_count} block${output.node_count === 1 ? "" : "s"}`, + }; + } + if (output.type === "clarification_needed") { + return { + badgeText: "Create agent", + title: "Needs clarification", + description: `${output.questions.length} question${output.questions.length === 1 ? "" : "s"}`, + }; + } + if ( + output.type === "operation_started" || + output.type === "operation_pending" || + output.type === "operation_in_progress" + ) { + return { badgeText: "Create agent", title: "Creating agent" }; + } + return { badgeText: "Create agent", title: "Error" }; +} + +export function CreateAgentTool({ part }: Props) { + const text = getAnimationText(part); + const { onSend } = useCopilotChatActions(); + + const output = getCreateAgentToolOutput(part); + const hasExpandableContent = + part.state === "output-available" && + !!output && + (output.type === "operation_started" || + output.type === "operation_pending" || + output.type === "operation_in_progress" || + output.type === "agent_preview" || + output.type === "agent_saved" || + output.type === "clarification_needed" || + output.type === "error"); + + function handleClarificationAnswers(answers: Record) { + const contextMessage = Object.entries(answers) + .map(([keyword, answer]) => `${keyword}: ${answer}`) + .join("\n"); + + onSend( + `I have the answers to your questions:\n\n${contextMessage}\n\nPlease proceed with creating the agent.`, + ); + } + + return ( +
+
+ + +
+ + {hasExpandableContent && output && ( + + {(output.type === "operation_started" || + output.type === "operation_pending") && ( +
+

{output.message}

+

+ Operation: {output.operation_id} +

+

+ Check your library in a few minutes. +

+
+ )} + + {output.type === "operation_in_progress" && ( +
+

{output.message}

+

+ Please wait for the current operation to finish. +

+
+ )} + + {output.type === "agent_saved" && ( +
+

{output.message}

+
+ + Open in library + + + Open in builder + +
+
+                {truncateText(
+                  formatMaybeJson({ agent_id: output.agent_id }),
+                  800,
+                )}
+              
+
+ )} + + {output.type === "agent_preview" && ( +
+

{output.message}

+ {output.description?.trim() && ( +

+ {output.description} +

+ )} +
+                {truncateText(formatMaybeJson(output.agent_json), 1600)}
+              
+
+ )} + + {output.type === "clarification_needed" && ( + + )} + + {output.type === "error" && ( +
+

{output.message}

+ {output.error && ( +
+                  {formatMaybeJson(output.error)}
+                
+ )} + {output.details && ( +
+                  {formatMaybeJson(output.details)}
+                
+ )} +
+ )} +
+ )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/helpers.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/helpers.tsx new file mode 100644 index 0000000000..8153ce1406 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/CreateAgent/helpers.tsx @@ -0,0 +1,168 @@ +import type { ToolUIPart } from "ai"; +import { + CheckCircleIcon, + CircleNotchIcon, + XCircleIcon, +} from "@phosphor-icons/react"; + +export interface ClarifyingQuestion { + question: string; + keyword: string; + example?: string; +} + +export interface OperationStartedOutput { + type: "operation_started"; + message: string; + session_id?: string; + operation_id: string; + tool_name: string; +} + +export interface OperationPendingOutput { + type: "operation_pending"; + message: string; + session_id?: string; + operation_id: string; + tool_name: string; +} + +export interface OperationInProgressOutput { + type: "operation_in_progress"; + message: string; + session_id?: string; + tool_call_id: string; +} + +export interface AgentPreviewOutput { + type: "agent_preview"; + message: string; + session_id?: string; + agent_json: Record; + agent_name: string; + description: string; + node_count: number; + link_count: number; +} + +export interface AgentSavedOutput { + type: "agent_saved"; + message: string; + session_id?: string; + agent_id: string; + agent_name: string; + library_agent_id: string; + library_agent_link: string; + agent_page_link: string; +} + +export interface ClarificationNeededOutput { + type: "clarification_needed"; + message: string; + session_id?: string; + questions: ClarifyingQuestion[]; +} + +export interface ErrorOutput { + type: "error"; + message: string; + session_id?: string; + error?: string | null; + details?: Record | null; +} + +export type CreateAgentToolOutput = + | OperationStartedOutput + | OperationPendingOutput + | OperationInProgressOutput + | AgentPreviewOutput + | AgentSavedOutput + | ClarificationNeededOutput + | ErrorOutput; + +function parseOutput(output: unknown): CreateAgentToolOutput | null { + if (!output) return null; + if (typeof output === "string") { + const trimmed = output.trim(); + if (!trimmed) return null; + try { + return JSON.parse(trimmed) as CreateAgentToolOutput; + } catch { + return null; + } + } + if (typeof output === "object") return output as CreateAgentToolOutput; + return null; +} + +export function getCreateAgentToolOutput( + part: unknown, +): CreateAgentToolOutput | null { + if (!part || typeof part !== "object") return null; + return parseOutput((part as { output?: unknown }).output); +} + +export function getAnimationText(part: { + state: ToolUIPart["state"]; + input?: unknown; + output?: unknown; +}): string { + switch (part.state) { + case "input-streaming": + return "Creating agent"; + case "input-available": + return "Generating agent workflow"; + case "output-available": { + const output = parseOutput(part.output); + if (!output) return "Agent created"; + if (output.type === "operation_started") return "Agent creation started"; + if (output.type === "operation_pending") + return "Agent creation in progress"; + if (output.type === "operation_in_progress") + return "Agent creation already in progress"; + if (output.type === "agent_saved") return `Saved: ${output.agent_name}`; + if (output.type === "agent_preview") + return `Preview: ${output.agent_name}`; + if (output.type === "clarification_needed") return "Needs clarification"; + return "Error creating agent"; + } + case "output-error": + return "Error creating agent"; + default: + return "Processing"; + } +} + +export function StateIcon({ state }: { state: ToolUIPart["state"] }) { + switch (state) { + case "input-streaming": + case "input-available": + return ( + + ); + case "output-available": + return ; + case "output-error": + return ; + default: + return null; + } +} + +export function formatMaybeJson(value: unknown): string { + if (typeof value === "string") return value; + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} + +export function truncateText(text: string, maxChars: number): string { + const trimmed = text.trim(); + if (trimmed.length <= maxChars) return trimmed; + return `${trimmed.slice(0, maxChars).trimEnd()}…`; +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/EditAgent.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/EditAgent.tsx new file mode 100644 index 0000000000..a170342900 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/EditAgent.tsx @@ -0,0 +1,189 @@ +"use client"; + +import type { ToolUIPart } from "ai"; +import Link from "next/link"; +import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation"; +import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion"; +import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions"; +import { ClarificationQuestionsWidget } from "@/components/contextual/Chat/components/ClarificationQuestionsWidget/ClarificationQuestionsWidget"; +import { + formatMaybeJson, + getAnimationText, + getEditAgentToolOutput, + StateIcon, + truncateText, + type EditAgentToolOutput, +} from "./helpers"; + +export interface EditAgentToolPart { + type: string; + toolCallId: string; + state: ToolUIPart["state"]; + input?: unknown; + output?: unknown; +} + +interface Props { + part: EditAgentToolPart; +} + +function getAccordionMeta(output: EditAgentToolOutput): { + badgeText: string; + title: string; + description?: string; +} { + if (output.type === "agent_saved") { + return { badgeText: "Edit agent", title: output.agent_name }; + } + if (output.type === "agent_preview") { + return { + badgeText: "Edit agent", + title: output.agent_name, + description: `${output.node_count} block${output.node_count === 1 ? "" : "s"}`, + }; + } + if (output.type === "clarification_needed") { + return { + badgeText: "Edit agent", + title: "Needs clarification", + description: `${output.questions.length} question${output.questions.length === 1 ? "" : "s"}`, + }; + } + if ( + output.type === "operation_started" || + output.type === "operation_pending" || + output.type === "operation_in_progress" + ) { + return { badgeText: "Edit agent", title: "Editing agent" }; + } + return { badgeText: "Edit agent", title: "Error" }; +} + +export function EditAgentTool({ part }: Props) { + const text = getAnimationText(part); + const { onSend } = useCopilotChatActions(); + + const output = getEditAgentToolOutput(part); + const hasExpandableContent = + part.state === "output-available" && + !!output && + (output.type === "operation_started" || + output.type === "operation_pending" || + output.type === "operation_in_progress" || + output.type === "agent_preview" || + output.type === "agent_saved" || + output.type === "clarification_needed" || + output.type === "error"); + + function handleClarificationAnswers(answers: Record) { + const contextMessage = Object.entries(answers) + .map(([keyword, answer]) => `${keyword}: ${answer}`) + .join("\n"); + + onSend( + `I have the answers to your questions:\n\n${contextMessage}\n\nPlease proceed with editing the agent.`, + ); + } + + return ( +
+
+ + +
+ + {hasExpandableContent && output && ( + + {(output.type === "operation_started" || + output.type === "operation_pending") && ( +
+

{output.message}

+

+ Operation: {output.operation_id} +

+

+ Check your library in a few minutes. +

+
+ )} + + {output.type === "operation_in_progress" && ( +
+

{output.message}

+

+ Please wait for the current operation to finish. +

+
+ )} + + {output.type === "agent_saved" && ( +
+

{output.message}

+
+ + Open in library + + + Open in builder + +
+
+                {truncateText(
+                  formatMaybeJson({ agent_id: output.agent_id }),
+                  800,
+                )}
+              
+
+ )} + + {output.type === "agent_preview" && ( +
+

{output.message}

+ {output.description?.trim() && ( +

+ {output.description} +

+ )} +
+                {truncateText(formatMaybeJson(output.agent_json), 1600)}
+              
+
+ )} + + {output.type === "clarification_needed" && ( + + )} + + {output.type === "error" && ( +
+

{output.message}

+ {output.error && ( +
+                  {formatMaybeJson(output.error)}
+                
+ )} + {output.details && ( +
+                  {formatMaybeJson(output.details)}
+                
+ )} +
+ )} +
+ )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/helpers.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/helpers.tsx new file mode 100644 index 0000000000..af2b38449c --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/EditAgent/helpers.tsx @@ -0,0 +1,168 @@ +import type { ToolUIPart } from "ai"; +import { + CheckCircleIcon, + CircleNotchIcon, + XCircleIcon, +} from "@phosphor-icons/react"; + +export interface ClarifyingQuestion { + question: string; + keyword: string; + example?: string; +} + +export interface OperationStartedOutput { + type: "operation_started"; + message: string; + session_id?: string; + operation_id: string; + tool_name: string; +} + +export interface OperationPendingOutput { + type: "operation_pending"; + message: string; + session_id?: string; + operation_id: string; + tool_name: string; +} + +export interface OperationInProgressOutput { + type: "operation_in_progress"; + message: string; + session_id?: string; + tool_call_id: string; +} + +export interface AgentPreviewOutput { + type: "agent_preview"; + message: string; + session_id?: string; + agent_json: Record; + agent_name: string; + description: string; + node_count: number; + link_count: number; +} + +export interface AgentSavedOutput { + type: "agent_saved"; + message: string; + session_id?: string; + agent_id: string; + agent_name: string; + library_agent_id: string; + library_agent_link: string; + agent_page_link: string; +} + +export interface ClarificationNeededOutput { + type: "clarification_needed"; + message: string; + session_id?: string; + questions: ClarifyingQuestion[]; +} + +export interface ErrorOutput { + type: "error"; + message: string; + session_id?: string; + error?: string | null; + details?: Record | null; +} + +export type EditAgentToolOutput = + | OperationStartedOutput + | OperationPendingOutput + | OperationInProgressOutput + | AgentPreviewOutput + | AgentSavedOutput + | ClarificationNeededOutput + | ErrorOutput; + +function parseOutput(output: unknown): EditAgentToolOutput | null { + if (!output) return null; + if (typeof output === "string") { + const trimmed = output.trim(); + if (!trimmed) return null; + try { + return JSON.parse(trimmed) as EditAgentToolOutput; + } catch { + return null; + } + } + if (typeof output === "object") return output as EditAgentToolOutput; + return null; +} + +export function getEditAgentToolOutput( + part: unknown, +): EditAgentToolOutput | null { + if (!part || typeof part !== "object") return null; + return parseOutput((part as { output?: unknown }).output); +} + +export function getAnimationText(part: { + state: ToolUIPart["state"]; + input?: unknown; + output?: unknown; +}): string { + switch (part.state) { + case "input-streaming": + return "Editing agent"; + case "input-available": + return "Updating agent workflow"; + case "output-available": { + const output = parseOutput(part.output); + if (!output) return "Agent updated"; + if (output.type === "operation_started") return "Agent update started"; + if (output.type === "operation_pending") + return "Agent update in progress"; + if (output.type === "operation_in_progress") + return "Agent update already in progress"; + if (output.type === "agent_saved") return `Saved: ${output.agent_name}`; + if (output.type === "agent_preview") + return `Preview: ${output.agent_name}`; + if (output.type === "clarification_needed") return "Needs clarification"; + return "Error editing agent"; + } + case "output-error": + return "Error editing agent"; + default: + return "Processing"; + } +} + +export function StateIcon({ state }: { state: ToolUIPart["state"] }) { + switch (state) { + case "input-streaming": + case "input-available": + return ( + + ); + case "output-available": + return ; + case "output-error": + return ; + default: + return null; + } +} + +export function formatMaybeJson(value: unknown): string { + if (typeof value === "string") return value; + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} + +export function truncateText(text: string, maxChars: number): string { + const trimmed = text.trim(); + if (trimmed.length <= maxChars) return trimmed; + return `${trimmed.slice(0, maxChars).trimEnd()}…`; +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/RunAgent.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/RunAgent.tsx index 276b82bf85..83b601b949 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/RunAgent.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/RunAgent.tsx @@ -4,6 +4,8 @@ import type { ToolUIPart } from "ai"; import Link from "next/link"; import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation"; import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion"; +import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions"; +import { ChatCredentialsSetup } from "@/components/contextual/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup"; import { formatMaybeJson, getAnimationText, @@ -37,6 +39,14 @@ function getAccordionMeta(output: RunAgentToolOutput): { }; } + if (output.type === "agent_details") { + return { + badgeText: "Run agent", + title: output.agent.name, + description: "Inputs required", + }; + } + if (output.type === "setup_requirements") { const missingCredsCount = Object.keys( output.setup_info.user_readiness.missing_credentials ?? {}, @@ -60,16 +70,24 @@ function getAccordionMeta(output: RunAgentToolOutput): { export function RunAgentTool({ part }: Props) { const text = getAnimationText(part); + const { onSend } = useCopilotChatActions(); const output = getRunAgentToolOutput(part); const hasExpandableContent = part.state === "output-available" && !!output && (output.type === "execution_started" || + output.type === "agent_details" || output.type === "setup_requirements" || output.type === "need_login" || output.type === "error"); + function handleAllCredentialsComplete() { + onSend( + "I've configured the required credentials. Please check if everything is ready and proceed with running the agent.", + ); + } + return (
@@ -78,7 +96,13 @@ export function RunAgentTool({ part }: Props) {
{hasExpandableContent && output && ( - + {output.type === "execution_started" && (
@@ -107,6 +131,28 @@ export function RunAgentTool({ part }: Props) {
)} + {output.type === "agent_details" && ( +
+

{output.message}

+ + {output.agent.description?.trim() && ( +

+ {output.agent.description} +

+ )} + +
+

Inputs

+

+ Provide required inputs in chat, or ask to run with defaults. +

+
+                  {formatMaybeJson(output.agent.inputs)}
+                
+
+
+ )} + {output.type === "setup_requirements" && (

{output.message}

@@ -114,21 +160,24 @@ export function RunAgentTool({ part }: Props) { {Object.keys( output.setup_info.user_readiness.missing_credentials ?? {}, ).length > 0 && ( -
-

- Missing credentials -

-
    - {Object.entries( - output.setup_info.user_readiness.missing_credentials ?? - {}, - ).map(([field, cred]) => ( -
  • - {cred.title} ({cred.provider}) -
  • - ))} -
-
+ ({ + provider: cred.provider, + providerName: + cred.provider_name ?? cred.provider.replace(/_/g, " "), + credentialTypes: (cred.types ?? [cred.type]) as Array< + "api_key" | "oauth2" | "user_password" | "host_scoped" + >, + title: cred.title, + scopes: cred.scopes, + }))} + agentName={output.setup_info.agent_name} + message={output.message} + onAllCredentialsComplete={handleAllCredentialsComplete} + onCancel={() => {}} + /> )} {output.setup_info.requirements.inputs?.length > 0 && ( diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/helpers.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/helpers.tsx index a63394be8f..709b3393e8 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/helpers.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunAgent/helpers.tsx @@ -18,8 +18,11 @@ export interface RunAgentInput { export interface CredentialsMeta { id: string; provider: string; + provider_name?: string; type: string; + types?: string[]; title: string; + scopes?: string[]; } export interface SetupInfo { @@ -78,9 +81,31 @@ export interface NeedLoginOutput { session_id?: string; } +export interface AgentDetailsOutput { + type: "agent_details"; + message: string; + session_id?: string; + agent: { + id: string; + name: string; + description: string; + inputs: Record; + credentials: CredentialsMeta[]; + execution_options?: { + manual?: boolean; + scheduled?: boolean; + webhook?: boolean; + }; + }; + user_authenticated?: boolean; + graph_id?: string | null; + graph_version?: number | null; +} + export type RunAgentToolOutput = | SetupRequirementsOutput | ExecutionStartedOutput + | AgentDetailsOutput | NeedLoginOutput | ErrorOutput; @@ -143,6 +168,9 @@ export function getAnimationText(part: { if (output.type === "execution_started") { return `Started: ${output.graph_name}`; } + if (output.type === "agent_details") { + return `Agent inputs: ${output.agent.name}`; + } if (output.type === "setup_requirements") { return `Needs setup: ${output.setup_info.agent_name}`; } diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/RunBlock.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/RunBlock.tsx index 4dc08e70eb..5315105999 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/RunBlock.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/RunBlock.tsx @@ -3,6 +3,8 @@ import type { ToolUIPart } from "ai"; import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation"; import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion"; +import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions"; +import { ChatCredentialsSetup } from "@/components/contextual/Chat/components/ChatCredentialsSetup/ChatCredentialsSetup"; import { formatMaybeJson, getAnimationText, @@ -59,6 +61,7 @@ function getAccordionMeta(output: RunBlockToolOutput): { export function RunBlockTool({ part }: Props) { const text = getAnimationText(part); + const { onSend } = useCopilotChatActions(); const output = getRunBlockToolOutput(part); const hasExpandableContent = @@ -68,6 +71,12 @@ export function RunBlockTool({ part }: Props) { output.type === "setup_requirements" || output.type === "error"); + function handleAllCredentialsComplete() { + onSend( + "I've configured the required credentials. Please re-run the block now.", + ); + } + return (
@@ -76,7 +85,10 @@ export function RunBlockTool({ part }: Props) {
{hasExpandableContent && output && ( - + {output.type === "block_output" && (

{output.message}

@@ -106,21 +118,24 @@ export function RunBlockTool({ part }: Props) { {Object.keys( output.setup_info.user_readiness.missing_credentials ?? {}, ).length > 0 && ( -
-

- Missing credentials -

-
    - {Object.entries( - output.setup_info.user_readiness.missing_credentials ?? - {}, - ).map(([field, cred]) => ( -
  • - {cred.title} ({cred.provider}) -
  • - ))} -
-
+ ({ + provider: cred.provider, + providerName: + cred.provider_name ?? cred.provider.replace(/_/g, " "), + credentialTypes: (cred.types ?? [cred.type]) as Array< + "api_key" | "oauth2" | "user_password" | "host_scoped" + >, + title: cred.title, + scopes: cred.scopes, + }))} + agentName={output.setup_info.agent_name} + message={output.message} + onAllCredentialsComplete={handleAllCredentialsComplete} + onCancel={() => {}} + /> )} {output.setup_info.requirements.inputs?.length > 0 && ( diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/helpers.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/helpers.tsx index 7b73eb860b..bf97031dbf 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/helpers.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/RunBlock/helpers.tsx @@ -13,8 +13,11 @@ export interface RunBlockInput { export interface CredentialsMeta { id: string; provider: string; + provider_name?: string; type: string; + types?: string[]; title: string; + scopes?: string[]; } export interface SetupInfo {