import { z } from "zod"; import { ISSUE_EXECUTION_DECISION_OUTCOMES, ISSUE_EXECUTION_POLICY_MODES, ISSUE_EXECUTION_STAGE_TYPES, ISSUE_EXECUTION_STATE_STATUSES, ISSUE_PRIORITIES, ISSUE_STATUSES, } from "../constants.js"; export const ISSUE_EXECUTION_WORKSPACE_PREFERENCES = [ "inherit", "shared_workspace", "isolated_workspace", "operator_branch", "reuse_existing", "agent_default", ] as const; const executionWorkspaceStrategySchema = z .object({ type: z.enum(["project_primary", "git_worktree", "adapter_managed", "cloud_sandbox"]).optional(), baseRef: z.string().optional().nullable(), branchTemplate: z.string().optional().nullable(), worktreeParentDir: z.string().optional().nullable(), provisionCommand: z.string().optional().nullable(), teardownCommand: z.string().optional().nullable(), }) .strict(); export const issueExecutionWorkspaceSettingsSchema = z .object({ mode: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional(), workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(), workspaceRuntime: z.record(z.unknown()).optional().nullable(), }) .strict(); export const issueAssigneeAdapterOverridesSchema = z .object({ adapterConfig: z.record(z.unknown()).optional(), useProjectWorkspace: z.boolean().optional(), }) .strict(); const issueExecutionStagePrincipalBaseSchema = z.object({ type: z.enum(["agent", "user"]), agentId: z.string().uuid().optional().nullable(), userId: z.string().optional().nullable(), }); export const issueExecutionStagePrincipalSchema = issueExecutionStagePrincipalBaseSchema .superRefine((value, ctx) => { if (value.type === "agent") { if (!value.agentId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Agent participants require agentId", path: ["agentId"] }); } if (value.userId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Agent participants cannot set userId", path: ["userId"] }); } return; } if (!value.userId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "User participants require userId", path: ["userId"] }); } if (value.agentId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "User participants cannot set agentId", path: ["agentId"] }); } }); export const issueExecutionStageParticipantSchema = issueExecutionStagePrincipalBaseSchema.extend({ id: z.string().uuid().optional(), }).superRefine((value, ctx) => { if (value.type === "agent") { if (!value.agentId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Agent participants require agentId", path: ["agentId"] }); } if (value.userId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Agent participants cannot set userId", path: ["userId"] }); } return; } if (!value.userId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "User participants require userId", path: ["userId"] }); } if (value.agentId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "User participants cannot set agentId", path: ["agentId"] }); } }); export const issueExecutionStageSchema = z.object({ id: z.string().uuid().optional(), type: z.enum(ISSUE_EXECUTION_STAGE_TYPES), approvalsNeeded: z.literal(1).optional().default(1), participants: z.array(issueExecutionStageParticipantSchema).default([]), }); export const issueExecutionPolicySchema = z.object({ mode: z.enum(ISSUE_EXECUTION_POLICY_MODES).optional().default("normal"), commentRequired: z.boolean().optional().default(true), stages: z.array(issueExecutionStageSchema).default([]), }); export const issueExecutionStateSchema = z.object({ status: z.enum(ISSUE_EXECUTION_STATE_STATUSES), currentStageId: z.string().uuid().nullable(), currentStageIndex: z.number().int().nonnegative().nullable(), currentStageType: z.enum(ISSUE_EXECUTION_STAGE_TYPES).nullable(), currentParticipant: issueExecutionStagePrincipalSchema.nullable(), returnAssignee: issueExecutionStagePrincipalSchema.nullable(), completedStageIds: z.array(z.string().uuid()).default([]), lastDecisionId: z.string().uuid().nullable(), lastDecisionOutcome: z.enum(ISSUE_EXECUTION_DECISION_OUTCOMES).nullable(), }); export const createIssueSchema = z.object({ projectId: z.string().uuid().optional().nullable(), projectWorkspaceId: z.string().uuid().optional().nullable(), goalId: z.string().uuid().optional().nullable(), parentId: z.string().uuid().optional().nullable(), blockedByIssueIds: z.array(z.string().uuid()).optional(), inheritExecutionWorkspaceFromIssueId: z.string().uuid().optional().nullable(), title: z.string().min(1), description: z.string().optional().nullable(), status: z.enum(ISSUE_STATUSES).optional().default("backlog"), priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"), assigneeAgentId: z.string().uuid().optional().nullable(), assigneeUserId: z.string().optional().nullable(), requestDepth: z.number().int().nonnegative().optional().default(0), billingCode: z.string().optional().nullable(), assigneeAdapterOverrides: issueAssigneeAdapterOverridesSchema.optional().nullable(), executionPolicy: issueExecutionPolicySchema.optional().nullable(), executionWorkspaceId: z.string().uuid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), labelIds: z.array(z.string().uuid()).optional(), }); export type CreateIssue = z.infer; export const createIssueLabelSchema = z.object({ name: z.string().trim().min(1).max(48), color: z.string().regex(/^#(?:[0-9a-fA-F]{6})$/, "Color must be a 6-digit hex value"), }); export type CreateIssueLabel = z.infer; export const updateIssueSchema = createIssueSchema.partial().extend({ comment: z.string().min(1).optional(), reopen: z.boolean().optional(), interrupt: z.boolean().optional(), hiddenAt: z.string().datetime().nullable().optional(), }); export type UpdateIssue = z.infer; export type IssueExecutionWorkspaceSettings = z.infer; export const checkoutIssueSchema = z.object({ agentId: z.string().uuid(), expectedStatuses: z.array(z.enum(ISSUE_STATUSES)).nonempty(), }); export type CheckoutIssue = z.infer; export const addIssueCommentSchema = z.object({ body: z.string().min(1), reopen: z.boolean().optional(), interrupt: z.boolean().optional(), }); export type AddIssueComment = z.infer; export const linkIssueApprovalSchema = z.object({ approvalId: z.string().uuid(), }); export type LinkIssueApproval = z.infer; export const createIssueAttachmentMetadataSchema = z.object({ issueCommentId: z.string().uuid().optional().nullable(), }); export type CreateIssueAttachmentMetadata = z.infer; export const ISSUE_DOCUMENT_FORMATS = ["markdown"] as const; export const issueDocumentFormatSchema = z.enum(ISSUE_DOCUMENT_FORMATS); export const issueDocumentKeySchema = z .string() .trim() .min(1) .max(64) .regex(/^[a-z0-9][a-z0-9_-]*$/, "Document key must be lowercase letters, numbers, _ or -"); export const upsertIssueDocumentSchema = z.object({ title: z.string().trim().max(200).nullable().optional(), format: issueDocumentFormatSchema, body: z.string().max(524288), changeSummary: z.string().trim().max(500).nullable().optional(), baseRevisionId: z.string().uuid().nullable().optional(), }); export const restoreIssueDocumentRevisionSchema = z.object({}); export type IssueDocumentFormat = z.infer; export type UpsertIssueDocument = z.infer; export type RestoreIssueDocumentRevision = z.infer;