From 3234a1371bafa64b3210393054e5767863323038 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Wed, 22 Jul 2026 11:26:40 -0400 Subject: [PATCH 1/2] fix: hide loop builder instructions from chat Generated-By: PostHog Code Task-Id: 4ede3282-1f33-48ee-bf6c-fe3329ef80b8 --- .../loops/hooks/useLoopBuilderTask.ts | 12 +++++++----- .../features/loops/loopBuilderPrompt.test.ts | 19 +++++++++++++++++-- .../src/features/loops/loopBuilderPrompt.ts | 19 +++++++++++++++++-- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts b/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts index 987ab6b79a..7506a0624b 100644 --- a/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts +++ b/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts @@ -4,7 +4,7 @@ import { useInboxCloudTaskRunner, } from "@posthog/ui/features/inbox/hooks/useInboxCloudTaskRunner"; import { useCallback, useMemo, useRef } from "react"; -import { buildLoopBuilderPrompt } from "../loopBuilderPrompt"; +import { buildLoopBuilderSystemInstructions } from "../loopBuilderPrompt"; interface UseLoopBuilderTaskReturn { /** Start an auto-mode cloud session that builds a loop from `instructions` and navigate to it. */ @@ -30,13 +30,15 @@ export function useLoopBuilderTask(context?: { const buildInput = useCallback( (ctx: InboxCloudTaskInputContext): TaskCreationInput => { - const prompt = buildLoopBuilderPrompt({ - instructions: instructionsRef.current, + const userPrompt = instructionsRef.current.trim(); + const systemInstructions = buildLoopBuilderSystemInstructions({ + hasSeed: !!userPrompt, context: contextRef.current, }); return { - content: prompt, - taskDescription: prompt, + content: userPrompt, + taskDescription: userPrompt, + customInstructions: systemInstructions, // Building a loop is pure PostHog-MCP work (loops-list, integrations-list, // loops-create); it never touches a working tree. Run repo-less so the // sandbox skips the clone and isn't tied to some arbitrary default repo. diff --git a/packages/ui/src/features/loops/loopBuilderPrompt.test.ts b/packages/ui/src/features/loops/loopBuilderPrompt.test.ts index 8575f7a5f1..47cdaace6b 100644 --- a/packages/ui/src/features/loops/loopBuilderPrompt.test.ts +++ b/packages/ui/src/features/loops/loopBuilderPrompt.test.ts @@ -1,17 +1,32 @@ import { describe, expect, it } from "vitest"; -import { buildLoopBuilderPrompt } from "./loopBuilderPrompt"; +import { + buildLoopBuilderPrompt, + buildLoopBuilderSystemInstructions, +} from "./loopBuilderPrompt"; describe("buildLoopBuilderPrompt", () => { it("embeds the seed instructions when provided", () => { const prompt = buildLoopBuilderPrompt({ instructions: "Summarize failing CI runs", }); + expect(prompt).toContain("Summarize failing CI runs"); expect(prompt).toContain( - "Here's what I want automated:\n\nSummarize failing CI runs", + "The user's message describes what they want automated.", ); expect(prompt).not.toContain("Start by asking me"); }); + it("keeps the user prompt out of system instructions", () => { + const instructions = buildLoopBuilderSystemInstructions({ + hasSeed: true, + }); + + expect(instructions).toContain( + "The user's message describes what they want automated.", + ); + expect(instructions).not.toContain("Summarize failing CI runs"); + }); + it.each([ { name: "absent", instructions: undefined }, { name: "whitespace-only", instructions: " \n" }, diff --git a/packages/ui/src/features/loops/loopBuilderPrompt.ts b/packages/ui/src/features/loops/loopBuilderPrompt.ts index e08b9ea9ad..9ac25f977d 100644 --- a/packages/ui/src/features/loops/loopBuilderPrompt.ts +++ b/packages/ui/src/features/loops/loopBuilderPrompt.ts @@ -13,13 +13,28 @@ export function buildLoopBuilderPrompt({ }): string { const seed = instructions?.trim(); + return [ + seed || undefined, + buildLoopBuilderSystemInstructions({ hasSeed: !!seed, context }), + ] + .filter((part): part is string => !!part) + .join("\n\n"); +} + +export function buildLoopBuilderSystemInstructions({ + hasSeed, + context, +}: { + hasSeed: boolean; + context?: { folderId: string; name: string }; +}): string { return `Your job in this session is to help me create a Loop for this PostHog project, then create it for me. A Loop is a named, cloud-executed agent automation: instructions the agent runs whenever a trigger fires (a schedule, a GitHub event, or an API call). Loops run unattended in a sandbox and can post results, open pull requests, and keep a context up to date. ${ - seed - ? `Here's what I want automated:\n\n${seed}\n` + hasSeed + ? "The user's message describes what they want automated.\n" : `Start by asking me what I want automated, and offer a couple of concrete ideas.\n` }${ context From c6f08789fdd8c8ccbd8684470ac1ff160957ff00 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 21:24:30 -0700 Subject: [PATCH 2/2] fall back to placeholder content when unseeded --- .../ui/src/features/loops/hooks/useLoopBuilderTask.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts b/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts index 7506a0624b..284a401976 100644 --- a/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts +++ b/packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts @@ -31,13 +31,16 @@ export function useLoopBuilderTask(context?: { const buildInput = useCallback( (ctx: InboxCloudTaskInputContext): TaskCreationInput => { const userPrompt = instructionsRef.current.trim(); + const hasSeed = !!userPrompt; const systemInstructions = buildLoopBuilderSystemInstructions({ - hasSeed: !!userPrompt, + hasSeed, context: contextRef.current, }); + // createTask rejects empty content and the saga drops customInstructions without message text + const taskContent = hasSeed ? userPrompt : "Build a loop"; return { - content: userPrompt, - taskDescription: userPrompt, + content: taskContent, + taskDescription: taskContent, customInstructions: systemInstructions, // Building a loop is pure PostHog-MCP work (loops-list, integrations-list, // loops-create); it never touches a working tree. Run repo-less so the