Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,58 +1,116 @@
import { NavigationArrowIcon, SparkleIcon } from "@phosphor-icons/react";
import { agentChatStore } from "@posthog/core/agent-chat/agentChatStore";
import { SidebarSimpleIcon, SparkleIcon } from "@phosphor-icons/react";
import {
Button,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@posthog/quill";
import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag";
import { Button } from "@posthog/ui/primitives/Button";
import { Badge, Flex, Tooltip } from "@radix-ui/themes";
import { useStore } from "zustand";
import { Flex } from "@radix-ui/themes";
import { AGENT_PLATFORM_FLAG } from "../featureFlag";
import {
AGENT_BUILDER_CHAT_ID,
useAgentBuilderStore,
} from "./agentBuilderStore";
import { headerActionForPage } from "./agentBuilderActions";
import { useAgentBuilderStore } from "./agentBuilderStore";

/**
* The agents-header control cluster — identical across every agents view.
* Authoring lives entirely inside the dock (there is no native "new agent"
* form), so the only header affordance is the dock toggle itself, plus a
* "Following" indicator while the builder is mid-turn with follow mode on.
*
* One split button is the single entry point into the Agent Builder dock:
* - the primary segment is the contextual "edit with AI" action for the view
* you're on (New agent / Edit configuration / Explain this session / …) — it
* opens the dock and seeds the matching prompt,
* - the trailing segment just opens/closes the dock without seeding, so you
* can peek at or dismiss the existing conversation.
* The two were previously near-identical gold buttons; fusing them keeps both
* affordances but with one sparkle (the AI identity) and one neutral toggle.
* Views with no obvious action (Scouts) collapse to the lone open/close toggle.
* Renders nothing unless the `agent-platform` flag is on.
*/
export function AgentBuilderHeaderControls() {
const enabled = useFeatureFlag(AGENT_PLATFORM_FLAG);
const visible = useAgentBuilderStore((s) => s.visible);
const setVisible = useAgentBuilderStore((s) => s.setVisible);
const followMode = useAgentBuilderStore((s) => s.followMode);
const status = useStore(
agentChatStore,
(s) => s.chats[AGENT_BUILDER_CHAT_ID]?.status,
);
const page = useAgentBuilderStore((s) => s.page);
const toggleVisible = useAgentBuilderStore((s) => s.toggleVisible);
const startAgentBuilder = useAgentBuilderStore((s) => s.startAgentBuilder);

if (!enabled) return null;

const running = status === "streaming" || status === "starting";
const action = headerActionForPage(page);
const toggleTip = visible
? "Hide the agent builder (⌘⇧I)"
: "Open the agent builder (⌘⇧I)";

return (
<Flex align="center" gap="2" className="shrink-0">
{running && followMode ? (
<Tooltip content="The agent builder is navigating this view">
<Badge color="purple" variant="soft" size="1">
<NavigationArrowIcon size={11} weight="fill" />
Following
</Badge>
</Tooltip>
) : null}
{!visible ? (
<Tooltip content="Open the agent builder (⌘⇧I)">
<Button
variant="outline"
size="1"
onClick={() => setVisible(true)}
aria-label="Open agent builder"
>
<SparkleIcon size={14} weight="fill" />
</Button>
</Tooltip>
) : null}
</Flex>
<TooltipProvider delay={500}>
<Flex align="center" gap="2" className="shrink-0">
{action ? (
<div className="flex items-center">
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="sm"
className="rounded-s-[3px] rounded-e-none"
onClick={() =>
startAgentBuilder(action.prompt, action.agentSlug)
}
>
<SparkleIcon
size={14}
weight="fill"
className="text-(--accent-9)"
/>
{action.label}
</Button>
}
/>
<TooltipContent side="top">
Open the agent builder and start here
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="icon-sm"
className="rounded-s-none rounded-e-[3px] border-s-0"
aria-label={toggleTip}
onClick={toggleVisible}
>
<SidebarSimpleIcon
size={14}
weight={visible ? "fill" : "regular"}
/>
</Button>
}
/>
<TooltipContent side="top">{toggleTip}</TooltipContent>
</Tooltip>
</div>
) : (
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="icon-sm"
aria-label={toggleTip}
onClick={toggleVisible}
>
<SparkleIcon
size={14}
weight="fill"
className="text-(--accent-9)"
/>
</Button>
}
/>
<TooltipContent side="top">{toggleTip}</TooltipContent>
</Tooltip>
)}
</Flex>
</TooltipProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { AgentBuilderPageContext } from "./agentBuilderStore";

export interface AgentBuilderAction {
/** Short button label, e.g. "New agent" / "Explain this session". */
label: string;
/** Seed prompt sent to the agent builder when clicked. */
prompt: string;
/** Subject agent for the context envelope (null for fleet-level actions). */
agentSlug: string | null;
}

/**
* The contextual agent-builder action for a given view — the AI button's
* content. Drives the abstract header controls so every agents view gets a
* button that fits what you're looking at. Returns null for views with no
* obvious action (just the show/following affordances remain).
*/
export function headerActionForPage(
page: AgentBuilderPageContext,
): AgentBuilderAction | null {
switch (page.kind) {
case "agent-list":
return {
label: "New agent",
prompt:
"Help me create a new agent — walk me through what it should do, then set it up.",
agentSlug: null,
};
case "agent":
return {
label: "Explain this agent",
prompt: "Explain what this agent does and how it's configured.",
agentSlug: page.slug,
};
Comment thread
dmarticus marked this conversation as resolved.
case "agent-config":
return {
label: "Edit configuration",
prompt: "Help me change this agent's configuration.",
agentSlug: page.slug,
};
case "agent-sessions":
return {
label: "Review sessions",
prompt:
"Review this agent's recent sessions and surface anything notable.",
agentSlug: page.slug,
};
case "agent-session":
return {
label: "Explain this session",
prompt: "Explain what happened in this session, step by step.",
agentSlug: page.slug,
};
case "agent-approvals":
return {
label: "Review approvals",
prompt: "Review the pending approval requests for this agent.",
agentSlug: page.slug,
};
case "agent-memory":
return {
label: "Ask about memory",
prompt: "Summarize what's stored in this agent's memory.",
agentSlug: page.slug,
};
case "agent-observability":
return {
label: "Ask about performance",
prompt:
"Summarize this agent's spend, volume, and failure rate, and call out anything notable.",
agentSlug: page.slug,
};
default:
return null;
}
}
Loading