Skip to content

Commit aa4d5e8

Browse files
authored
fix(agent-builder): prefill seeded prompt instead of auto-sending
Opening the Agent Builder dock via a contextual action ("New agent", "Edit configuration", etc.) used to auto-send the seeded prompt, so the sidebar started a chat on its own. Instead, prefill the composer with the seeded prompt and let the user review and hit send. Since prefilling is non-destructive, the start-fresh-vs-continue confirmation dialog is no longer needed and is removed. Generated-By: PostHog Code Task-Id: fc2c5587-1464-4cb1-9116-0e7a34f70734
1 parent 73b695c commit aa4d5e8

4 files changed

Lines changed: 43 additions & 96 deletions

File tree

packages/ui/src/features/agent-applications/agent-builder/AgentBuilderDock.tsx

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { useAgentChatPendingApproval } from "../hooks/useAgentChatPendingApprova
2323
import { agentIngressBaseUrl } from "../utils/ingress";
2424
import { AgentBuilderMcpConnectDialog } from "./AgentBuilderMcpConnectDialog";
2525
import { AgentBuilderSecretForm } from "./AgentBuilderSecretForm";
26-
import { AgentBuilderSeedDialog } from "./AgentBuilderSeedDialog";
2726
import {
2827
AGENT_BUILDER_CHAT_ID,
2928
AGENT_BUILDER_SLUG,
@@ -322,37 +321,22 @@ export function AgentBuilderDock() {
322321
setPendingMcpConnect(null);
323322
}
324323

325-
// Edit-with-AI hand-offs: send the seeded prompt once when a new seed lands.
326-
// An empty dock starts immediately; if a chat is already in progress, confirm
327-
// whether to start fresh or continue (so a deliberate "New agent" / "Edit with
328-
// AI" doesn't silently wipe or append onto an unrelated conversation).
324+
// Contextual hand-offs ("New agent" / "Edit with AI" / …): prefill the
325+
// seeded prompt into the composer when a new seed lands — never send it. The
326+
// user reviews and hits send, so opening the dock doesn't fire a chat on its
327+
// own. Prefilling is non-destructive, so no start-fresh-vs-continue prompt is
328+
// needed: it drops into whatever chat is open, and the header "New chat" (+)
329+
// clears first if the user wants a fresh conversation.
329330
const lastSeedRef = useRef(0);
330-
const [seedConfirm, setSeedConfirm] = useState<string | null>(null);
331+
const [draft, setDraft] = useState<{ text: string; token: number } | null>(
332+
null,
333+
);
331334
useEffect(() => {
332335
if (!seed || seed.seq === lastSeedRef.current) return;
333336
lastSeedRef.current = seed.seq;
334337
consumeSeed(seed.seq);
335-
if (chat.messages.length === 0) {
336-
chat.send(seed.prompt);
337-
} else {
338-
setSeedConfirm(seed.prompt);
339-
}
340-
}, [seed, chat, consumeSeed]);
341-
342-
function seedStartFresh() {
343-
if (!seedConfirm) return;
344-
setPendingSecret(null);
345-
setPendingMcpConnect(null);
346-
chat.newChat();
347-
setLastSession(null);
348-
chat.send(seedConfirm);
349-
setSeedConfirm(null);
350-
}
351-
function seedContinue() {
352-
if (!seedConfirm) return;
353-
chat.send(seedConfirm);
354-
setSeedConfirm(null);
355-
}
338+
setDraft({ text: seed.prompt, token: seed.seq });
339+
}, [seed, consumeSeed]);
356340

357341
return (
358342
<Flex
@@ -432,6 +416,7 @@ export function AgentBuilderDock() {
432416
placeholder={placeholder}
433417
emptyState={<AgentBuilderEmptyState page={page} onPick={chat.send} />}
434418
emptyHint="Ask the agent builder to inspect, debug, or edit your agents. It can see what you're looking at and walk you there."
419+
draft={draft ?? undefined}
435420
belowConversation={
436421
pendingApproval ? (
437422
<AgentChatPendingApprovalCard
@@ -459,14 +444,6 @@ export function AgentBuilderDock() {
459444
/>
460445
)}
461446

462-
<AgentBuilderSeedDialog
463-
open={seedConfirm != null}
464-
prompt={seedConfirm ?? ""}
465-
onStartFresh={seedStartFresh}
466-
onContinue={seedContinue}
467-
onCancel={() => setSeedConfirm(null)}
468-
/>
469-
470447
<AgentBuilderMcpConnectDialog
471448
pending={pendingMcpConnect}
472449
busy={mcpConnectBusy}

packages/ui/src/features/agent-applications/agent-builder/AgentBuilderSeedDialog.tsx

Lines changed: 0 additions & 58 deletions
This file was deleted.

packages/ui/src/features/agent-applications/agent-builder/agentBuilderStore.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export type AgentBuilderPageContext =
2727
| { kind: "agent-chat"; slug: string }
2828
| { kind: "unknown" };
2929

30-
/** A pending "Edit with AI" hand-off: open the dock and send `prompt`. */
30+
/** A pending contextual hand-off: open the dock and prefill the composer with
31+
* `prompt` (the user reviews and sends — it is never auto-sent). */
3132
export interface AgentBuilderSeed {
3233
/** Monotonic id so a consumer can mark exactly one seed handled. */
3334
seq: number;
@@ -113,7 +114,7 @@ interface AgentBuilderStore {
113114
setVisible: (visible: boolean) => void;
114115
setFollowMode: (followMode: boolean) => void;
115116
setPage: (page: AgentBuilderPageContext) => void;
116-
/** Open the dock and queue a prompt to send. */
117+
/** Open the dock and prefill the composer with a prompt (not sent). */
117118
startAgentBuilder: (prompt: string, agentSlug?: string | null) => void;
118119
/** Mark a seed handled (no-op if a newer seed has since replaced it). */
119120
consumeSeed: (seq: number) => void;

packages/ui/src/features/agent-applications/components/AgentChatSurface.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import {
88
import type { AcpMessage } from "@posthog/shared";
99
import { ThreadView } from "@posthog/ui/features/sessions/components/ThreadView";
1010
import { Flex, Text, Tooltip } from "@radix-ui/themes";
11-
import { type KeyboardEvent, type ReactNode, useState } from "react";
11+
import {
12+
type KeyboardEvent,
13+
type ReactNode,
14+
useEffect,
15+
useRef,
16+
useState,
17+
} from "react";
1218

1319
/**
1420
* The conversation + composer half of a deployed-agent chat, shared by the
@@ -28,6 +34,7 @@ export function AgentChatSurface({
2834
composerDisabledReason,
2935
scrollX = true,
3036
placeholder = "Message this agent…",
37+
draft,
3138
onSend,
3239
onCancel,
3340
}: {
@@ -50,6 +57,10 @@ export function AgentChatSurface({
5057
scrollX?: boolean;
5158
/** Composer placeholder. */
5259
placeholder?: string;
60+
/** When set, prefills the composer with `text` (without sending). Bump
61+
* `token` each time a new draft should repopulate the input — the same text
62+
* re-applies on a fresh token so re-triggering a seeded prompt works. */
63+
draft?: { text: string; token: number };
5364
onSend: (text: string) => void;
5465
onCancel: () => void;
5566
}) {
@@ -84,6 +95,7 @@ export function AgentChatSurface({
8495
isStreaming={isStreaming}
8596
placeholder={placeholder}
8697
disabledReason={composerDisabledReason}
98+
draft={draft}
8799
onSend={onSend}
88100
onCancel={onCancel}
89101
/>
@@ -95,17 +107,31 @@ function Composer({
95107
isStreaming,
96108
placeholder,
97109
disabledReason,
110+
draft,
98111
onSend,
99112
onCancel,
100113
}: {
101114
isStreaming: boolean;
102115
placeholder: string;
103116
disabledReason?: string;
117+
draft?: { text: string; token: number };
104118
onSend: (text: string) => void;
105119
onCancel: () => void;
106120
}) {
107121
const [text, setText] = useState("");
108122
const parked = !!disabledReason;
123+
const textareaRef = useRef<HTMLTextAreaElement>(null);
124+
125+
// Prefill (but don't send) when a new draft lands — a seeded prompt drops into
126+
// the composer for the user to review, edit, and send. Keyed on `token` so the
127+
// same prompt re-applies on a fresh trigger.
128+
const lastDraftToken = useRef<number | null>(null);
129+
useEffect(() => {
130+
if (!draft || draft.token === lastDraftToken.current) return;
131+
lastDraftToken.current = draft.token;
132+
setText(draft.text);
133+
textareaRef.current?.focus();
134+
}, [draft]);
109135

110136
function submit() {
111137
if (parked) return;
@@ -132,6 +158,7 @@ function Composer({
132158
<div className="shrink-0 px-3 pt-2 pb-3">
133159
<InputGroup className="h-auto cursor-text bg-card focus-within:ring-1 focus-within:ring-purple-9">
134160
<InputGroupTextarea
161+
ref={textareaRef}
135162
value={text}
136163
onChange={(e) => setText(e.target.value)}
137164
onKeyDown={onKeyDown}

0 commit comments

Comments
 (0)