mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(copilot): workspace file listing fix in GenericTool
Remove all long-running tool changes, keep only the workspace file listing improvements in GenericTool.tsx (base64 decoding, rich file objects, MCP text extraction).
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
import { PlusCircleIcon } from "@phosphor-icons/react";
|
||||
import {
|
||||
ContentGrid,
|
||||
ContentHint,
|
||||
} from "../../components/ToolAccordion/AccordionContent";
|
||||
import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion";
|
||||
import { MiniGame } from "../../tools/CreateAgent/components/MiniGame/MiniGame";
|
||||
|
||||
interface Props {
|
||||
/** Whether the tool is currently streaming/executing */
|
||||
isStreaming: boolean;
|
||||
/** Custom title for the accordion (optional) */
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a mini-game while a long-running tool executes.
|
||||
* Automatically shown for tools marked as is_long_running=True in the backend.
|
||||
*/
|
||||
export function LongRunningToolDisplay({ isStreaming, title }: Props) {
|
||||
if (!isStreaming) return null;
|
||||
|
||||
const displayTitle =
|
||||
title || "This may take a few minutes. Play while you wait.";
|
||||
|
||||
return (
|
||||
<ToolAccordion
|
||||
icon={<PlusCircleIcon size={32} weight="light" />}
|
||||
title={displayTitle}
|
||||
defaultExpanded={true}
|
||||
>
|
||||
<ContentGrid>
|
||||
<MiniGame />
|
||||
<ContentHint>
|
||||
This could take a few minutes — play while you wait!
|
||||
</ContentHint>
|
||||
</ContentGrid>
|
||||
</ToolAccordion>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { getGetV2GetSessionQueryKey } from "@/app/api/__generated__/endpoints/chat/chat";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type { UIDataTypes, UIMessage, UITools } from "ai";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { convertChatSessionMessagesToUiMessages } from "../helpers/convertChatSessionToUiMessages";
|
||||
|
||||
const OPERATING_TYPES = new Set([
|
||||
"operation_started",
|
||||
"operation_pending",
|
||||
"operation_in_progress",
|
||||
]);
|
||||
|
||||
const POLL_INTERVAL_MS = 1_500;
|
||||
|
||||
/**
|
||||
* Detects whether any message contains a tool part whose output indicates
|
||||
* a long-running operation is still in progress.
|
||||
*/
|
||||
function hasOperatingTool(
|
||||
messages: UIMessage<unknown, UIDataTypes, UITools>[],
|
||||
) {
|
||||
for (const msg of messages) {
|
||||
for (const part of msg.parts) {
|
||||
if (!part.type.startsWith("tool-")) continue;
|
||||
const toolPart = part as { output?: unknown };
|
||||
if (!toolPart.output) continue;
|
||||
const output =
|
||||
typeof toolPart.output === "string"
|
||||
? safeParse(toolPart.output)
|
||||
: toolPart.output;
|
||||
if (
|
||||
output &&
|
||||
typeof output === "object" &&
|
||||
"type" in output &&
|
||||
OPERATING_TYPES.has((output as { type: string }).type)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function safeParse(value: string): unknown {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the session endpoint while any tool is in an "operating" state
|
||||
* (operation_started / operation_pending / operation_in_progress).
|
||||
*
|
||||
* When the session data shows the tool output has changed (e.g. to
|
||||
* agent_saved), it calls `setMessages` with the updated messages.
|
||||
*/
|
||||
export function useLongRunningToolPolling(
|
||||
sessionId: string | null,
|
||||
messages: UIMessage<unknown, UIDataTypes, UITools>[],
|
||||
setMessages: (
|
||||
updater: (
|
||||
prev: UIMessage<unknown, UIDataTypes, UITools>[],
|
||||
) => UIMessage<unknown, UIDataTypes, UITools>[],
|
||||
) => void,
|
||||
) {
|
||||
const queryClient = useQueryClient();
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
const stopPolling = useCallback(() => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const poll = useCallback(async () => {
|
||||
if (!sessionId) return;
|
||||
|
||||
// Invalidate the query cache so the next fetch gets fresh data
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getGetV2GetSessionQueryKey(sessionId),
|
||||
});
|
||||
|
||||
// Fetch fresh session data
|
||||
const data = queryClient.getQueryData<{
|
||||
status: number;
|
||||
data: { messages?: unknown[] };
|
||||
}>(getGetV2GetSessionQueryKey(sessionId));
|
||||
|
||||
if (data?.status !== 200 || !data.data.messages) return;
|
||||
|
||||
const freshMessages = convertChatSessionMessagesToUiMessages(
|
||||
sessionId,
|
||||
data.data.messages,
|
||||
);
|
||||
|
||||
if (!freshMessages || freshMessages.length === 0) return;
|
||||
|
||||
// Update when the long-running tool completed
|
||||
if (!hasOperatingTool(freshMessages)) {
|
||||
setMessages(() => freshMessages);
|
||||
stopPolling();
|
||||
}
|
||||
}, [sessionId, queryClient, setMessages, stopPolling]);
|
||||
|
||||
useEffect(() => {
|
||||
const shouldPoll = hasOperatingTool(messages);
|
||||
|
||||
// Always clear any previous interval first so we never leak timers
|
||||
// when the effect re-runs due to dependency changes (e.g. messages
|
||||
// updating as the LLM streams text after the tool call).
|
||||
stopPolling();
|
||||
|
||||
if (shouldPoll && sessionId) {
|
||||
intervalRef.current = setInterval(() => {
|
||||
poll();
|
||||
}, POLL_INTERVAL_MS);
|
||||
}
|
||||
|
||||
return () => {
|
||||
stopPolling();
|
||||
};
|
||||
}, [messages, sessionId, poll, stopPolling]);
|
||||
}
|
||||
@@ -10,13 +10,13 @@ import {
|
||||
import type { ToolUIPart } from "ai";
|
||||
import Image from "next/image";
|
||||
import NextLink from "next/link";
|
||||
import { LongRunningToolDisplay } from "../../components/LongRunningToolDisplay/LongRunningToolDisplay";
|
||||
import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions";
|
||||
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
|
||||
import {
|
||||
ContentCardDescription,
|
||||
ContentCodeBlock,
|
||||
ContentGrid,
|
||||
ContentHint,
|
||||
ContentMessage,
|
||||
} from "../../components/ToolAccordion/AccordionContent";
|
||||
import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion";
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
ClarifyingQuestion,
|
||||
} from "./components/ClarificationQuestionsCard";
|
||||
import sparklesImg from "./components/MiniGame/assets/sparkles.png";
|
||||
import { MiniGame } from "./components/MiniGame/MiniGame";
|
||||
import { SuggestedGoalCard } from "./components/SuggestedGoalCard";
|
||||
import {
|
||||
AccordionIcon,
|
||||
@@ -35,6 +36,9 @@ import {
|
||||
isAgentSavedOutput,
|
||||
isClarificationNeededOutput,
|
||||
isErrorOutput,
|
||||
isOperationInProgressOutput,
|
||||
isOperationPendingOutput,
|
||||
isOperationStartedOutput,
|
||||
isSuggestedGoalOutput,
|
||||
ToolIcon,
|
||||
truncateText,
|
||||
@@ -82,6 +86,18 @@ function getAccordionMeta(output: CreateAgentToolOutput) {
|
||||
expanded: true,
|
||||
};
|
||||
}
|
||||
if (
|
||||
isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output)
|
||||
) {
|
||||
return {
|
||||
icon,
|
||||
title:
|
||||
"Creating agent, this may take a few minutes. Play while you wait.",
|
||||
expanded: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
icon: (
|
||||
<WarningDiamondIcon size={32} weight="light" className="text-red-500" />
|
||||
@@ -103,10 +119,19 @@ export function CreateAgentTool({ part }: Props) {
|
||||
const isError =
|
||||
part.state === "output-error" || (!!output && isErrorOutput(output));
|
||||
|
||||
const isOperating =
|
||||
!!output &&
|
||||
(isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output));
|
||||
|
||||
const hasExpandableContent =
|
||||
part.state === "output-available" &&
|
||||
!!output &&
|
||||
(isAgentPreviewOutput(output) ||
|
||||
(isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output) ||
|
||||
isAgentPreviewOutput(output) ||
|
||||
isAgentSavedOutput(output) ||
|
||||
isClarificationNeededOutput(output) ||
|
||||
isSuggestedGoalOutput(output) ||
|
||||
@@ -144,14 +169,17 @@ export function CreateAgentTool({ part }: Props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Show mini-game while tool is executing */}
|
||||
<LongRunningToolDisplay
|
||||
isStreaming={isStreaming}
|
||||
title="Creating agent, this may take a few minutes. Play while you wait."
|
||||
/>
|
||||
|
||||
{hasExpandableContent && output && (
|
||||
<ToolAccordion {...getAccordionMeta(output)}>
|
||||
{isOperating && (
|
||||
<ContentGrid>
|
||||
<MiniGame />
|
||||
<ContentHint>
|
||||
This could take a few minutes — play while you wait!
|
||||
</ContentHint>
|
||||
</ContentGrid>
|
||||
)}
|
||||
|
||||
{isAgentSavedOutput(output) && (
|
||||
<div className="rounded-xl border border-border/60 bg-card p-4 shadow-sm">
|
||||
<div className="flex items-baseline gap-2">
|
||||
|
||||
@@ -2,6 +2,9 @@ import type { AgentPreviewResponse } from "@/app/api/__generated__/models/agentP
|
||||
import type { AgentSavedResponse } from "@/app/api/__generated__/models/agentSavedResponse";
|
||||
import type { ClarificationNeededResponse } from "@/app/api/__generated__/models/clarificationNeededResponse";
|
||||
import type { ErrorResponse } from "@/app/api/__generated__/models/errorResponse";
|
||||
import type { OperationInProgressResponse } from "@/app/api/__generated__/models/operationInProgressResponse";
|
||||
import type { OperationPendingResponse } from "@/app/api/__generated__/models/operationPendingResponse";
|
||||
import type { OperationStartedResponse } from "@/app/api/__generated__/models/operationStartedResponse";
|
||||
import { ResponseType } from "@/app/api/__generated__/models/responseType";
|
||||
import type { SuggestedGoalResponse } from "@/app/api/__generated__/models/suggestedGoalResponse";
|
||||
import {
|
||||
@@ -13,6 +16,9 @@ import type { ToolUIPart } from "ai";
|
||||
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
|
||||
|
||||
export type CreateAgentToolOutput =
|
||||
| OperationStartedResponse
|
||||
| OperationPendingResponse
|
||||
| OperationInProgressResponse
|
||||
| AgentPreviewResponse
|
||||
| AgentSavedResponse
|
||||
| ClarificationNeededResponse
|
||||
@@ -33,6 +39,9 @@ function parseOutput(output: unknown): CreateAgentToolOutput | null {
|
||||
if (typeof output === "object") {
|
||||
const type = (output as { type?: unknown }).type;
|
||||
if (
|
||||
type === ResponseType.operation_started ||
|
||||
type === ResponseType.operation_pending ||
|
||||
type === ResponseType.operation_in_progress ||
|
||||
type === ResponseType.agent_preview ||
|
||||
type === ResponseType.agent_saved ||
|
||||
type === ResponseType.clarification_needed ||
|
||||
@@ -41,6 +50,9 @@ function parseOutput(output: unknown): CreateAgentToolOutput | null {
|
||||
) {
|
||||
return output as CreateAgentToolOutput;
|
||||
}
|
||||
if ("operation_id" in output && "tool_name" in output)
|
||||
return output as OperationStartedResponse | OperationPendingResponse;
|
||||
if ("tool_call_id" in output) return output as OperationInProgressResponse;
|
||||
if ("agent_json" in output && "agent_name" in output)
|
||||
return output as AgentPreviewResponse;
|
||||
if ("agent_id" in output && "library_agent_id" in output)
|
||||
@@ -60,6 +72,30 @@ export function getCreateAgentToolOutput(
|
||||
return parseOutput((part as { output?: unknown }).output);
|
||||
}
|
||||
|
||||
export function isOperationStartedOutput(
|
||||
output: CreateAgentToolOutput,
|
||||
): output is OperationStartedResponse {
|
||||
return (
|
||||
output.type === ResponseType.operation_started ||
|
||||
("operation_id" in output && "tool_name" in output)
|
||||
);
|
||||
}
|
||||
|
||||
export function isOperationPendingOutput(
|
||||
output: CreateAgentToolOutput,
|
||||
): output is OperationPendingResponse {
|
||||
return output.type === ResponseType.operation_pending;
|
||||
}
|
||||
|
||||
export function isOperationInProgressOutput(
|
||||
output: CreateAgentToolOutput,
|
||||
): output is OperationInProgressResponse {
|
||||
return (
|
||||
output.type === ResponseType.operation_in_progress ||
|
||||
"tool_call_id" in output
|
||||
);
|
||||
}
|
||||
|
||||
export function isAgentPreviewOutput(
|
||||
output: CreateAgentToolOutput,
|
||||
): output is AgentPreviewResponse {
|
||||
@@ -108,6 +144,10 @@ export function getAnimationText(part: {
|
||||
case "output-available": {
|
||||
const output = parseOutput(part.output);
|
||||
if (!output) return "Creating a new agent";
|
||||
if (isOperationStartedOutput(output)) return "Agent creation started";
|
||||
if (isOperationPendingOutput(output)) return "Agent creation in progress";
|
||||
if (isOperationInProgressOutput(output))
|
||||
return "Agent creation already in progress";
|
||||
if (isAgentSavedOutput(output)) return `Saved ${output.agent_name}`;
|
||||
if (isAgentPreviewOutput(output)) return `Preview "${output.agent_name}"`;
|
||||
if (isClarificationNeededOutput(output)) return "Needs clarification";
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
import { WarningDiamondIcon } from "@phosphor-icons/react";
|
||||
import type { ToolUIPart } from "ai";
|
||||
import { LongRunningToolDisplay } from "../../components/LongRunningToolDisplay/LongRunningToolDisplay";
|
||||
import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions";
|
||||
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
|
||||
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
|
||||
import {
|
||||
ContentCardDescription,
|
||||
ContentCodeBlock,
|
||||
ContentGrid,
|
||||
ContentHint,
|
||||
ContentLink,
|
||||
ContentMessage,
|
||||
} from "../../components/ToolAccordion/AccordionContent";
|
||||
import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion";
|
||||
import { MiniGame } from "../CreateAgent/components/MiniGame/MiniGame";
|
||||
import {
|
||||
ClarificationQuestionsCard,
|
||||
ClarifyingQuestion,
|
||||
@@ -26,6 +28,9 @@ import {
|
||||
isAgentSavedOutput,
|
||||
isClarificationNeededOutput,
|
||||
isErrorOutput,
|
||||
isOperationInProgressOutput,
|
||||
isOperationPendingOutput,
|
||||
isOperationStartedOutput,
|
||||
ToolIcon,
|
||||
truncateText,
|
||||
type EditAgentToolOutput,
|
||||
@@ -70,6 +75,17 @@ function getAccordionMeta(output: EditAgentToolOutput): {
|
||||
description: `${questions.length} question${questions.length === 1 ? "" : "s"}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output)
|
||||
) {
|
||||
return {
|
||||
icon: <OrbitLoader size={32} />,
|
||||
title: "Editing agent, this may take a few minutes. Play while you wait.",
|
||||
expanded: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
icon: (
|
||||
<WarningDiamondIcon size={32} weight="light" className="text-red-500" />
|
||||
@@ -88,10 +104,18 @@ export function EditAgentTool({ part }: Props) {
|
||||
const output = getEditAgentToolOutput(part);
|
||||
const isError =
|
||||
part.state === "output-error" || (!!output && isErrorOutput(output));
|
||||
const isOperating =
|
||||
!!output &&
|
||||
(isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output));
|
||||
const hasExpandableContent =
|
||||
part.state === "output-available" &&
|
||||
!!output &&
|
||||
(isAgentPreviewOutput(output) ||
|
||||
(isOperationStartedOutput(output) ||
|
||||
isOperationPendingOutput(output) ||
|
||||
isOperationInProgressOutput(output) ||
|
||||
isAgentPreviewOutput(output) ||
|
||||
isAgentSavedOutput(output) ||
|
||||
isClarificationNeededOutput(output) ||
|
||||
isErrorOutput(output));
|
||||
@@ -124,14 +148,17 @@ export function EditAgentTool({ part }: Props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Show mini-game while tool is executing */}
|
||||
<LongRunningToolDisplay
|
||||
isStreaming={isStreaming}
|
||||
title="Editing agent, this may take a few minutes. Play while you wait."
|
||||
/>
|
||||
|
||||
{hasExpandableContent && output && (
|
||||
<ToolAccordion {...getAccordionMeta(output)}>
|
||||
{isOperating && (
|
||||
<ContentGrid>
|
||||
<MiniGame />
|
||||
<ContentHint>
|
||||
This could take a few minutes — play while you wait!
|
||||
</ContentHint>
|
||||
</ContentGrid>
|
||||
)}
|
||||
|
||||
{isAgentSavedOutput(output) && (
|
||||
<ContentGrid>
|
||||
<ContentMessage>{output.message}</ContentMessage>
|
||||
|
||||
@@ -2,6 +2,9 @@ import type { AgentPreviewResponse } from "@/app/api/__generated__/models/agentP
|
||||
import type { AgentSavedResponse } from "@/app/api/__generated__/models/agentSavedResponse";
|
||||
import type { ClarificationNeededResponse } from "@/app/api/__generated__/models/clarificationNeededResponse";
|
||||
import type { ErrorResponse } from "@/app/api/__generated__/models/errorResponse";
|
||||
import type { OperationInProgressResponse } from "@/app/api/__generated__/models/operationInProgressResponse";
|
||||
import type { OperationPendingResponse } from "@/app/api/__generated__/models/operationPendingResponse";
|
||||
import type { OperationStartedResponse } from "@/app/api/__generated__/models/operationStartedResponse";
|
||||
import { ResponseType } from "@/app/api/__generated__/models/responseType";
|
||||
import {
|
||||
NotePencilIcon,
|
||||
@@ -12,6 +15,9 @@ import type { ToolUIPart } from "ai";
|
||||
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
|
||||
|
||||
export type EditAgentToolOutput =
|
||||
| OperationStartedResponse
|
||||
| OperationPendingResponse
|
||||
| OperationInProgressResponse
|
||||
| AgentPreviewResponse
|
||||
| AgentSavedResponse
|
||||
| ClarificationNeededResponse
|
||||
@@ -31,6 +37,9 @@ function parseOutput(output: unknown): EditAgentToolOutput | null {
|
||||
if (typeof output === "object") {
|
||||
const type = (output as { type?: unknown }).type;
|
||||
if (
|
||||
type === ResponseType.operation_started ||
|
||||
type === ResponseType.operation_pending ||
|
||||
type === ResponseType.operation_in_progress ||
|
||||
type === ResponseType.agent_preview ||
|
||||
type === ResponseType.agent_saved ||
|
||||
type === ResponseType.clarification_needed ||
|
||||
@@ -38,6 +47,9 @@ function parseOutput(output: unknown): EditAgentToolOutput | null {
|
||||
) {
|
||||
return output as EditAgentToolOutput;
|
||||
}
|
||||
if ("operation_id" in output && "tool_name" in output)
|
||||
return output as OperationStartedResponse | OperationPendingResponse;
|
||||
if ("tool_call_id" in output) return output as OperationInProgressResponse;
|
||||
if ("agent_json" in output && "agent_name" in output)
|
||||
return output as AgentPreviewResponse;
|
||||
if ("agent_id" in output && "library_agent_id" in output)
|
||||
@@ -56,6 +68,30 @@ export function getEditAgentToolOutput(
|
||||
return parseOutput((part as { output?: unknown }).output);
|
||||
}
|
||||
|
||||
export function isOperationStartedOutput(
|
||||
output: EditAgentToolOutput,
|
||||
): output is OperationStartedResponse {
|
||||
return (
|
||||
output.type === ResponseType.operation_started ||
|
||||
("operation_id" in output && "tool_name" in output)
|
||||
);
|
||||
}
|
||||
|
||||
export function isOperationPendingOutput(
|
||||
output: EditAgentToolOutput,
|
||||
): output is OperationPendingResponse {
|
||||
return output.type === ResponseType.operation_pending;
|
||||
}
|
||||
|
||||
export function isOperationInProgressOutput(
|
||||
output: EditAgentToolOutput,
|
||||
): output is OperationInProgressResponse {
|
||||
return (
|
||||
output.type === ResponseType.operation_in_progress ||
|
||||
"tool_call_id" in output
|
||||
);
|
||||
}
|
||||
|
||||
export function isAgentPreviewOutput(
|
||||
output: EditAgentToolOutput,
|
||||
): output is AgentPreviewResponse {
|
||||
@@ -96,6 +132,10 @@ export function getAnimationText(part: {
|
||||
case "output-available": {
|
||||
const output = parseOutput(part.output);
|
||||
if (!output) return "Editing the agent";
|
||||
if (isOperationStartedOutput(output)) return "Agent update started";
|
||||
if (isOperationPendingOutput(output)) return "Agent update in progress";
|
||||
if (isOperationInProgressOutput(output))
|
||||
return "Agent update already in progress";
|
||||
if (isAgentSavedOutput(output)) return `Saved "${output.agent_name}"`;
|
||||
if (isAgentPreviewOutput(output)) return `Preview "${output.agent_name}"`;
|
||||
if (isClarificationNeededOutput(output)) return "Needs clarification";
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Tools that take a long time to execute (several minutes).
|
||||
* These tools will automatically show the mini-game while executing.
|
||||
*
|
||||
* This list matches the backend tools where is_long_running=True.
|
||||
*/
|
||||
export const LONG_RUNNING_TOOLS = [
|
||||
"create_agent",
|
||||
"edit_agent",
|
||||
"customize_agent",
|
||||
] as const;
|
||||
|
||||
export type LongRunningToolName = (typeof LONG_RUNNING_TOOLS)[number];
|
||||
|
||||
/**
|
||||
* Check if a tool is marked as long-running.
|
||||
*/
|
||||
export function isLongRunningTool(toolName: string): boolean {
|
||||
return LONG_RUNNING_TOOLS.includes(toolName as LongRunningToolName);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { DefaultChatTransport } from "ai";
|
||||
import type { UIMessage } from "ai";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useChatSession } from "./useChatSession";
|
||||
import { useLongRunningToolPolling } from "./hooks/useLongRunningToolPolling";
|
||||
|
||||
const STREAM_START_TIMEOUT_MS = 12_000;
|
||||
|
||||
@@ -226,6 +227,11 @@ export function useCopilotPage() {
|
||||
resumeStream();
|
||||
}, [hasActiveStream, sessionId, hydratedMessages, status, resumeStream]);
|
||||
|
||||
// Poll session endpoint when a long-running tool (create_agent, edit_agent)
|
||||
// is in progress. When the backend completes, the session data will contain
|
||||
// the final tool output — this hook detects the change and updates messages.
|
||||
useLongRunningToolPolling(sessionId, messages, setMessages);
|
||||
|
||||
// Clear messages when session is null
|
||||
useEffect(() => {
|
||||
if (!sessionId) setMessages([]);
|
||||
|
||||
Reference in New Issue
Block a user