import { defineAction } from "@agent-native/core"; import { getWorkspaceAppIdValidationError, normalizeWorkspaceAppId, } from "@agent-native/core/shared"; import { z } from "zod"; import { isLocalAppCreationRuntime, scaffoldWorkspaceAppFromTemplate, startWorkspaceAppCreation, } from "../server/lib/app-creation-store.js"; import { getCuratedWorkspaceTemplate } from "../server/lib/curated-workspace-templates.js"; import { recordAudit } from "../server/lib/dispatch-store.js"; function buildRemixPrompt(input: { templateName: string; templateId: string; setupNote: string; description?: string | null; }): string { return [ "Create a private workspace app from a curated first-party template.", `Source template: ${input.templateName} (${input.templateId}).`, "Recreate the source template's product shape and capabilities as an independent workspace app.", "Never copy source-app data, records, user content, secrets, credentials, tokens, API keys, or private configuration.", "Use empty or synthetic seed data only, keep the new app private to the current workspace, and do not create a public demo.", `Setup note: ${input.setupNote}`, input.description?.trim() ? `Requested customization: ${input.description.trim()}` : "Keep the first version close to the curated source template while preserving independent ownership.", ].join(" "); } export default defineAction({ description: "Create a private, independent app from one of Dispatch's curated first-party workspace templates. Valid templates are mail, calendar, analytics, slides, content, clips, brain, assets, forms, and design. The new app must not copy source-app data, secrets, credentials, or private configuration; local workspaces scaffold the template, while hosted workspaces start a Builder app-creation branch.", schema: z.object({ templateId: z .string() .min(1) .describe( "Curated source template id: mail, calendar, analytics, slides, content, clips, brain, assets, forms, or design.", ), appId: z .string() .max(64) .transform(normalizeWorkspaceAppId) .optional() .nullable() .refine( (appId) => appId == null || !getWorkspaceAppIdValidationError(appId), { message: "Use a non-reserved app id with lowercase letters, numbers, and hyphens.", }, ) .describe( "Optional target workspace app id or human-friendly name. Names are converted to lowercase, hyphenated ids. Defaults to