From 72f3e5a45ee9e21c704b59cffa8aee7924a4b772 Mon Sep 17 00:00:00 2001 From: Adam Bowker Date: Mon, 20 Jul 2026 15:34:35 -0400 Subject: [PATCH] refactor(canvas): move freeform authoring contract to @posthog/shared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relocate the freeform canvas authoring contract — the system-prompt builder (freeformSystemPromptFor), the import whitelist, the starter scaffold, and the freeform template id — from @posthog/core into @posthog/shared, so @posthog/agent can consume it (for the canvas_checkout tool) without a core↔agent package cycle. Pure move: @posthog/core/canvas/{canvasTemplates,freeformSchemas,freeformWhitelist, freeformStarter} now re-export from @posthog/shared, so existing consumers are unchanged. Generated-By: PostHog Code Task-Id: 2ed89933-091b-446c-af91-5cc72d939b3b --- packages/core/src/canvas/canvasTemplates.ts | 162 +---------------- .../core/src/canvas/canvasTemplatesService.ts | 7 +- packages/core/src/canvas/dashboardsService.ts | 3 +- packages/core/src/canvas/freeformSchemas.ts | 1 - packages/shared/package.json | 12 ++ packages/shared/src/canvas-freeform-prompt.ts | 163 ++++++++++++++++++ .../src/canvas-freeform-starter.ts} | 0 .../src/canvas-freeform-whitelist.test.ts} | 2 +- .../src/canvas-freeform-whitelist.ts} | 0 packages/shared/tsup.config.ts | 3 + .../canvas/components/canvasTemplateIcon.tsx | 2 +- .../canvas/freeform/sandboxRuntime.ts | 2 +- .../ui/src/features/canvas/freeformPrompt.ts | 4 +- 13 files changed, 191 insertions(+), 170 deletions(-) create mode 100644 packages/shared/src/canvas-freeform-prompt.ts rename packages/{core/src/canvas/freeformStarter.ts => shared/src/canvas-freeform-starter.ts} (100%) rename packages/{core/src/canvas/freeformWhitelist.test.ts => shared/src/canvas-freeform-whitelist.test.ts} (98%) rename packages/{core/src/canvas/freeformWhitelist.ts => shared/src/canvas-freeform-whitelist.ts} (100%) diff --git a/packages/core/src/canvas/canvasTemplates.ts b/packages/core/src/canvas/canvasTemplates.ts index d75cfba72b..f42e240c7e 100644 --- a/packages/core/src/canvas/canvasTemplates.ts +++ b/packages/core/src/canvas/canvasTemplates.ts @@ -1,5 +1,7 @@ -import { FREEFORM_TEMPLATE_ID } from "./freeformSchemas"; -import { FREEFORM_WHITELIST } from "./freeformWhitelist"; +import { + FREEFORM_SYSTEM_PROMPT, + FREEFORM_TEMPLATE_ID, +} from "@posthog/shared/canvas-freeform-prompt"; import type { CanvasSuggestion } from "./templateSchemas"; export interface CanvasTemplate { @@ -13,162 +15,6 @@ export interface CanvasTemplate { systemPrompt: string; } -// Freeform React canvas (Q1/Q12): the agent writes a real single-file React app -// that runs in a sandboxed iframe, instead of emitting json-render patches. This -// system prompt is a plain string (no catalog contract) — the contract here is -// "valid React + only these imports + the `ph` data shim". -const FREEFORM_WHITELIST_NAMES = FREEFORM_WHITELIST.map((e) => e.name).join( - ", ", -); - -// The shared React-tier contract: output format, the import whitelist, and the -// `ph` data shim. Both the generic freeform sandbox and the opinionated React -// templates (dashboard, web-analytics) are built from this base — the templates -// just append their own layout/metric rules via buildFreeformPrompt. -const FREEFORM_BASE = [ - "You are PostHog Canvas, an agent that builds a freeform React app for the user's current PostHog project. The app runs in a sandboxed iframe.", - "", - "OUTPUT FORMAT — every turn:", - "- Write a SHORT sentence of prose, then the COMPLETE app as ONE fenced code block tagged tsx (```tsx ... ```).", - "- FULL-FILE REWRITE: always output the entire file, even for a tiny change. Never output a partial file, a diff, or multiple code blocks.", - "- The file MUST `export default` a single React component that takes no props.", - "", - "IMPORTS — allowed packages ONLY:", - `- You may import ONLY from: ${FREEFORM_WHITELIST_NAMES}.`, - '- Import React hooks from "react" (e.g. `import React, { useState, useEffect } from "react"`). Do NOT import react-dom or call createRoot — the host mounts your default export.', - '- Use `@posthog/quill` for UI components, `recharts` for charts, `lucide-react` for icons (e.g. `import { Calendar, RefreshCw } from "lucide-react"`), and `dayjs` for dates.', - "- FORBIDDEN: any other import, dynamic import(), require(), fetch(), XMLHttpRequest,