const CODEX_ARTIFACT_TEMPLATE_KINDS = [ "document", "presentation", "spreadsheet", "site", "google-docs", "google-slides", "google-sheets", "image", "email", "slack", ] as const; export type CodexArtifactTemplateKind = (typeof CODEX_ARTIFACT_TEMPLATE_KINDS)[number]; const CODEX_ARTIFACT_TEMPLATE_GALLERY_KINDS = ["imagegen", "product-design"] as const; export type CodexArtifactTemplateGalleryKind = (typeof CODEX_ARTIFACT_TEMPLATE_GALLERY_KINDS)[number]; export interface CodexArtifactTemplate { readonly artifactKind: CodexArtifactTemplateKind; readonly displayName: string; readonly galleryKind?: CodexArtifactTemplateGalleryKind; readonly skillDirectory: string; readonly skillName: string; } const CODEX_ARTIFACT_TEMPLATE_LABEL_BY_KIND = { document: "Document template", presentation: "Presentation template", spreadsheet: "Spreadsheet template", site: "Site template", "google-docs": "Google Doc template", "google-slides": "Google Slides template", "google-sheets": "Google Sheet template", image: "Image template", email: "Email template", slack: "Slack template", } satisfies Record; export type CodexArtifactTemplateAttributes = Readonly>; const WINDOWS_DRIVE_PATH_REGEX = /^[A-Za-z]:[\\/]/; const WINDOWS_UNC_PATH_REGEX = /^(?:\\\\[^\\]+\\[^\\]+|\/\/[^/]+\/[^/]+)/; function isCodexArtifactTemplateKind(value: unknown): value is CodexArtifactTemplateKind { return CODEX_ARTIFACT_TEMPLATE_KINDS.some((kind) => kind === value); } function isCodexArtifactTemplateGalleryKind( value: unknown, ): value is CodexArtifactTemplateGalleryKind { return CODEX_ARTIFACT_TEMPLATE_GALLERY_KINDS.some((kind) => kind === value); } function isAbsoluteSkillDirectory(value: string): boolean { return ( (value.startsWith("/") && !value.startsWith("//")) || WINDOWS_DRIVE_PATH_REGEX.test(value) || WINDOWS_UNC_PATH_REGEX.test(value) ); } /** Mirrors the Codex result-card schema so malformed directives remain literal Markdown. */ export function resolveCodexArtifactTemplate( attributes: CodexArtifactTemplateAttributes | null | undefined, ): CodexArtifactTemplate | null { const artifactKind = attributes?.artifact_kind; const displayNameValue = attributes?.display_name; const displayName = typeof displayNameValue === "string" ? displayNameValue.trim() : undefined; const galleryKind = attributes?.gallery_kind; const skillDirectory = attributes?.skill_directory; const skillName = attributes?.skill_name; if ( !isCodexArtifactTemplateKind(artifactKind) || !displayName || typeof skillDirectory !== "string" || !isAbsoluteSkillDirectory(skillDirectory) || typeof skillName !== "string" || !skillName.startsWith("artifact-template-") || (galleryKind !== undefined && !isCodexArtifactTemplateGalleryKind(galleryKind)) ) { return null; } return { artifactKind, displayName, ...(galleryKind === undefined ? {} : { galleryKind }), skillDirectory, skillName, }; } const USE_PROMPT_BY_KIND: Record string> = { document: (skill) => `Create a document using this ${skill} about…`, presentation: (skill) => `Create a presentation using the ${skill} template about…`, spreadsheet: (skill) => `Create a spreadsheet using this ${skill} about…`, site: (skill) => `Create a Site using this ${skill} about…`, "google-docs": (skill) => `Create a Google Doc using this ${skill} about…`, "google-slides": (skill) => `Create a Google Slides presentation using this ${skill} about…`, "google-sheets": (skill) => `Create a Google Sheet using this ${skill} about…`, image: (skill) => `Create an image using this ${skill} of…`, email: (skill) => `Draft an email using this ${skill} about…`, slack: (skill) => `Draft a Slack message using this ${skill} about…`, }; export function codexArtifactTemplateUsePrompt(template: CodexArtifactTemplate): string { return USE_PROMPT_BY_KIND[template.artifactKind](`$${template.skillName}`); } export function codexArtifactTemplatePresentationLabel(kind: CodexArtifactTemplateKind): string { return CODEX_ARTIFACT_TEMPLATE_LABEL_BY_KIND[kind]; } export function appendCodexArtifactTemplateUsePrompt( draft: string, template: CodexArtifactTemplate, ): string { const prompt = codexArtifactTemplateUsePrompt(template); const trimmedDraft = draft.trimEnd(); const promptStart = trimmedDraft.length - prompt.length; const alreadyEndsWithPrompt = promptStart >= 0 && trimmedDraft.slice(promptStart) === prompt && (promptStart === 0 || /\s/.test(trimmedDraft[promptStart - 1] ?? "")); if (alreadyEndsWithPrompt) { return draft; } const needsLeadingSpace = draft.length > 0 && !/\s$/.test(draft); return `${draft}${needsLeadingSpace ? " " : ""}${prompt}`; }