diff --git a/packages/shared/src/types/plugin.ts b/packages/shared/src/types/plugin.ts index 95f7964e950..d5231aadff5 100644 --- a/packages/shared/src/types/plugin.ts +++ b/packages/shared/src/types/plugin.ts @@ -24,6 +24,7 @@ import type { RoutineCatchUpPolicy, RoutineConcurrencyPolicy, RoutineStatus, + IssueSurfaceVisibility, } from "../constants.js"; import type { Agent } from "./agent.js"; import type { Project } from "./project.js"; @@ -242,7 +243,7 @@ export interface PluginManagedRoutineDeclaration { triggers?: Array>; /** Defaults for issues created by this routine. */ issueTemplate?: { - surfaceVisibility?: "plugin_operation" | "normal"; + surfaceVisibility?: IssueSurfaceVisibility; originId?: string | null; billingCode?: string | null; }; diff --git a/packages/shared/src/validators/plugin.test.ts b/packages/shared/src/validators/plugin.test.ts index b8b3cbdab5f..7de0c31687c 100644 --- a/packages/shared/src/validators/plugin.test.ts +++ b/packages/shared/src/validators/plugin.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { PLUGIN_CAPABILITIES } from "../constants.js"; -import { pluginUiSlotDeclarationSchema } from "./plugin.js"; +import { pluginManagedRoutineDeclarationSchema, pluginUiSlotDeclarationSchema } from "./plugin.js"; describe("plugin capability constants", () => { it("exposes each capability once", () => { @@ -8,6 +8,28 @@ describe("plugin capability constants", () => { }); }); +describe("plugin managed routine validators", () => { + it("accepts core issue surface visibility values in routine templates", () => { + const parsed = pluginManagedRoutineDeclarationSchema.parse({ + routineKey: "wiki.refresh", + title: "Refresh Wiki", + issueTemplate: { surfaceVisibility: "default" }, + }); + + expect(parsed.issueTemplate?.surfaceVisibility).toBe("default"); + }); + + it("rejects non-core issue surface visibility values in routine templates", () => { + const parsed = pluginManagedRoutineDeclarationSchema.safeParse({ + routineKey: "wiki.refresh", + title: "Refresh Wiki", + issueTemplate: { surfaceVisibility: "normal" }, + }); + + expect(parsed.success).toBe(false); + }); +}); + describe("plugin UI slot validators", () => { it("accepts route-scoped sidebar slots with a routePath", () => { const parsed = pluginUiSlotDeclarationSchema.parse({ diff --git a/packages/shared/src/validators/plugin.ts b/packages/shared/src/validators/plugin.ts index 01cb7f1f61f..336620a0acb 100644 --- a/packages/shared/src/validators/plugin.ts +++ b/packages/shared/src/validators/plugin.ts @@ -21,6 +21,7 @@ import { ROUTINE_STATUSES, ROUTINE_TRIGGER_KINDS, ROUTINE_TRIGGER_SIGNING_MODES, + ISSUE_SURFACE_VISIBILITIES, } from "../constants.js"; import { routineVariableSchema } from "./routine.js"; @@ -201,7 +202,7 @@ export const pluginManagedRoutineDeclarationSchema = z.object({ replayWindowSec: z.number().int().min(30).max(86_400).optional().nullable(), })).max(20).optional(), issueTemplate: z.object({ - surfaceVisibility: z.enum(["plugin_operation", "normal"]).optional(), + surfaceVisibility: z.enum(ISSUE_SURFACE_VISIBILITIES).optional(), originId: z.string().trim().max(255).nullable().optional(), billingCode: z.string().trim().max(200).nullable().optional(), }).optional(),