Compare commits

..

3 Commits

Author SHA1 Message Date
Swifty
6fe2efba3e Merge branch 'dev' into swiftyos/secrt-1954-gracefully-disable-feature-request-tools-when-linear_api_key 2026-02-16 11:55:45 +01:00
Swifty
fa66473f68 Merge branch 'dev' into swiftyos/secrt-1954-gracefully-disable-feature-request-tools-when-linear_api_key 2026-02-13 16:39:21 +01:00
Swifty
753726dc18 fix(backend): disable feature request tools when Linear config is missing
Conditionally register search_feature_requests and create_feature_request
tools only when LINEAR_API_KEY, LINEAR_FEATURE_REQUEST_PROJECT_ID, and
LINEAR_FEATURE_REQUEST_TEAM_ID are all configured. This prevents the LLM
from calling tools that will fail at runtime and avoids confusing error
messages for users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:52:15 +01:00
20 changed files with 314 additions and 806 deletions

View File

@@ -14,7 +14,6 @@ from .check_operation_status import CheckOperationStatusTool
from .create_agent import CreateAgentTool
from .customize_agent import CustomizeAgentTool
from .edit_agent import EditAgentTool
from .feature_requests import CreateFeatureRequestTool, SearchFeatureRequestsTool
from .find_agent import FindAgentTool
from .find_block import FindBlockTool
from .find_library_agent import FindLibraryAgentTool
@@ -65,6 +64,38 @@ TOOL_REGISTRY: dict[str, BaseTool] = {
"delete_workspace_file": DeleteWorkspaceFileTool(),
}
def _register_feature_request_tools() -> None:
"""Register feature request tools only if Linear configuration is available."""
from backend.util.settings import Settings
try:
secrets = Settings().secrets
except Exception:
logger.warning("Feature request tools disabled: failed to load settings")
return
if not (
secrets.linear_api_key
and secrets.linear_feature_request_project_id
and secrets.linear_feature_request_team_id
):
logger.info(
"Feature request tools disabled: LINEAR_API_KEY, "
"LINEAR_FEATURE_REQUEST_PROJECT_ID, or "
"LINEAR_FEATURE_REQUEST_TEAM_ID is not configured"
)
return
from .feature_requests import CreateFeatureRequestTool, SearchFeatureRequestsTool
TOOL_REGISTRY["search_feature_requests"] = SearchFeatureRequestsTool()
TOOL_REGISTRY["create_feature_request"] = CreateFeatureRequestTool()
logger.info("Feature request tools enabled")
_register_feature_request_tools()
# Export individual tool instances for backwards compatibility
find_agent_tool = TOOL_REGISTRY["find_agent"]
run_agent_tool = TOOL_REGISTRY["run_agent"]

View File

@@ -4,11 +4,11 @@ import { Button } from "@/components/atoms/Button/Button";
import { Text } from "@/components/atoms/Text/Text";
import {
BookOpenIcon,
CheckFatIcon,
PencilSimpleIcon,
WarningDiamondIcon,
} from "@phosphor-icons/react";
import type { ToolUIPart } from "ai";
import Image from "next/image";
import NextLink from "next/link";
import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions";
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
@@ -24,7 +24,6 @@ import {
ClarificationQuestionsCard,
ClarifyingQuestion,
} from "./components/ClarificationQuestionsCard";
import sparklesImg from "./components/MiniGame/assets/sparkles.png";
import { MiniGame } from "./components/MiniGame/MiniGame";
import {
AccordionIcon,
@@ -84,8 +83,7 @@ function getAccordionMeta(output: CreateAgentToolOutput) {
) {
return {
icon,
title:
"Creating agent, this may take a few minutes. Play while you wait.",
title: "Creating agent, this may take a few minutes. Sit back and relax.",
expanded: true,
};
}
@@ -169,20 +167,16 @@ export function CreateAgentTool({ part }: Props) {
{isAgentSavedOutput(output) && (
<div className="rounded-xl border border-border/60 bg-card p-4 shadow-sm">
<div className="flex items-baseline gap-2">
<Image
src={sparklesImg}
alt="sparkles"
width={24}
height={24}
className="relative top-1"
<CheckFatIcon
size={18}
weight="regular"
className="relative top-1 text-green-500"
/>
<Text
variant="body-medium"
className="mb-2 text-[16px] text-black"
className="text-blacks mb-2 text-[16px]"
>
Agent{" "}
<span className="text-violet-600">{output.agent_name}</span>{" "}
has been saved to your library!
{output.message}
</Text>
</div>
<div className="mt-3 flex flex-wrap gap-4">

View File

@@ -2,65 +2,20 @@
import { useMiniGame } from "./useMiniGame";
function Key({ children }: { children: React.ReactNode }) {
return <strong>[{children}]</strong>;
}
export function MiniGame() {
const { canvasRef, activeMode, showOverlay, score, highScore, onContinue } =
useMiniGame();
const isRunActive =
activeMode === "run" || activeMode === "idle" || activeMode === "over";
let overlayText: string | undefined;
let buttonLabel = "Continue";
if (activeMode === "idle") {
buttonLabel = "Start";
} else if (activeMode === "boss-intro") {
overlayText = "Face the bandit!";
} else if (activeMode === "boss-defeated") {
overlayText = "Great job, keep on going";
} else if (activeMode === "over") {
overlayText = `Score: ${score} / Record: ${highScore}`;
buttonLabel = "Retry";
}
const { canvasRef } = useMiniGame();
return (
<div className="flex flex-col gap-2">
<p className="text-sm font-medium text-purple-500">
{isRunActive ? (
<>
Run mode: <Key>Space</Key> to jump
</>
) : (
<>
Duel mode: <Key></Key> to move · <Key>Z</Key> to attack ·{" "}
<Key>X</Key> to block · <Key>Space</Key> to jump
</>
)}
</p>
<div className="relative w-full overflow-hidden rounded-md border border-accent bg-background text-foreground">
<canvas
ref={canvasRef}
tabIndex={0}
className="block w-full outline-none"
/>
{showOverlay && (
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/40">
{overlayText && (
<p className="text-lg font-bold text-white">{overlayText}</p>
)}
<button
type="button"
onClick={onContinue}
className="rounded-md bg-white px-4 py-2 text-sm font-semibold text-zinc-800 shadow-md transition-colors hover:bg-zinc-100"
>
{buttonLabel}
</button>
</div>
)}
</div>
<div
className="w-full overflow-hidden rounded-md bg-background text-foreground"
style={{ border: "1px solid #d17fff" }}
>
<canvas
ref={canvasRef}
tabIndex={0}
className="block w-full outline-none"
style={{ imageRendering: "pixelated" }}
/>
</div>
);
}

View File

@@ -136,7 +136,7 @@ export function getAnimationText(part: {
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 (isAgentSavedOutput(output)) return `Saved "${output.agent_name}"`;
if (isAgentPreviewOutput(output)) return `Preview "${output.agent_name}"`;
if (isClarificationNeededOutput(output)) return "Needs clarification";
return "Error creating agent";

View File

@@ -5,6 +5,7 @@ import type { ToolUIPart } from "ai";
import { useCopilotChatActions } from "../../components/CopilotChatActionsProvider/useCopilotChatActions";
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
import { ProgressBar } from "../../components/ProgressBar/ProgressBar";
import {
ContentCardDescription,
ContentCodeBlock,
@@ -14,7 +15,7 @@ import {
ContentMessage,
} from "../../components/ToolAccordion/AccordionContent";
import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion";
import { MiniGame } from "../CreateAgent/components/MiniGame/MiniGame";
import { useAsymptoticProgress } from "../../hooks/useAsymptoticProgress";
import {
ClarificationQuestionsCard,
ClarifyingQuestion,
@@ -53,7 +54,6 @@ function getAccordionMeta(output: EditAgentToolOutput): {
title: string;
titleClassName?: string;
description?: string;
expanded?: boolean;
} {
const icon = <AccordionIcon />;
@@ -80,11 +80,7 @@ function getAccordionMeta(output: EditAgentToolOutput): {
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: <OrbitLoader size={32} />, title: "Editing agent" };
}
return {
icon: (
@@ -109,6 +105,7 @@ export function EditAgentTool({ part }: Props) {
(isOperationStartedOutput(output) ||
isOperationPendingOutput(output) ||
isOperationInProgressOutput(output));
const progress = useAsymptoticProgress(isOperating);
const hasExpandableContent =
part.state === "output-available" &&
!!output &&
@@ -152,9 +149,9 @@ export function EditAgentTool({ part }: Props) {
<ToolAccordion {...getAccordionMeta(output)}>
{isOperating && (
<ContentGrid>
<MiniGame />
<ProgressBar value={progress} className="max-w-[280px]" />
<ContentHint>
This could take a few minutes play while you wait!
This could take a few minutes, grab a coffee
</ContentHint>
</ContentGrid>
)}

View File

@@ -2,14 +2,8 @@
import type { ToolUIPart } from "ai";
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
import { ToolAccordion } from "../../components/ToolAccordion/ToolAccordion";
import {
ContentGrid,
ContentHint,
ContentMessage,
} from "../../components/ToolAccordion/AccordionContent";
import { MiniGame } from "../CreateAgent/components/MiniGame/MiniGame";
import { ContentMessage } from "../../components/ToolAccordion/AccordionContent";
import {
getAccordionMeta,
getAnimationText,
@@ -66,21 +60,6 @@ export function RunAgentTool({ part }: Props) {
/>
</div>
{isStreaming && !output && (
<ToolAccordion
icon={<OrbitLoader size={32} />}
title="Running agent, this may take a few minutes. Play while you wait."
expanded={true}
>
<ContentGrid>
<MiniGame />
<ContentHint>
This could take a few minutes play while you wait!
</ContentHint>
</ContentGrid>
</ToolAccordion>
)}
{hasExpandableContent && output && (
<ToolAccordion {...getAccordionMeta(output)}>
{isRunAgentExecutionStartedOutput(output) && (

View File

@@ -1,4 +0,0 @@
declare module "*.png" {
const content: import("next/image").StaticImageData;
export default content;
}

View File

@@ -218,17 +218,6 @@ If you initially installed Docker with Hyper-V, you **dont need to reinstall*
For more details, refer to [Docker's official documentation](https://docs.docker.com/desktop/windows/wsl/).
### ⚠️ Podman Not Supported
AutoGPT requires **Docker** (Docker Desktop or Docker Engine). **Podman and podman-compose are not supported** and may cause path resolution issues, particularly on Windows.
If you see errors like:
```text
Error: the specified Containerfile or Dockerfile does not exist, ..\..\autogpt_platform\backend\Dockerfile
```
This indicates you're using Podman instead of Docker. Please install [Docker Desktop](https://docs.docker.com/desktop/) and use `docker compose` instead of `podman-compose`.
## Development