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
15 changes: 10 additions & 5 deletions packages/ui/src/features/loops/hooks/useLoopBuilderTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -30,13 +30,18 @@ export function useLoopBuilderTask(context?: {

const buildInput = useCallback(
(ctx: InboxCloudTaskInputContext): TaskCreationInput => {
const prompt = buildLoopBuilderPrompt({
instructions: instructionsRef.current,
const userPrompt = instructionsRef.current.trim();
const hasSeed = !!userPrompt;
const systemInstructions = buildLoopBuilderSystemInstructions({
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: prompt,
taskDescription: prompt,
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
// sandbox skips the clone and isn't tied to some arbitrary default repo.
Expand Down
19 changes: 17 additions & 2 deletions packages/ui/src/features/loops/loopBuilderPrompt.test.ts
Original file line number Diff line number Diff line change
@@ -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" },
Expand Down
19 changes: 17 additions & 2 deletions packages/ui/src/features/loops/loopBuilderPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading