diff --git a/packages/google-sheets-mcp-server/package.json b/packages/google-sheets-mcp-server/package.json index c17e56f707..a1747d88de 100644 --- a/packages/google-sheets-mcp-server/package.json +++ b/packages/google-sheets-mcp-server/package.json @@ -47,7 +47,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "googleapis": "^174.0.1", - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^22.20.1", diff --git a/packages/google-sheets-mcp-server/src/tools.ts b/packages/google-sheets-mcp-server/src/tools.ts index c084f65e46..1054317781 100644 --- a/packages/google-sheets-mcp-server/src/tools.ts +++ b/packages/google-sheets-mcp-server/src/tools.ts @@ -7,7 +7,7 @@ export type ToolResult = CallToolResult; export interface GoogleSheetsToolDefinition { name: string; description: string; - schema: z.AnyZodObject; + schema: z.ZodObject; annotations: ToolAnnotations; execute: (input: Record) => Promise; } @@ -84,7 +84,7 @@ function formatTextResponse(value: unknown): ToolResult { } function errorMessage(error: unknown): string { - if (error instanceof z.ZodError) return error.errors.map((entry) => entry.message).join("; "); + if (error instanceof z.ZodError) return error.issues.map((entry) => entry.message).join("; "); if (error instanceof Error) return error.message; return String(error); } diff --git a/packages/kv-demo-mcp-server/package.json b/packages/kv-demo-mcp-server/package.json index 83356617b3..f7491de8f2 100644 --- a/packages/kv-demo-mcp-server/package.json +++ b/packages/kv-demo-mcp-server/package.json @@ -45,7 +45,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^22.20.1", diff --git a/packages/kv-demo-mcp-server/src/tools.ts b/packages/kv-demo-mcp-server/src/tools.ts index 29017a525e..6e61d383e6 100644 --- a/packages/kv-demo-mcp-server/src/tools.ts +++ b/packages/kv-demo-mcp-server/src/tools.ts @@ -7,7 +7,7 @@ export type ToolResult = CallToolResult; export interface KvToolDefinition { name: string; description: string; - schema: z.AnyZodObject; + schema: z.ZodObject; annotations: ToolAnnotations; execute: (input: Record) => Promise; } @@ -50,7 +50,7 @@ function formatTextResponse(value: unknown): ToolResult { } function errorMessage(error: unknown): string { - if (error instanceof z.ZodError) return error.errors.map((entry) => entry.message).join("; "); + if (error instanceof z.ZodError) return error.issues.map((entry) => entry.message).join("; "); if (error instanceof Error) return error.message; return String(error); } diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index ba33a5ff5e..03d8b88d61 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -45,7 +45,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "@paperclipai/shared": "workspace:*", - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^22.20.1", diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts index f5772b33a4..6d0acc6f32 100644 --- a/packages/mcp-server/src/tools.ts +++ b/packages/mcp-server/src/tools.ts @@ -19,7 +19,7 @@ import { formatErrorResponse, formatTextResponse } from "./format.js"; export interface ToolDefinition { name: string; description: string; - schema: z.AnyZodObject; + schema: z.ZodObject; execute: (input: Record) => Promise<{ content: Array<{ type: "text"; text: string }>; }>; @@ -51,26 +51,26 @@ function parseOptionalJson(raw: string | undefined | null): unknown { return JSON.parse(raw); } -const companyIdOptional = z.string().uuid().optional().nullable(); -const agentIdOptional = z.string().uuid().optional().nullable(); +const companyIdOptional = z.string().guid().optional().nullable(); +const agentIdOptional = z.string().guid().optional().nullable(); const issueIdSchema = z.string().min(1); const projectIdSchema = z.string().min(1); -const goalIdSchema = z.string().uuid(); -const approvalIdSchema = z.string().uuid(); +const goalIdSchema = z.string().guid(); +const approvalIdSchema = z.string().guid(); const documentKeySchema = z.string().trim().min(1).max(64); const listIssuesSchema = z.object({ companyId: companyIdOptional, status: z.string().optional(), - projectId: z.string().uuid().optional(), - assigneeAgentId: z.string().uuid().optional(), - participantAgentId: z.string().uuid().optional(), + projectId: z.string().guid().optional(), + assigneeAgentId: z.string().guid().optional(), + participantAgentId: z.string().guid().optional(), assigneeUserId: z.string().optional(), touchedByUserId: z.string().optional(), inboxArchivedByUserId: z.string().optional(), unreadForUserId: z.string().optional(), - labelId: z.string().uuid().optional(), - executionWorkspaceId: z.string().uuid().optional(), + labelId: z.string().guid().optional(), + executionWorkspaceId: z.string().guid().optional(), originKind: z.string().optional(), originId: z.string().optional(), includeRoutineExecutions: z.boolean().optional(), @@ -80,7 +80,7 @@ const listIssuesSchema = z.object({ const listCommentsSchema = z.object({ issueId: issueIdSchema, - after: z.string().uuid().optional(), + after: z.string().guid().optional(), order: z.enum(["asc", "desc"]).optional(), limit: z.number().int().positive().max(500).optional(), }); @@ -92,7 +92,7 @@ const upsertDocumentToolSchema = z.object({ format: z.enum(["markdown"]).default("markdown"), body: z.string().max(524288), changeSummary: z.string().trim().max(500).nullable().optional(), - baseRevisionId: z.string().uuid().nullable().optional(), + baseRevisionId: z.string().guid().nullable().optional(), }); const createIssueToolSchema = z.object({ @@ -116,8 +116,8 @@ const addCommentToolSchema = z.object({ const createSuggestTasksToolSchema = z.object({ issueId: issueIdSchema, idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -127,8 +127,8 @@ const createSuggestTasksToolSchema = z.object({ const createAskUserQuestionsToolSchema = z.object({ issueId: issueIdSchema, idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -138,8 +138,8 @@ const createAskUserQuestionsToolSchema = z.object({ const createRequestConfirmationToolSchema = z.object({ issueId: issueIdSchema, idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("none"), @@ -149,8 +149,8 @@ const createRequestConfirmationToolSchema = z.object({ const createRequestCheckboxConfirmationToolSchema = z.object({ issueId: issueIdSchema, idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -176,7 +176,7 @@ const apiRequestSchema = z.object({ const workspaceRuntimeControlTargetSchema = z.object({ workspaceCommandId: z.string().min(1).optional().nullable(), - runtimeServiceId: z.string().uuid().optional().nullable(), + runtimeServiceId: z.string().guid().optional().nullable(), serviceIndex: z.number().int().nonnegative().optional().nullable(), }); @@ -187,7 +187,7 @@ const issueWorkspaceRuntimeControlSchema = z.object({ const waitForIssueWorkspaceServiceSchema = z.object({ issueId: issueIdSchema, - runtimeServiceId: z.string().uuid().optional().nullable(), + runtimeServiceId: z.string().guid().optional().nullable(), serviceName: z.string().min(1).optional().nullable(), timeoutSeconds: z.number().int().positive().max(300).optional(), }); @@ -287,7 +287,7 @@ export function createToolDefinitions(client: PaperclipApiClient): ToolDefinitio makeTool( "paperclipGetHeartbeatContext", "Get compact heartbeat context for an issue", - z.object({ issueId: issueIdSchema, wakeCommentId: z.string().uuid().optional() }), + z.object({ issueId: issueIdSchema, wakeCommentId: z.string().guid().optional() }), async ({ issueId, wakeCommentId }) => { const qs = wakeCommentId ? `?wakeCommentId=${encodeURIComponent(wakeCommentId)}` : ""; return client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/heartbeat-context${qs}`); @@ -309,7 +309,7 @@ export function createToolDefinitions(client: PaperclipApiClient): ToolDefinitio makeTool( "paperclipGetComment", "Get a specific issue comment by id", - z.object({ issueId: issueIdSchema, commentId: z.string().uuid() }), + z.object({ issueId: issueIdSchema, commentId: z.string().guid() }), async ({ issueId, commentId }) => client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/comments/${encodeURIComponent(commentId)}`), ), @@ -558,7 +558,7 @@ export function createToolDefinitions(client: PaperclipApiClient): ToolDefinitio z.object({ issueId: issueIdSchema, key: documentKeySchema, - revisionId: z.string().uuid(), + revisionId: z.string().guid(), }), async ({ issueId, key, revisionId }) => client.requestJson( diff --git a/packages/plugins/sdk/package.json b/packages/plugins/sdk/package.json index b14ea3e66f..60824d27f0 100644 --- a/packages/plugins/sdk/package.json +++ b/packages/plugins/sdk/package.json @@ -109,7 +109,7 @@ }, "dependencies": { "@paperclipai/shared": "workspace:*", - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^22.20.1", diff --git a/packages/shared/package.json b/packages/shared/package.json index 0f441450a2..715aa35b7b 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -45,7 +45,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^22.20.1", diff --git a/packages/shared/src/config-schema.ts b/packages/shared/src/config-schema.ts index 190645062e..f9c8d41cb0 100644 --- a/packages/shared/src/config-schema.ts +++ b/packages/shared/src/config-schema.ts @@ -101,11 +101,11 @@ export const secretsConfigSchema = z.object({ export const telemetryConfigSchema = z.object({ enabled: z.boolean().default(true), -}).passthrough().default({}); +}).passthrough().prefault({}); export const updatesConfigSchema = z.object({ checkEnabled: z.boolean().default(true), -}).passthrough().default({}); +}).passthrough().prefault({}); export const paperclipConfigSchema = z .object({ @@ -210,18 +210,14 @@ function isRecord(value: unknown): value is Record { } function unwrapConfigSchema(schema: z.ZodTypeAny): z.ZodTypeAny { - let current = schema; + let current: z.ZodTypeAny = schema; while (true) { - if (current instanceof z.ZodEffects) { - current = current.innerType(); - continue; - } if (current instanceof z.ZodOptional) { - current = current.unwrap(); + current = current.unwrap() as z.ZodTypeAny; continue; } if (current instanceof z.ZodDefault) { - current = current.removeDefault(); + current = current.unwrap() as z.ZodTypeAny; continue; } return current; diff --git a/packages/shared/src/frontmatter.ts b/packages/shared/src/frontmatter.ts index 4ee9c9b9d7..59cfd440d5 100644 --- a/packages/shared/src/frontmatter.ts +++ b/packages/shared/src/frontmatter.ts @@ -44,7 +44,7 @@ const skillMetadataValueSchema: z.ZodType = z.lazy(() => z.boolean(), z.null(), z.array(skillMetadataValueSchema), - z.record(skillMetadataValueSchema), + z.record(z.string(), skillMetadataValueSchema), ]) ); @@ -52,7 +52,7 @@ export const skillFrontmatterSchema = z.object({ name: z.string().regex(SKILL_FRONTMATTER_SLUG_RE, "Expected a lowercase URL slug."), description: z.string().min(1), "allowed-tools": z.array(z.string()).optional(), - metadata: z.record(skillMetadataValueSchema).optional(), + metadata: z.record(z.string(), skillMetadataValueSchema).optional(), }).passthrough(); export const skillFrontmatterKnownKeys = [ diff --git a/packages/shared/src/validators/access.ts b/packages/shared/src/validators/access.ts index 021fba0bb7..cee3e5567c 100644 --- a/packages/shared/src/validators/access.ts +++ b/packages/shared/src/validators/access.ts @@ -74,7 +74,7 @@ export const createCliAuthChallengeSchema = z.object({ command: z.string().min(1).max(240), clientName: z.string().max(120).optional().nullable(), requestedAccess: boardCliAuthAccessLevelSchema.default("board"), - requestedCompanyId: z.string().uuid().optional().nullable(), + requestedCompanyId: z.string().guid().optional().nullable(), }); export type CreateCliAuthChallenge = z.infer; @@ -88,7 +88,7 @@ export type ResolveCliAuthChallenge = z.infer; @@ -128,8 +128,8 @@ export type UpdateCompanyMemberWithPermissions = z.infer; export const updateUserCompanyAccessSchema = z.object({ - companyIds: z.array(z.string().uuid()).default([]), + companyIds: z.array(z.string().guid()).default([]), }); export type UpdateUserCompanyAccess = z.infer; diff --git a/packages/shared/src/validators/adapter-auth-session.ts b/packages/shared/src/validators/adapter-auth-session.ts index 5a867a825b..aed8b2aba0 100644 --- a/packages/shared/src/validators/adapter-auth-session.ts +++ b/packages/shared/src/validators/adapter-auth-session.ts @@ -18,8 +18,8 @@ export type AdapterAuthSessionFailure = z.infer; export const startAdapterAuthSessionRequestSchema = z.object({ - environmentId: z.string().uuid(), + environmentId: z.string().guid(), adapterType: z.enum(AGENT_ADAPTER_TYPES), ttlSeconds: z.number().int().min(60).max(24 * 60 * 60).optional(), }).strict(); diff --git a/packages/shared/src/validators/adapter-registry.ts b/packages/shared/src/validators/adapter-registry.ts index dcab1a93e8..6fb9f655d4 100644 --- a/packages/shared/src/validators/adapter-registry.ts +++ b/packages/shared/src/validators/adapter-registry.ts @@ -8,7 +8,7 @@ export const adapterRegistryEntrySchema = z envKeys: z.array(z.string()).optional(), allowFqdns: z.array(z.string()).optional(), probeCommand: z.array(z.string()).optional(), - defaultEnv: z.record(z.string()).optional(), + defaultEnv: z.record(z.string(), z.string()).optional(), }) .strict(); diff --git a/packages/shared/src/validators/adapter-skills.ts b/packages/shared/src/validators/adapter-skills.ts index d0befc9110..ae43f7b180 100644 --- a/packages/shared/src/validators/adapter-skills.ts +++ b/packages/shared/src/validators/adapter-skills.ts @@ -29,7 +29,7 @@ export const agentSkillAssignmentModeSchema = z.enum([ export const agentDesiredSkillEntrySchema = z.object({ key: z.string().min(1), - versionId: z.string().uuid().nullable(), + versionId: z.string().guid().nullable(), }); export const agentDesiredSkillSelectionSchema = z.union([ @@ -40,8 +40,8 @@ export const agentDesiredSkillSelectionSchema = z.union([ export const agentSkillEntrySchema = z.object({ key: z.string().min(1), runtimeName: z.string().min(1).nullable(), - versionId: z.string().uuid().nullable().optional(), - currentVersionId: z.string().uuid().nullable().optional(), + versionId: z.string().guid().nullable().optional(), + currentVersionId: z.string().guid().nullable().optional(), desired: z.boolean(), managed: z.boolean(), state: agentSkillStateSchema, diff --git a/packages/shared/src/validators/agent.ts b/packages/shared/src/validators/agent.ts index 308cd057c1..794438fa52 100644 --- a/packages/shared/src/validators/agent.ts +++ b/packages/shared/src/validators/agent.ts @@ -9,6 +9,7 @@ import { agentAdapterTypeSchema } from "../adapter-type.js"; import { envConfigSchema } from "./secret.js"; import { trustAuthorizationPolicySchema, trustPresetSchema } from "./trust-policy.js"; import { agentDesiredSkillSelectionSchema } from "./adapter-skills.js"; +import { objectWithoutDefaults } from "./partial.js"; export const agentPermissionsSchema = z.object({ canCreateAgents: z.boolean().optional().default(false), @@ -73,14 +74,14 @@ export const createAgentSchema = z.object({ role: z.enum(AGENT_ROLES).optional().default("general"), title: z.string().optional().nullable(), icon: z.enum(AGENT_ICON_NAMES).optional().nullable(), - reportsTo: z.string().uuid().optional().nullable(), + reportsTo: z.string().guid().optional().nullable(), capabilities: z.string().optional().nullable(), desiredSkills: z.array(agentDesiredSkillSelectionSchema).optional(), adapterType: agentAdapterTypeSchema, adapterConfig: adapterConfigSchema.optional().default({}), instructionsBundle: createAgentInstructionsBundleSchema.optional(), runtimeConfig: agentRuntimeConfigSchema.optional().default({}), - defaultEnvironmentId: z.string().uuid().optional().nullable(), + defaultEnvironmentId: z.string().guid().optional().nullable(), budgetMonthlyCents: z.number().int().nonnegative().optional().default(0), permissions: agentPermissionsSchema.optional(), metadata: z.record(z.string(), z.unknown()).optional().nullable(), @@ -116,14 +117,15 @@ export const builtInAgentResetSchema = z.object({ export type BuiltInAgentReset = z.infer; export const createAgentHireSchema = createAgentSchema.extend({ - sourceIssueId: z.string().uuid().optional().nullable(), - sourceIssueIds: z.array(z.string().uuid()).optional(), + sourceIssueId: z.string().guid().optional().nullable(), + sourceIssueIds: z.array(z.string().guid()).optional(), }); export type CreateAgentHire = z.infer; -export const updateAgentSchema = createAgentSchema - .omit({ permissions: true }) +export const updateAgentSchema = objectWithoutDefaults( + createAgentSchema.omit({ permissions: true }), +) .partial() .extend({ permissions: z.never().optional(), @@ -143,11 +145,11 @@ export type UpdateAgentInstructionsPath = z.infer { const hasProjectBoundary = Boolean(value.projectId) || Boolean(value.projectIds?.length); const hasParentBoundary = Boolean(value.parentIssueId) || Boolean(value.parentIssueIds?.length); @@ -166,7 +168,7 @@ export const standardAgentKeyScopeSchema = z.object({ export const skillTestAgentKeyScopeSchema = z.object({ kind: z.literal("skill_test"), - issueId: z.string().uuid(), + issueId: z.string().guid(), }).strict(); export const agentApiKeyScopeSchema = z.union([ @@ -226,7 +228,7 @@ export const testAdapterEnvironmentSchema = z.object({ * environment is non-local (SSH/sandbox), the test probes are executed * inside that environment so the result reflects real agent execution. */ - environmentId: z.string().uuid().optional().nullable(), + environmentId: z.string().guid().optional().nullable(), }); export type TestAdapterEnvironment = z.infer; diff --git a/packages/shared/src/validators/approval.ts b/packages/shared/src/validators/approval.ts index d24efadd76..c1c074dc2f 100644 --- a/packages/shared/src/validators/approval.ts +++ b/packages/shared/src/validators/approval.ts @@ -4,9 +4,9 @@ import { multilineTextSchema } from "./text.js"; export const createApprovalSchema = z.object({ type: z.enum(APPROVAL_TYPES), - requestedByAgentId: z.string().uuid().optional().nullable(), + requestedByAgentId: z.string().guid().optional().nullable(), payload: z.record(z.string(), z.unknown()), - issueIds: z.array(z.string().uuid()).optional(), + issueIds: z.array(z.string().guid()).optional(), }); export type CreateApproval = z.infer; diff --git a/packages/shared/src/validators/artifact.ts b/packages/shared/src/validators/artifact.ts index e8b14e8e8b..92e3102691 100644 --- a/packages/shared/src/validators/artifact.ts +++ b/packages/shared/src/validators/artifact.ts @@ -12,10 +12,10 @@ export const companyArtifactGroupBySchema = z.enum(["none", "task", "parent_task export const companyArtifactsQuerySchema = z.object({ kind: z.enum(["image", "video", "text", "document", "file", "all"]).optional().default("all"), - projectId: z.string().uuid().optional(), + projectId: z.string().guid().optional(), q: z.string().trim().max(COMPANY_ARTIFACTS_MAX_QUERY_LENGTH).optional(), groupBy: companyArtifactGroupBySchema.optional().default("none"), - groupIssueId: z.string().uuid().optional(), + groupIssueId: z.string().guid().optional(), starred: z.preprocess( (value) => value === "true" ? true : value === "false" ? false : value, z.boolean(), @@ -41,16 +41,16 @@ export const companyArtifactSchema = z.object({ openPath: z.string().nullable(), downloadPath: z.string().nullable(), issue: z.object({ - id: z.string().uuid(), + id: z.string().guid(), identifier: z.string(), title: z.string(), }), project: z.object({ - id: z.string().uuid(), + id: z.string().guid(), name: z.string(), }).nullable(), createdByAgent: z.object({ - id: z.string().uuid(), + id: z.string().guid(), name: z.string(), }).nullable(), updatedAt: z.string().datetime(), @@ -61,7 +61,7 @@ export const companyArtifactGroupSchema = z.object({ id: z.string().min(1), groupBy: companyArtifactGroupBySchema.exclude(["none"]), issue: z.object({ - id: z.string().uuid(), + id: z.string().guid(), identifier: z.string(), title: z.string(), }), diff --git a/packages/shared/src/validators/budget.ts b/packages/shared/src/validators/budget.ts index abae5a90dd..55f629c8f4 100644 --- a/packages/shared/src/validators/budget.ts +++ b/packages/shared/src/validators/budget.ts @@ -8,7 +8,7 @@ import { export const upsertBudgetPolicySchema = z.object({ scopeType: z.enum(BUDGET_SCOPE_TYPES), - scopeId: z.string().uuid(), + scopeId: z.string().guid(), metric: z.enum(BUDGET_METRICS).optional().default("billed_cents"), windowKind: z.enum(BUDGET_WINDOW_KINDS).optional().default("calendar_month_utc"), amount: z.number().int().nonnegative(), diff --git a/packages/shared/src/validators/claude-setup-token-session.ts b/packages/shared/src/validators/claude-setup-token-session.ts index f2dbe68357..663782e8b7 100644 --- a/packages/shared/src/validators/claude-setup-token-session.ts +++ b/packages/shared/src/validators/claude-setup-token-session.ts @@ -22,7 +22,7 @@ export type SetupTokenTransportAdvisory = z.infer Boolean(value.inputId) || Boolean(value.content?.trim()), { message: "inputId or content is required", }); export const companySkillTestRunListQuerySchema = z.object({ - inputId: z.string().uuid().optional(), + inputId: z.string().guid().optional(), }); export const catalogSkillKindSchema = z.enum(["bundled", "optional"]); diff --git a/packages/shared/src/validators/company.ts b/packages/shared/src/validators/company.ts index dc808ccc90..b7052cf144 100644 --- a/packages/shared/src/validators/company.ts +++ b/packages/shared/src/validators/company.ts @@ -4,8 +4,9 @@ import { ISSUE_THREAD_INTERACTION_RESOLVER_POLICIES, MAX_COMPANY_ATTACHMENT_MAX_BYTES, } from "../constants.js"; +import { objectWithoutDefaults } from "./partial.js"; -const logoAssetIdSchema = z.string().uuid().nullable().optional(); +const logoAssetIdSchema = z.string().guid().nullable().optional(); const brandColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/).nullable().optional(); const feedbackDataSharingTermsVersionSchema = z.string().min(1).nullable().optional(); const attachmentMaxBytesSchema = z @@ -37,21 +38,23 @@ export const createCompanySchema = z.object({ export type CreateCompany = z.infer; -export const updateCompanySchema = createCompanySchema - .partial() - .extend({ - status: z.enum(COMPANY_STATUSES).optional(), - spentMonthlyCents: z.number().int().nonnegative().optional(), - requireBoardApprovalForNewAgents: z.boolean().optional(), - interactionResolverGovernance: interactionResolverGovernanceSchema.optional(), - feedbackDataSharingEnabled: z.boolean().optional(), - feedbackDataSharingConsentAt: z.coerce.date().nullable().optional(), - feedbackDataSharingConsentByUserId: z.string().min(1).nullable().optional(), - feedbackDataSharingTermsVersion: feedbackDataSharingTermsVersionSchema, - brandColor: brandColorSchema, - logoAssetId: logoAssetIdSchema, - attachmentMaxBytes: attachmentMaxBytesSchema.optional(), - }); +export const updateCompanySchema = objectWithoutDefaults( + createCompanySchema + .partial() + .extend({ + status: z.enum(COMPANY_STATUSES).optional(), + spentMonthlyCents: z.number().int().nonnegative().optional(), + requireBoardApprovalForNewAgents: z.boolean().optional(), + interactionResolverGovernance: interactionResolverGovernanceSchema.optional(), + feedbackDataSharingEnabled: z.boolean().optional(), + feedbackDataSharingConsentAt: z.coerce.date().nullable().optional(), + feedbackDataSharingConsentByUserId: z.string().min(1).nullable().optional(), + feedbackDataSharingTermsVersion: feedbackDataSharingTermsVersionSchema, + brandColor: brandColorSchema, + logoAssetId: logoAssetIdSchema, + attachmentMaxBytes: attachmentMaxBytesSchema.optional(), + }), +); export type UpdateCompany = z.infer; diff --git a/packages/shared/src/validators/cost.ts b/packages/shared/src/validators/cost.ts index d38ec43681..1ed2263178 100644 --- a/packages/shared/src/validators/cost.ts +++ b/packages/shared/src/validators/cost.ts @@ -2,11 +2,11 @@ import { z } from "zod"; import { BILLING_TYPES, COST_STATUSES } from "../constants.js"; export const createCostEventSchema = z.object({ - agentId: z.string().uuid(), - issueId: z.string().uuid().optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - goalId: z.string().uuid().optional().nullable(), - heartbeatRunId: z.string().uuid().optional().nullable(), + agentId: z.string().guid(), + issueId: z.string().guid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + goalId: z.string().guid().optional().nullable(), + heartbeatRunId: z.string().guid().optional().nullable(), billingCode: z.string().optional().nullable(), provider: z.string().min(1), biller: z.string().min(1).optional(), diff --git a/packages/shared/src/validators/decision.ts b/packages/shared/src/validators/decision.ts index f2395af3a5..02af23ad44 100644 --- a/packages/shared/src/validators/decision.ts +++ b/packages/shared/src/validators/decision.ts @@ -5,7 +5,7 @@ export const decisionEffectStalenessSchema = z.enum(["strict", "lenient"]); export const decisionOptionStyleSchema = z.enum(["default", "primary", "destructive"]); const decisionEffectBaseShape = { - targetIssueId: z.string().uuid(), + targetIssueId: z.string().guid(), staleness: decisionEffectStalenessSchema, }; @@ -21,12 +21,12 @@ export const createIssueDecisionEffectSchema = z.object({ draft: z.object({ title: z.string().trim().min(1).max(500), description: z.string().max(100_000).nullable().optional(), - parentId: z.string().uuid().nullable().optional(), - assigneeAgentId: z.string().uuid().nullable().optional(), + parentId: z.string().guid().nullable().optional(), + assigneeAgentId: z.string().guid().nullable().optional(), assigneeUserId: z.string().trim().min(1).nullable().optional(), - projectId: z.string().uuid().nullable().optional(), - goalId: z.string().uuid().nullable().optional(), - blockedByIssueIds: z.array(z.string().uuid()).max(100).optional(), + projectId: z.string().guid().nullable().optional(), + goalId: z.string().guid().nullable().optional(), + blockedByIssueIds: z.array(z.string().guid()).max(100).optional(), }), }); @@ -40,14 +40,14 @@ export const updateIssueStatusDecisionEffectSchema = z.object({ export const assignIssueDecisionEffectSchema = z.object({ type: z.literal("assign_issue"), ...decisionEffectBaseShape, - assigneeAgentId: z.string().uuid().nullable().optional(), + assigneeAgentId: z.string().guid().nullable().optional(), assigneeUserId: z.string().trim().min(1).nullable().optional(), comment: z.string().trim().min(1).max(20_000).nullable().optional(), }); export const cancelIssueTreeDecisionEffectSchema = z.object({ type: z.literal("cancel_issue_tree"), - targetIssueId: z.string().uuid(), + targetIssueId: z.string().guid(), staleness: z.literal("strict"), reasonComment: z.string().trim().min(1).max(20_000), }); @@ -55,7 +55,7 @@ export const cancelIssueTreeDecisionEffectSchema = z.object({ export const resolveBlockerDecisionEffectSchema = z.object({ type: z.literal("resolve_blocker"), ...decisionEffectBaseShape, - removeBlockedByIssueIds: z.array(z.string().uuid()).min(1).max(100), + removeBlockedByIssueIds: z.array(z.string().guid()).min(1).max(100), }); export const decisionEffectSchema = z.discriminatedUnion("type", [ diff --git a/packages/shared/src/validators/document-annotation.ts b/packages/shared/src/validators/document-annotation.ts index 4fcfe1ba2c..d60dca3192 100644 --- a/packages/shared/src/validators/document-annotation.ts +++ b/packages/shared/src/validators/document-annotation.ts @@ -44,16 +44,16 @@ export const documentAnnotationAnchorSelectorSchema = z.object({ }).strict(); export const createDocumentAnnotationThreadSchema = z.object({ - baseRevisionId: z.string().uuid(), + baseRevisionId: z.string().guid(), baseRevisionNumber: z.number().int().positive(), selector: documentAnnotationAnchorSelectorSchema, body: multilineTextSchema.pipe(z.string().min(1).max(20_000)), - issueCommentId: z.string().uuid().nullable().optional(), + issueCommentId: z.string().guid().nullable().optional(), }).strict(); export const createDocumentAnnotationCommentSchema = z.object({ body: multilineTextSchema.pipe(z.string().min(1).max(20_000)), - issueCommentId: z.string().uuid().nullable().optional(), + issueCommentId: z.string().guid().nullable().optional(), }).strict(); export const updateDocumentAnnotationThreadSchema = z.object({ diff --git a/packages/shared/src/validators/environment-custom-images.ts b/packages/shared/src/validators/environment-custom-images.ts index 9ef97e3d69..f79a98e19c 100644 --- a/packages/shared/src/validators/environment-custom-images.ts +++ b/packages/shared/src/validators/environment-custom-images.ts @@ -31,8 +31,8 @@ export type EnvironmentCustomImageSetupConnectionSummary = z.infer; export const environmentCustomImageTemplateSchema = z.object({ - id: z.string().uuid(), - environmentId: z.string().uuid(), + id: z.string().guid(), + environmentId: z.string().guid(), provider: providerKeySchema, templateKind: environmentCustomImageTemplateKindSchema, templateRef: z.string().min(1).nullable(), @@ -40,10 +40,10 @@ export const environmentCustomImageTemplateSchema = z.object({ sourceEnvironmentConfigFingerprint: z.string().min(1).nullable(), status: environmentCustomImageTemplateStatusSchema, createdByUserId: z.string().min(1).nullable(), - createdByAgentId: z.string().uuid().nullable(), + createdByAgentId: z.string().guid().nullable(), capturedAt: isoDateTime.nullable(), lastUsedAt: isoDateTime.nullable(), - supersededByTemplateId: z.string().uuid().nullable(), + supersededByTemplateId: z.string().guid().nullable(), metadata: optionalRecordSchema, createdAt: isoDateTime, updatedAt: isoDateTime, @@ -52,16 +52,16 @@ export type EnvironmentCustomImageTemplate = z.infer; export const environmentCustomImageSetupSessionSchema = z.object({ - id: z.string().uuid(), - environmentId: z.string().uuid(), - templateId: z.string().uuid().nullable(), - promotedTemplateId: z.string().uuid().nullable(), + id: z.string().guid(), + environmentId: z.string().guid(), + templateId: z.string().guid().nullable(), + promotedTemplateId: z.string().guid().nullable(), provider: providerKeySchema, providerLeaseId: z.string().min(1).nullable(), - environmentLeaseId: z.string().uuid().nullable(), + environmentLeaseId: z.string().guid().nullable(), status: environmentCustomImageSetupSessionStatusSchema, startedByUserId: z.string().min(1).nullable(), - startedByAgentId: z.string().uuid().nullable(), + startedByAgentId: z.string().guid().nullable(), baseTemplateRef: z.string().min(1).nullable(), expiresAt: isoDateTime.nullable(), finishedAt: isoDateTime.nullable(), @@ -76,7 +76,7 @@ export type EnvironmentCustomImageSetupSession = z.infer; export const startEnvironmentCustomImageSetupSessionSchema = z.object({ - templateId: z.string().uuid().optional().nullable(), + templateId: z.string().guid().optional().nullable(), ttlSeconds: z.number().int().min(60).max(24 * 60 * 60).optional(), }).strict(); export type StartEnvironmentCustomImageSetupSession = diff --git a/packages/shared/src/validators/execution-workspace.ts b/packages/shared/src/validators/execution-workspace.ts index fc03413aeb..5f8874e5a0 100644 --- a/packages/shared/src/validators/execution-workspace.ts +++ b/packages/shared/src/validators/execution-workspace.ts @@ -30,26 +30,26 @@ const workspaceOverviewStatusFilterSchema = z.preprocess((value) => { }, z.array(executionWorkspaceStatusSchema).optional()); export const workspaceOverviewQuerySchema = z.object({ - projectId: z.string().uuid().optional(), + projectId: z.string().guid().optional(), status: workspaceOverviewStatusFilterSchema, limit: z.coerce.number().int().min(1).max(WORKSPACE_OVERVIEW_MAX_LIMIT).optional().default(WORKSPACE_OVERVIEW_DEFAULT_LIMIT), offset: z.coerce.number().int().min(0).optional().default(0), }).strict(); export const executionWorkspaceConfigSchema = z.object({ - environmentId: z.string().uuid().optional().nullable(), + environmentId: z.string().guid().optional().nullable(), provisionCommand: z.string().optional().nullable(), runtimeProvisionCommand: z.string().optional().nullable(), teardownCommand: z.string().optional().nullable(), cleanupCommand: z.string().optional().nullable(), workspaceRuntime: z.record(z.string(), z.unknown()).optional().nullable(), desiredState: z.enum(["running", "stopped", "manual"]).optional().nullable(), - serviceStates: z.record(z.enum(["running", "stopped", "manual"])).optional().nullable(), + serviceStates: z.record(z.string(), z.enum(["running", "stopped", "manual"])).optional().nullable(), }).strict(); export const workspaceRuntimeControlTargetSchema = z.object({ workspaceCommandId: z.string().min(1).optional().nullable(), - runtimeServiceId: z.string().uuid().optional().nullable(), + runtimeServiceId: z.string().guid().optional().nullable(), serviceIndex: z.number().int().nonnegative().optional().nullable(), }).strict(); @@ -77,7 +77,7 @@ export const executionWorkspaceCloseActionSchema = z.object({ }).strict(); export const executionWorkspaceCloseLinkedIssueSchema = z.object({ - id: z.string().uuid(), + id: z.string().guid(), identifier: z.string().nullable(), title: z.string(), status: z.string(), @@ -101,11 +101,11 @@ export const executionWorkspaceCloseGitReadinessSchema = z.object({ export const workspaceRuntimeServiceSchema = z.object({ id: z.string(), - companyId: z.string().uuid(), - projectId: z.string().uuid().nullable(), - projectWorkspaceId: z.string().uuid().nullable(), - executionWorkspaceId: z.string().uuid().nullable(), - issueId: z.string().uuid().nullable(), + companyId: z.string().guid(), + projectId: z.string().guid().nullable(), + projectWorkspaceId: z.string().guid().nullable(), + executionWorkspaceId: z.string().guid().nullable(), + issueId: z.string().guid().nullable(), scopeType: z.enum(["project_workspace", "execution_workspace", "run", "agent"]), scopeId: z.string().nullable(), serviceName: z.string(), @@ -118,8 +118,8 @@ export const workspaceRuntimeServiceSchema = z.object({ url: z.string().nullable(), provider: z.enum(["local_process", "adapter_managed"]), providerRef: z.string().nullable(), - ownerAgentId: z.string().uuid().nullable(), - startedByRunId: z.string().uuid().nullable(), + ownerAgentId: z.string().guid().nullable(), + startedByRunId: z.string().guid().nullable(), lastUsedAt: z.coerce.date(), startedAt: z.coerce.date(), stoppedAt: z.coerce.date().nullable(), @@ -130,7 +130,7 @@ export const workspaceRuntimeServiceSchema = z.object({ updatedAt: z.coerce.date(), }).strict(); export const executionWorkspaceCloseReadinessSchema = z.object({ - workspaceId: z.string().uuid(), + workspaceId: z.string().guid(), deliveryState: executionWorkspaceDeliveryStateSchema, state: executionWorkspaceCloseReadinessStateSchema, blockingReasons: z.array(z.string()), diff --git a/packages/shared/src/validators/external-object.ts b/packages/shared/src/validators/external-object.ts index 33ae0ce490..80a58f89a7 100644 --- a/packages/shared/src/validators/external-object.ts +++ b/packages/shared/src/validators/external-object.ts @@ -20,7 +20,7 @@ export const externalObjectCanonicalIdentitySchema = z scheme: z.enum(["http", "https"]), host: z.string().trim().min(1), path: z.string().trim().min(1), - queryParamHashes: z.record(z.string().regex(/^[a-f0-9]{64}$/)).optional(), + queryParamHashes: z.record(z.string(), z.string().regex(/^[a-f0-9]{64}$/)).optional(), }) .strict(); diff --git a/packages/shared/src/validators/feedback.ts b/packages/shared/src/validators/feedback.ts index 9ac34ef430..25d83bcc09 100644 --- a/packages/shared/src/validators/feedback.ts +++ b/packages/shared/src/validators/feedback.ts @@ -13,7 +13,7 @@ export const feedbackDataSharingPreferenceSchema = z.enum(FEEDBACK_DATA_SHARING_ export const upsertIssueFeedbackVoteSchema = z.object({ targetType: feedbackTargetTypeSchema, - targetId: z.string().uuid(), + targetId: z.string().guid(), vote: feedbackVoteValueSchema, reason: z.string().trim().max(1000).optional(), allowSharing: z.boolean().optional(), diff --git a/packages/shared/src/validators/finance.ts b/packages/shared/src/validators/finance.ts index 1f8bd99a92..f09eebf284 100644 --- a/packages/shared/src/validators/finance.ts +++ b/packages/shared/src/validators/finance.ts @@ -2,12 +2,12 @@ import { z } from "zod"; import { AGENT_ADAPTER_TYPES, FINANCE_DIRECTIONS, FINANCE_EVENT_KINDS, FINANCE_UNITS } from "../constants.js"; export const createFinanceEventSchema = z.object({ - agentId: z.string().uuid().optional().nullable(), - issueId: z.string().uuid().optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - goalId: z.string().uuid().optional().nullable(), - heartbeatRunId: z.string().uuid().optional().nullable(), - costEventId: z.string().uuid().optional().nullable(), + agentId: z.string().guid().optional().nullable(), + issueId: z.string().guid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + goalId: z.string().guid().optional().nullable(), + heartbeatRunId: z.string().guid().optional().nullable(), + costEventId: z.string().guid().optional().nullable(), billingCode: z.string().optional().nullable(), description: z.string().max(500).optional().nullable(), eventKind: z.enum(FINANCE_EVENT_KINDS), diff --git a/packages/shared/src/validators/folder.ts b/packages/shared/src/validators/folder.ts index 4f3e231029..b486444ab0 100644 --- a/packages/shared/src/validators/folder.ts +++ b/packages/shared/src/validators/folder.ts @@ -7,10 +7,10 @@ export const folderSlugSchema = z.string().trim().min(1).max(120).regex( ); export const folderSchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), + id: z.string().guid(), + companyId: z.string().guid(), kind: folderKindSchema, - parentId: z.string().uuid().nullable(), + parentId: z.string().guid().nullable(), name: z.string().min(1), slug: folderSlugSchema, systemKey: z.string().nullable(), @@ -35,7 +35,7 @@ export const folderListResultSchema = z.object({ export const createFolderSchema = z.object({ kind: folderKindSchema, - parentId: z.string().uuid().optional().nullable(), + parentId: z.string().guid().optional().nullable(), name: z.string().trim().min(1).max(120), slug: folderSlugSchema.optional().nullable(), color: z.string().trim().min(1).max(80).optional().nullable(), @@ -52,7 +52,7 @@ export const updateFolderSchema = z.object({ }); export const moveFolderSchema = z.object({ - parentId: z.string().uuid().optional().nullable(), + parentId: z.string().guid().optional().nullable(), position: z.number().int().min(0), }); @@ -62,8 +62,8 @@ export const ensureMySkillFolderSchema = z.object({ export const moveFolderItemSchema = z.object({ kind: folderKindSchema, - itemId: z.string().uuid(), - folderId: z.string().uuid().optional().nullable(), + itemId: z.string().guid(), + folderId: z.string().guid().optional().nullable(), }); export type CreateFolder = z.infer; diff --git a/packages/shared/src/validators/goal.ts b/packages/shared/src/validators/goal.ts index 63ca9d848b..6027241349 100644 --- a/packages/shared/src/validators/goal.ts +++ b/packages/shared/src/validators/goal.ts @@ -1,17 +1,18 @@ import { z } from "zod"; import { GOAL_LEVELS, GOAL_STATUSES } from "../constants.js"; +import { objectWithoutDefaults } from "./partial.js"; export const createGoalSchema = z.object({ title: z.string().min(1), description: z.string().optional().nullable(), level: z.enum(GOAL_LEVELS).optional().default("task"), status: z.enum(GOAL_STATUSES).optional().default("planned"), - parentId: z.string().uuid().optional().nullable(), - ownerAgentId: z.string().uuid().optional().nullable(), + parentId: z.string().guid().optional().nullable(), + ownerAgentId: z.string().guid().optional().nullable(), }); export type CreateGoal = z.infer; -export const updateGoalSchema = createGoalSchema.partial(); +export const updateGoalSchema = objectWithoutDefaults(createGoalSchema).partial(); export type UpdateGoal = z.infer; diff --git a/packages/shared/src/validators/inbox-agent-policy.ts b/packages/shared/src/validators/inbox-agent-policy.ts index 214bffcad1..29cef157f4 100644 --- a/packages/shared/src/validators/inbox-agent-policy.ts +++ b/packages/shared/src/validators/inbox-agent-policy.ts @@ -4,7 +4,7 @@ export const inboxAgentPolicyModeSchema = z.enum(["open", "allowlist", "disabled export const updateInboxAgentPolicySchema = z.object({ mode: inboxAgentPolicyModeSchema, - allowedAgentIds: z.array(z.string().uuid()).max(100).default([]), + allowedAgentIds: z.array(z.string().guid()).max(100).default([]), }).strict().superRefine((value, ctx) => { if (value.mode !== "allowlist" && value.allowedAgentIds.length > 0) { ctx.addIssue({ diff --git a/packages/shared/src/validators/instance.ts b/packages/shared/src/validators/instance.ts index 0ae769f8f8..1fc088daab 100644 --- a/packages/shared/src/validators/instance.ts +++ b/packages/shared/src/validators/instance.ts @@ -10,6 +10,7 @@ import { MIN_ISSUE_GRAPH_LIVENESS_AUTO_RECOVERY_LOOKBACK_HOURS, } from "../types/instance.js"; import { feedbackDataSharingPreferenceSchema } from "./feedback.js"; +import { shapeWithoutDefaults } from "./partial.js"; function presetSchema(presets: T, label: string) { return z.number().refine( @@ -36,7 +37,10 @@ export const instanceGeneralSettingsSchema = z.object({ executionMode: z.enum(["kubernetes", "any"]).optional(), }).strict(); -export const patchInstanceGeneralSettingsSchema = instanceGeneralSettingsSchema.partial(); +export const patchInstanceGeneralSettingsSchema = z + .object(shapeWithoutDefaults(instanceGeneralSettingsSchema.shape)) + .partial() + .strict(); export const instanceExperimentalSettingsSchema = z.object({ enableEnvironments: z.boolean().default(false), @@ -81,11 +85,17 @@ export const instanceExperimentalSettingsSchema = z.object({ .default(DEFAULT_ISSUE_GRAPH_LIVENESS_AUTO_RECOVERY_LOOKBACK_HOURS), }).strict(); -export const patchInstanceExperimentalSettingsSchema = instanceExperimentalSettingsSchema - .omit({ - worktreeRunExecutionActivatedAt: true, - worktreeRunExecutionActivationInstanceId: true, - }) +export const patchInstanceExperimentalSettingsSchema = z + .object( + shapeWithoutDefaults( + instanceExperimentalSettingsSchema + .omit({ + worktreeRunExecutionActivatedAt: true, + worktreeRunExecutionActivationInstanceId: true, + }) + .shape, + ), + ) .partial() .strip(); @@ -98,11 +108,11 @@ export const managedSettingMetadataSchema = z.object({ // instances every overlaid key is listed in `managedKeys`; self-hosted // responses omit the field entirely. export const instanceExperimentalSettingsWithManagedSchema = instanceExperimentalSettingsSchema.extend({ - managedKeys: z.record(managedSettingMetadataSchema).optional(), + managedKeys: z.record(z.string(), managedSettingMetadataSchema).optional(), }).strict(); export const patchInstanceSettingsSchema = z.object({ - defaultEnvironmentId: z.string().uuid().nullable().optional(), + defaultEnvironmentId: z.string().guid().nullable().optional(), }).strict(); export const issueGraphLivenessAutoRecoveryRequestSchema = z.object({ @@ -115,17 +125,24 @@ export const issueGraphLivenessAutoRecoveryRequestSchema = z.object({ }).strict(); export type InstanceGeneralSettings = z.infer; -export type PatchInstanceGeneralSettings = z.infer; +// The patch schema removes each default so an absent key stays absent. Declare +// the type from the full settings type, so every field keeps its precise type. +export type PatchInstanceGeneralSettings = Partial; export type InstanceExperimentalSettings = z.infer; -export type PatchInstanceExperimentalSettings = z.infer; +export type PatchInstanceExperimentalSettings = Partial< + Omit< + InstanceExperimentalSettings, + "worktreeRunExecutionActivatedAt" | "worktreeRunExecutionActivationInstanceId" + > +>; export type PatchInstanceSettings = z.infer; export type IssueGraphLivenessAutoRecoveryRequest = z.infer< typeof issueGraphLivenessAutoRecoveryRequestSchema >; export const instanceSettingsSchema = z.object({ - id: z.string().uuid(), - defaultEnvironmentId: z.string().uuid().nullable(), + id: z.string().guid(), + defaultEnvironmentId: z.string().guid().nullable(), general: instanceGeneralSettingsSchema, experimental: instanceExperimentalSettingsWithManagedSchema, createdAt: z.union([z.date(), z.string().datetime()]), diff --git a/packages/shared/src/validators/issue.ts b/packages/shared/src/validators/issue.ts index 8eed625341..8cd4e7cd2a 100644 --- a/packages/shared/src/validators/issue.ts +++ b/packages/shared/src/validators/issue.ts @@ -38,6 +38,7 @@ import { } from "../constants.js"; import { multilineTextSchema } from "./text.js"; import { lowTrustReviewPresetPolicySchema, trustAuthorizationPolicySchema } from "./trust-policy.js"; +import { objectWithoutDefaults } from "./partial.js"; export const issueBlockedInboxStateSchema = z.enum([ "needs_attention", @@ -65,12 +66,12 @@ export const issueBlockedInboxReasonSchema = z.enum([ ]); export const issueBlockedInboxIssueRefSchema = z.object({ - id: z.string().uuid(), + id: z.string().guid(), identifier: z.string().nullable(), title: z.string(), status: z.enum(ISSUE_STATUSES), priority: z.enum(ISSUE_PRIORITIES), - assigneeAgentId: z.string().uuid().nullable(), + assigneeAgentId: z.string().guid().nullable(), assigneeUserId: z.string().nullable(), }).strict(); @@ -82,7 +83,7 @@ export const issueBlockedInboxAttentionSchema = z.object({ stoppedSinceAt: z.string().datetime().nullable(), owner: z.object({ type: z.enum(["agent", "user", "board", "external", "unknown"]), - agentId: z.string().uuid().nullable(), + agentId: z.string().guid().nullable(), userId: z.string().nullable(), label: z.string().nullable(), }).strict(), @@ -93,8 +94,8 @@ export const issueBlockedInboxAttentionSchema = z.object({ sourceIssue: issueBlockedInboxIssueRefSchema.nullable(), leafIssue: issueBlockedInboxIssueRefSchema.nullable(), recoveryIssue: issueBlockedInboxIssueRefSchema.nullable(), - approvalId: z.string().uuid().nullable(), - interactionId: z.string().uuid().nullable(), + approvalId: z.string().guid().nullable(), + interactionId: z.string().guid().nullable(), sampleIssueIdentifier: z.string().nullable(), redaction: z.object({ externalDetailsRedacted: z.boolean(), @@ -158,7 +159,7 @@ export const issueExecutionWorkspaceSettingsSchema = z .object({ mode: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional(), sharedWorkspaceConcurrency: z.enum(["auto", "serialize", "allow"]).optional(), - environmentId: z.string().uuid().optional().nullable(), + environmentId: z.string().guid().optional().nullable(), workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(), workspaceRuntime: z.record(z.string(), z.unknown()).optional().nullable(), networkEgress: z.object({ @@ -187,7 +188,7 @@ export const issueAssigneeAdapterOverridesSchema = z const issueExecutionStagePrincipalBaseSchema = z.object({ type: z.enum(["agent", "user"]), - agentId: z.string().uuid().optional().nullable(), + agentId: z.string().guid().optional().nullable(), userId: z.string().optional().nullable(), }); @@ -211,7 +212,7 @@ export const issueExecutionStagePrincipalSchema = issueExecutionStagePrincipalBa }); export const issueExecutionStageParticipantSchema = issueExecutionStagePrincipalBaseSchema.extend({ - id: z.string().uuid().optional(), + id: z.string().guid().optional(), }).superRefine((value, ctx) => { if (value.type === "agent") { if (!value.agentId) { @@ -231,7 +232,7 @@ export const issueExecutionStageParticipantSchema = issueExecutionStagePrincipal }); export const issueExecutionStageSchema = z.object({ - id: z.string().uuid().optional(), + id: z.string().guid().optional(), type: z.enum(ISSUE_EXECUTION_STAGE_TYPES), approvalsNeeded: z.literal(1).optional().default(1), participants: z.array(issueExecutionStageParticipantSchema).default([]), @@ -282,31 +283,31 @@ export const issueReviewRequestSchema = z.object({ export const issueExecutionStateSchema = z.object({ status: z.enum(ISSUE_EXECUTION_STATE_STATUSES), - currentStageId: z.string().uuid().nullable(), + currentStageId: z.string().guid().nullable(), currentStageIndex: z.number().int().nonnegative().nullable(), currentStageType: z.enum(ISSUE_EXECUTION_STAGE_TYPES).nullable(), currentParticipant: issueExecutionStagePrincipalSchema.nullable(), returnAssignee: issueExecutionStagePrincipalSchema.nullable(), reviewRequest: issueReviewRequestSchema.nullable().optional().default(null), - completedStageIds: z.array(z.string().uuid()).default([]), - lastDecisionId: z.string().uuid().nullable(), + completedStageIds: z.array(z.string().guid()).default([]), + lastDecisionId: z.string().guid().nullable(), lastDecisionOutcome: z.enum(ISSUE_EXECUTION_DECISION_OUTCOMES).nullable(), monitor: issueExecutionMonitorStateSchema.optional().nullable(), changesRequestedCount: z.number().int().nonnegative().optional().default(0), }); export const issueRecoveryActionReadModelSchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), - sourceIssueId: z.string().uuid(), - recoveryIssueId: z.string().uuid().nullable(), + id: z.string().guid(), + companyId: z.string().guid(), + sourceIssueId: z.string().guid(), + recoveryIssueId: z.string().guid().nullable(), kind: z.enum(ISSUE_RECOVERY_ACTION_KINDS), status: z.enum(ISSUE_RECOVERY_ACTION_STATUSES), ownerType: z.enum(ISSUE_RECOVERY_ACTION_OWNER_TYPES), - ownerAgentId: z.string().uuid().nullable(), + ownerAgentId: z.string().guid().nullable(), ownerUserId: z.string().nullable(), - previousOwnerAgentId: z.string().uuid().nullable(), - returnOwnerAgentId: z.string().uuid().nullable(), + previousOwnerAgentId: z.string().guid().nullable(), + returnOwnerAgentId: z.string().guid().nullable(), cause: z.string().min(1), fingerprint: z.string().min(1), evidence: z.record(z.string(), z.unknown()), @@ -334,7 +335,7 @@ const RESOLVE_ISSUE_RECOVERY_ACTION_OUTCOMES = [ ] as const; export const resolveIssueRecoveryActionSchema = z.object({ - actionId: z.string().uuid().optional(), + actionId: z.string().guid().optional(), outcome: z.enum(RESOLVE_ISSUE_RECOVERY_ACTION_OUTCOMES), sourceIssueStatus: z.enum(["todo", "done", "in_review", "blocked"]), resolutionNote: multilineTextSchema.optional().nullable(), @@ -430,20 +431,20 @@ function withCreateIssueStatusDefault(schema: z.ZodObje } const createIssueBaseSchema = 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(), + projectId: z.string().guid().optional().nullable(), + projectWorkspaceId: z.string().guid().optional().nullable(), + goalId: z.string().guid().optional().nullable(), + parentId: z.string().guid().optional().nullable(), + blockedByIssueIds: z.array(z.string().guid()).optional(), unblockDescriptor: z.object({ owner: z.union([ - z.object({ agentId: z.string().uuid() }).strict(), + z.object({ agentId: z.string().guid() }).strict(), z.object({ userId: z.string().trim().min(1) }).strict(), z.literal("board"), ]), action: multilineTextSchema.pipe(z.string().trim().min(1).max(2_000)), }).strict().optional().nullable(), - inheritExecutionWorkspaceFromIssueId: z.string().uuid().optional().nullable(), + inheritExecutionWorkspaceFromIssueId: z.string().guid().optional().nullable(), title: z.string().min(1), description: multilineTextSchema.optional().nullable(), status: z.enum(ISSUE_STATUSES), @@ -451,7 +452,7 @@ const createIssueBaseSchema = z.object({ harnessKind: z.enum(ISSUE_HARNESS_KINDS).optional().nullable(), priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"), reviewPolicy: z.enum(ISSUE_REVIEW_POLICIES).optional().nullable(), - assigneeAgentId: z.string().uuid().optional().nullable(), + assigneeAgentId: z.string().guid().optional().nullable(), assigneeUserId: z.string().optional().nullable(), requestDepth: issueRequestDepthInputSchema.optional().default(0), createdByUserId: z.string().optional().nullable(), @@ -459,16 +460,16 @@ const createIssueBaseSchema = z.object({ billingCode: z.string().optional().nullable(), assigneeAdapterOverrides: issueAssigneeAdapterOverridesSchema.optional().nullable(), executionPolicy: issueExecutionPolicySchema.optional().nullable(), - executionWorkspaceId: z.string().uuid().optional().nullable(), + executionWorkspaceId: z.string().guid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), - labelIds: z.array(z.string().uuid()).optional(), + labelIds: z.array(z.string().guid()).optional(), watchdogDiscovery: z.object({ kind: z.enum(ISSUE_WATCHDOG_DISCOVERY_KINDS), evidenceMarkdown: multilineTextSchema.optional().nullable(), }).strict().optional().nullable(), watchdog: z.object({ - agentId: z.string().uuid(), + agentId: z.string().guid(), instructions: multilineTextSchema.optional().nullable(), }).strict().optional().nullable(), }); @@ -517,7 +518,7 @@ export const createIssueSchema = withCreateIssueStatusDefault( export type CreateIssue = z.infer; export const upsertIssueWatchdogSchema = z.object({ - agentId: z.string().uuid(), + agentId: z.string().guid(), instructions: multilineTextSchema.optional().nullable(), }).strict(); @@ -537,7 +538,7 @@ export const createChildIssueSchema = withCreateIssueStatusDefault(createIssueBa export type CreateChildIssue = z.infer; export const createAcceptedPlanDecompositionSchema = z.object({ - acceptedPlanRevisionId: z.string().uuid(), + acceptedPlanRevisionId: z.string().guid(), children: z.array(createChildIssueSchema).min(1).max(25), }); @@ -550,16 +551,18 @@ export const createIssueLabelSchema = z.object({ export type CreateIssueLabel = z.infer; -export const updateIssueSchema = createIssueBaseSchema.omit({ - createdByUserId: true, - responsibleUserId: true, - watchdog: true, -}).partial().extend({ +export const updateIssueSchema = objectWithoutDefaults( + createIssueBaseSchema.omit({ + createdByUserId: true, + responsibleUserId: true, + watchdog: true, + }), +).partial().extend({ requestDepth: issueRequestDepthInputSchema.optional(), assigneeAgentId: z.string().trim().min(1).optional().nullable(), comment: multilineTextSchema.pipe(z.string().min(1)).optional(), onBehalfOfUserId: z.string().trim().min(1).optional().nullable(), - reviewInteractionId: z.string().uuid().optional(), + reviewInteractionId: z.string().guid().optional(), reviewRequest: issueReviewRequestSchema.optional().nullable(), reopen: z.boolean().optional(), resume: z.boolean().optional(), @@ -586,7 +589,7 @@ export const stalledReviewDecisionSchema = z.object({ export type StalledReviewDecision = z.infer; export const checkoutIssueSchema = z.object({ - agentId: z.string().uuid(), + agentId: z.string().guid(), expectedStatuses: z.array(z.enum(ISSUE_STATUSES)).nonempty(), }); @@ -631,21 +634,21 @@ const issueCommentMetadataKeyValueRowSchema = issueCommentMetadataBaseRowSchema. const issueCommentMetadataIssueLinkRowSchema = issueCommentMetadataBaseRowSchema.extend({ type: z.literal("issue_link"), - issueId: z.string().uuid().nullable().optional(), + issueId: z.string().guid().nullable().optional(), identifier: z.string().trim().min(1).max(80).nullable().optional(), title: z.string().trim().min(1).max(240).nullable().optional(), }).strict(); const issueCommentMetadataAgentLinkRowSchema = issueCommentMetadataBaseRowSchema.extend({ type: z.literal("agent_link"), - agentId: z.string().uuid(), + agentId: z.string().guid(), name: z.string().trim().min(1).max(160).nullable().optional(), }).strict(); const issueCommentMetadataRunLinkRowSchema = issueCommentMetadataBaseRowSchema.extend({ type: z.literal("run_link"), - runId: z.string().uuid(), - agentId: z.string().uuid().nullable().optional(), + runId: z.string().guid(), + agentId: z.string().guid().nullable().optional(), title: z.string().trim().min(1).max(160).nullable().optional(), }).strict(); @@ -673,7 +676,7 @@ export const issueCommentMetadataSectionSchema = z.object({ export const issueCommentMetadataSchema = z.object({ version: z.literal(1), - sourceRunId: z.string().uuid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), authorizationReason: z.string().trim().min(1).max(160).nullable().optional(), sections: z.array(issueCommentMetadataSectionSchema).min(1).max(20), }).strict(); @@ -721,15 +724,15 @@ export const issueDocumentKeySchema = z export const suggestedTaskDraftSchema = z.object({ clientKey: z.string().trim().min(1).max(120), parentClientKey: z.string().trim().min(1).max(120).nullable().optional(), - parentId: z.string().uuid().nullable().optional(), + parentId: z.string().guid().nullable().optional(), title: z.string().trim().min(1).max(240), description: multilineTextSchema.pipe(z.string().trim().max(20000)).nullable().optional(), priority: z.enum(ISSUE_PRIORITIES).nullable().optional(), workMode: z.enum(ISSUE_WORK_MODES).nullable().optional(), - assigneeAgentId: z.string().uuid().nullable().optional(), + assigneeAgentId: z.string().guid().nullable().optional(), assigneeUserId: z.string().trim().min(1).nullable().optional(), - projectId: z.string().uuid().nullable().optional(), - goalId: z.string().uuid().nullable().optional(), + projectId: z.string().guid().nullable().optional(), + goalId: z.string().guid().nullable().optional(), billingCode: z.string().trim().max(120).nullable().optional(), labels: z.array(z.string().trim().min(1).max(48)).max(20).optional(), hiddenInPreview: z.boolean().optional(), @@ -745,7 +748,7 @@ export const suggestedTaskDraftSchema = z.object({ export const suggestTasksPayloadSchema = z.object({ version: z.literal(1), - defaultParentId: z.string().uuid().nullable().optional(), + defaultParentId: z.string().guid().nullable().optional(), tasks: z.array(suggestedTaskDraftSchema).min(1).max(50), }).superRefine((value, ctx) => { const seenClientKeys = new Set(); @@ -764,10 +767,10 @@ export const suggestTasksPayloadSchema = z.object({ export const suggestTasksResultCreatedTaskSchema = z.object({ clientKey: z.string().trim().min(1).max(120), - issueId: z.string().uuid(), + issueId: z.string().guid(), identifier: z.string().trim().min(1).nullable().optional(), title: z.string().trim().min(1).nullable().optional(), - parentIssueId: z.string().uuid().nullable().optional(), + parentIssueId: z.string().guid().nullable().optional(), parentIdentifier: z.string().trim().min(1).nullable().optional(), }); @@ -858,10 +861,10 @@ export const askUserQuestionsResultSchema = z.object({ cancelled: z.literal(true).optional(), cancellationReason: z.string().trim().max(4000).nullable().optional(), expirationReason: z.enum(["superseded_by_comment", "superseded_by_newer_interaction"]).optional(), - commentId: z.string().uuid().nullable().optional(), + commentId: z.string().guid().nullable().optional(), // Set alongside expirationReason "superseded_by_newer_interaction": the id of // the newer sibling ask_user_questions that replaced this one (PAP-437). - supersededByInteractionId: z.string().uuid().nullable().optional(), + supersededByInteractionId: z.string().guid().nullable().optional(), summaryMarkdown: z.string().max(20000).nullable().optional(), }); @@ -878,10 +881,10 @@ const requestConfirmationTargetBaseSchema = z.object({ export const requestConfirmationIssueDocumentTargetSchema = requestConfirmationTargetBaseSchema.extend({ type: z.literal("issue_document"), - issueId: z.string().uuid().nullable().optional(), - documentId: z.string().uuid().nullable().optional(), + issueId: z.string().guid().nullable().optional(), + documentId: z.string().guid().nullable().optional(), key: issueDocumentKeySchema, - revisionId: z.string().uuid(), + revisionId: z.string().guid(), revisionNumber: z.number().int().positive().nullable().optional(), }); @@ -899,12 +902,12 @@ export const requestConfirmationTargetSchema = z.discriminatedUnion("type", [ export const requestConfirmationToolActionPayloadSchema = z.object({ version: z.literal(1), - actionRequestId: z.string().uuid(), - invocationId: z.string().uuid(), + actionRequestId: z.string().guid(), + invocationId: z.string().guid(), toolName: z.string().trim().min(1).max(500), toolDisplayName: z.string().trim().min(1).max(500), - connectionId: z.string().uuid().nullable(), - applicationId: z.string().uuid().nullable(), + connectionId: z.string().guid().nullable(), + applicationId: z.string().guid().nullable(), appDisplayName: z.string().trim().min(1).max(500).nullable(), risk: z.enum(["write", "destructive"]), previewMarkdown: z.string().trim().min(1).max(20000), @@ -915,10 +918,10 @@ export const requestConfirmationToolActionPayloadSchema = z.object({ export const requestConfirmationSecretProposalPayloadSchema = z.object({ version: z.literal(1), - proposalId: z.string().uuid(), + proposalId: z.string().guid(), sourceSecretLabel: z.string().trim().min(1).max(500), configPath: z.string().trim().min(1).max(500), - targetAgentId: z.string().uuid(), + targetAgentId: z.string().guid(), targetAgentName: z.string().trim().min(1).max(500), justification: z.string().trim().min(1).max(20000), expiresAt: z.string().datetime({ offset: true }), @@ -1045,9 +1048,9 @@ export const requestConfirmationResumeFailureSchema = z.object({ errorCode: z.string().trim().min(1).max(120).nullable(), attempt: z.number().int().min(0).max(100), maxAttempts: z.number().int().min(0).max(100), - runId: z.string().uuid().nullable().optional(), - retryRunId: z.string().uuid().nullable().optional(), - recoveryActionId: z.string().uuid().nullable().optional(), + runId: z.string().guid().nullable().optional(), + retryRunId: z.string().guid().nullable().optional(), + recoveryActionId: z.string().guid().nullable().optional(), updatedAt: z.string().trim().min(1).nullable().optional(), }); @@ -1084,8 +1087,8 @@ export const requestConfirmationResultSchema = z.object({ "addressee_deleted", ]), reason: z.string().trim().max(4000).nullable().optional(), - commentId: z.string().uuid().nullable().optional(), - supersededByInteractionId: z.string().uuid().nullable().optional(), + commentId: z.string().guid().nullable().optional(), + supersededByInteractionId: z.string().guid().nullable().optional(), staleTarget: requestConfirmationTargetSchema.nullable().optional(), resumeFailure: requestConfirmationResumeFailureSchema.nullable().optional(), toolAction: requestConfirmationToolActionResultSchema.optional(), @@ -1119,7 +1122,7 @@ export const requestItemVerdictsItemSchema = z.object({ description: z.string().trim().max(500).nullable().optional(), previewMarkdown: z.string().max(20000).nullable().optional(), href: requestConfirmationHrefSchema.nullable().optional(), - attachmentId: z.string().uuid().nullable().optional(), + attachmentId: z.string().guid().nullable().optional(), }); export const requestItemVerdictsPayloadSchema = z.object({ @@ -1200,10 +1203,10 @@ export const requestItemVerdictsResultItemSchema = z.object({ verdict: requestItemVerdictValueSchema, reason: z.string().trim().max(4000).nullable().optional(), resolvedByUserId: z.string().trim().min(1).max(255).nullable().optional(), - resolvedByAgentId: z.string().uuid().nullable().optional(), - resolvedByRunId: z.string().uuid().nullable().optional(), + resolvedByAgentId: z.string().guid().nullable().optional(), + resolvedByRunId: z.string().guid().nullable().optional(), resolvedAt: z.union([z.string().datetime(), z.date()]), - commentId: z.string().uuid().nullable().optional(), + commentId: z.string().guid().nullable().optional(), }).superRefine((value, ctx) => { if (!value.resolvedByUserId && !value.resolvedByAgentId) { ctx.addIssue({ @@ -1228,7 +1231,7 @@ export const requestItemVerdictsResultSchema = z.object({ complete: z.boolean(), items: z.array(requestItemVerdictsResultItemSchema) .max(REQUEST_ITEM_VERDICTS_ITEM_LIMIT), - commentId: z.string().uuid().nullable().optional(), + commentId: z.string().guid().nullable().optional(), staleTarget: requestConfirmationTargetSchema.nullable().optional(), }).superRefine((value, ctx) => { const itemIds = new Set(); @@ -1246,7 +1249,7 @@ export const requestItemVerdictsResultSchema = z.object({ const createIssueThreadInteractionCommon = { resolverPolicy: issueThreadInteractionResolverPolicySchema.optional(), - addresseeAgentId: z.string().uuid().nullable().optional(), + addresseeAgentId: z.string().guid().nullable().optional(), }; export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ @@ -1254,8 +1257,8 @@ export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ ...createIssueThreadInteractionCommon, kind: z.literal("suggest_tasks"), idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -1265,8 +1268,8 @@ export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ ...createIssueThreadInteractionCommon, kind: z.literal("ask_user_questions"), idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -1276,8 +1279,8 @@ export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ ...createIssueThreadInteractionCommon, kind: z.literal("request_confirmation"), idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("none"), @@ -1287,8 +1290,8 @@ export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ ...createIssueThreadInteractionCommon, kind: z.literal("request_checkbox_confirmation"), idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -1298,8 +1301,8 @@ export const createIssueThreadInteractionSchema = z.discriminatedUnion("kind", [ ...createIssueThreadInteractionCommon, kind: z.literal("request_item_verdicts"), idempotencyKey: z.string().trim().max(255).nullable().optional(), - sourceCommentId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), + sourceCommentId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), title: z.string().trim().max(240).nullable().optional(), summary: z.string().trim().max(1000).nullable().optional(), continuationPolicy: issueThreadInteractionContinuationPolicySchema.optional().default("wake_assignee"), @@ -1388,13 +1391,13 @@ export const submitIssueThreadInteractionVerdictsSchema = z.object({ export type SubmitIssueThreadInteractionVerdicts = z.infer; export const linkIssueApprovalSchema = z.object({ - approvalId: z.string().uuid(), + approvalId: z.string().guid(), }); export type LinkIssueApproval = z.infer; export const createIssueAttachmentMetadataSchema = z.object({ - issueCommentId: z.string().uuid().optional().nullable(), + issueCommentId: z.string().guid().optional().nullable(), }); export type CreateIssueAttachmentMetadata = z.infer; @@ -1408,7 +1411,7 @@ export const upsertIssueDocumentSchema = z.object({ format: issueDocumentFormatSchema, body: multilineTextSchema.pipe(z.string().max(524288)), changeSummary: z.string().trim().max(500).nullable().optional(), - baseRevisionId: z.string().uuid().nullable().optional(), + baseRevisionId: z.string().guid().nullable().optional(), }); export const restoreIssueDocumentRevisionSchema = z.object({}); diff --git a/packages/shared/src/validators/partial.ts b/packages/shared/src/validators/partial.ts new file mode 100644 index 0000000000..fa60ec7481 --- /dev/null +++ b/packages/shared/src/validators/partial.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; + +// Remove each `.default()` wrapper from one field. The wrapper can sit at the +// top, or under an `.optional()` or `.nullable()` wrapper, so this walks the +// chain and keeps the `optional`/`nullable` wrappers. The field then has no +// default at any level of the outer chain. +function fieldWithoutDefault(field: z.ZodTypeAny): z.ZodTypeAny { + if (field instanceof z.ZodDefault) { + return fieldWithoutDefault(field.unwrap() as z.ZodTypeAny); + } + if (field instanceof z.ZodOptional) { + return z.optional(fieldWithoutDefault(field.unwrap() as z.ZodTypeAny)); + } + if (field instanceof z.ZodNullable) { + return z.nullable(fieldWithoutDefault(field.unwrap() as z.ZodTypeAny)); + } + return field; +} + +// Zod 4 keeps a field `.default()` active after `.partial()`. A patch parse then +// fills the default for an absent key. The merge then overwrites a stored value +// that the caller did not send. A patch schema must keep only the keys the +// caller sends. This helper removes each top-level field default from an object +// shape. `.partial()` then leaves an absent key absent. +export function shapeWithoutDefaults(shape: z.ZodRawShape): z.ZodRawShape { + return Object.fromEntries( + Object.entries(shape).map(([key, field]) => [ + key, + fieldWithoutDefault(field as z.ZodTypeAny), + ]), + ); +} + +// Build a patch object schema from a base object schema. The result drops each +// top-level `.default()`, so a `.partial()` parse keeps only the keys the caller +// sends. Chain `.partial()` and any `.extend()`/`.refine()` on the result. +export function objectWithoutDefaults( + schema: z.ZodObject, +): z.ZodObject { + return z.object(shapeWithoutDefaults(schema.shape)) as unknown as z.ZodObject; +} diff --git a/packages/shared/src/validators/pipeline.ts b/packages/shared/src/validators/pipeline.ts index 73bc5741eb..d6f47aa20b 100644 --- a/packages/shared/src/validators/pipeline.ts +++ b/packages/shared/src/validators/pipeline.ts @@ -24,22 +24,22 @@ export const pipelineStageApproverSchema = z.object({ export const pipelineStageOnEnterSchema = z.object({ type: z.literal("run_routine"), - routineId: z.string().uuid(), + routineId: z.string().guid(), id: z.string().trim().min(1).max(200).optional(), - projectId: z.string().uuid().optional().nullable(), - projectWorkspaceId: z.string().uuid().optional().nullable(), - executionWorkspaceId: z.string().uuid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + projectWorkspaceId: z.string().guid().optional().nullable(), + executionWorkspaceId: z.string().guid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), }).passthrough(); export const pipelineStageAutomationSchema = z.object({ - routineId: z.string().uuid().optional().nullable(), - assigneeAgentId: z.string().uuid().optional().nullable(), + routineId: z.string().guid().optional().nullable(), + assigneeAgentId: z.string().guid().optional().nullable(), instructionsBody: z.string().optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - projectWorkspaceId: z.string().uuid().optional().nullable(), - executionWorkspaceId: z.string().uuid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + projectWorkspaceId: z.string().guid().optional().nullable(), + executionWorkspaceId: z.string().guid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), }).passthrough(); @@ -52,7 +52,7 @@ export const pipelineStageCarryOverPolicySchema = z.object({ }); export const pipelineStageBreakdownSchema = z.object({ - targetPipelineId: z.string().uuid(), + targetPipelineId: z.string().guid(), targetStageKey: z.string().trim().min(1).max(120), pieceNoun: z.string().trim().min(1).max(80).default("piece"), carryOverPolicy: pipelineStageCarryOverPolicySchema.optional(), @@ -137,7 +137,7 @@ export const pipelineAutomationRetryCleanupOptionsSchema = z.object({ export const pipelineAutomationRetryRequestSchema = z.object({ scope: pipelineAutomationRetryScopeSchema, - targetStageId: z.string().uuid().nullable().optional(), + targetStageId: z.string().guid().nullable().optional(), expectedVersion: z.number().int().positive(), cleanup: pipelineAutomationRetryCleanupOptionsSchema.default({ retireDirectChildren: true, diff --git a/packages/shared/src/validators/plugin.ts b/packages/shared/src/validators/plugin.ts index 37b959efef..24a6ecfce3 100644 --- a/packages/shared/src/validators/plugin.ts +++ b/packages/shared/src/validators/plugin.ts @@ -251,7 +251,7 @@ export const pluginManagedAgentDeclarationSchema = z.object({ instructions: z.object({ entryFile: z.string().min(1).max(200).optional(), content: z.string().max(200_000).optional(), - files: z.record(z.string().max(200_000)).optional(), + files: z.record(z.string(), z.string().max(200_000)).optional(), assetPath: z.string().min(1).max(500).optional(), }).optional(), }); @@ -287,7 +287,7 @@ export const pluginManagedRoutineDeclarationSchema = z.object({ description: z.string().max(10_000).nullable().optional(), assigneeRef: pluginManagedResourceRefSchema.extend({ resourceKind: z.literal("agent") }).nullable().optional(), projectRef: pluginManagedResourceRefSchema.extend({ resourceKind: z.literal("project") }).nullable().optional(), - goalId: z.string().uuid().nullable().optional(), + goalId: z.string().guid().nullable().optional(), status: z.enum(ROUTINE_STATUSES).optional(), priority: z.enum(ISSUE_PRIORITIES).optional(), concurrencyPolicy: z.enum(ROUTINE_CONCURRENCY_POLICIES).optional(), @@ -1200,7 +1200,7 @@ export type InstallPlugin = z.infer; * the plugin's instanceConfigSchema is done at the service layer. */ export const upsertPluginConfigSchema = z.object({ - companyId: z.string().uuid(), + companyId: z.string().guid(), configJson: z.record(z.string(), z.unknown()), }); @@ -1211,7 +1211,7 @@ export type UpsertPluginConfig = z.infer; * Allows a partial merge of config values. */ export const patchPluginConfigSchema = z.object({ - companyId: z.string().uuid(), + companyId: z.string().guid(), configJson: z.record(z.string(), z.unknown()), }); diff --git a/packages/shared/src/validators/project.ts b/packages/shared/src/validators/project.ts index d56eb3a3b9..0668443867 100644 --- a/packages/shared/src/validators/project.ts +++ b/packages/shared/src/validators/project.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { PROJECT_STATUSES, PROJECT_ICON_NAMES } from "../constants.js"; import { envConfigSchema } from "./secret.js"; import { trustAuthorizationPolicySchema } from "./trust-policy.js"; +import { objectWithoutDefaults } from "./partial.js"; const executionWorkspaceStrategySchema = z .object({ @@ -21,8 +22,8 @@ export const projectExecutionWorkspacePolicySchema = z sharedWorkspaceConcurrency: z.enum(["auto", "serialize", "allow"]).optional(), defaultMode: z.enum(["shared_workspace", "isolated_workspace", "operator_branch", "adapter_default"]).optional(), allowIssueOverride: z.boolean().optional(), - defaultProjectWorkspaceId: z.string().uuid().optional().nullable(), - environmentId: z.string().uuid().optional().nullable(), + defaultProjectWorkspaceId: z.string().guid().optional().nullable(), + environmentId: z.string().guid().optional().nullable(), workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(), workspaceRuntime: z.record(z.string(), z.unknown()).optional().nullable(), branchPolicy: z.record(z.string(), z.unknown()).optional().nullable(), @@ -36,7 +37,7 @@ export const projectExecutionWorkspacePolicySchema = z export const projectWorkspaceRuntimeConfigSchema = z.object({ workspaceRuntime: z.record(z.string(), z.unknown()).optional().nullable(), desiredState: z.enum(["running", "stopped", "manual"]).optional().nullable(), - serviceStates: z.record(z.enum(["running", "stopped", "manual"])).optional().nullable(), + serviceStates: z.record(z.string(), z.enum(["running", "stopped", "manual"])).optional().nullable(), }).strict(); const projectWorkspaceSourceTypeSchema = z.enum(["local_path", "git_repo", "remote_managed", "non_git_path"]); @@ -101,12 +102,12 @@ export type UpdateProjectWorkspace = z.infer; -export const updateProjectSchema = z.object(projectFields).partial(); +export const updateProjectSchema = objectWithoutDefaults( + z.object(projectFields), +).partial(); export type UpdateProject = z.infer; diff --git a/packages/shared/src/validators/routine.ts b/packages/shared/src/validators/routine.ts index 1f27068cbf..5d8bc3a4a1 100644 --- a/packages/shared/src/validators/routine.ts +++ b/packages/shared/src/validators/routine.ts @@ -16,6 +16,7 @@ import { } from "./issue.js"; import { envConfigSchema } from "./secret.js"; import { isValidRoutineDateString } from "../routine-variables.js"; +import { objectWithoutDefaults } from "./partial.js"; const routineVariableValueSchema = z.union([z.string(), z.number().finite(), z.boolean()]); @@ -62,13 +63,13 @@ export const routineVariableSchema = z.object({ }); export const createRoutineSchema = z.object({ - projectId: z.string().uuid().optional().nullable(), - folderId: z.string().uuid().optional().nullable(), - goalId: z.string().uuid().optional().nullable(), - parentIssueId: z.string().uuid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + folderId: z.string().guid().optional().nullable(), + goalId: z.string().guid().optional().nullable(), + parentIssueId: z.string().guid().optional().nullable(), title: z.string().trim().min(1).max(200), description: z.string().optional().nullable(), - assigneeAgentId: z.string().uuid().optional().nullable(), + assigneeAgentId: z.string().guid().optional().nullable(), priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"), status: z.enum(ROUTINE_STATUSES).optional().default("active"), concurrencyPolicy: z.enum(ROUTINE_CONCURRENCY_POLICIES).optional().default("coalesce_if_active"), @@ -81,21 +82,21 @@ export const createRoutineSchema = z.object({ export type CreateRoutine = z.infer; -export const updateRoutineSchema = createRoutineSchema.partial().extend({ - baseRevisionId: z.string().uuid().optional().nullable(), +export const updateRoutineSchema = objectWithoutDefaults(createRoutineSchema).partial().extend({ + baseRevisionId: z.string().guid().optional().nullable(), }); export type UpdateRoutine = z.infer; export const routineRevisionSnapshotRoutineV1Schema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), - projectId: z.string().uuid().nullable(), - folderId: z.string().uuid().nullable().optional(), - goalId: z.string().uuid().nullable(), - parentIssueId: z.string().uuid().nullable(), + id: z.string().guid(), + companyId: z.string().guid(), + projectId: z.string().guid().nullable(), + folderId: z.string().guid().nullable().optional(), + goalId: z.string().guid().nullable(), + parentIssueId: z.string().guid().nullable(), title: z.string().trim().min(1).max(200), description: z.string().nullable(), - assigneeAgentId: z.string().uuid().nullable(), + assigneeAgentId: z.string().guid().nullable(), priority: z.enum(ISSUE_PRIORITIES), status: z.enum(ROUTINE_STATUSES), concurrencyPolicy: z.enum(ROUTINE_CONCURRENCY_POLICIES), @@ -108,7 +109,7 @@ export const routineRevisionSnapshotRoutineV1Schema = z.object({ }).strict(); export const routineRevisionSnapshotTriggerV1Schema = z.object({ - id: z.string().uuid(), + id: z.string().guid(), kind: z.enum(ROUTINE_TRIGGER_KINDS), label: z.string().nullable(), enabled: z.boolean(), @@ -164,15 +165,15 @@ export const updateRoutineTriggerSchema = z.object({ export type UpdateRoutineTrigger = z.infer; export const runRoutineSchema = z.object({ - triggerId: z.string().uuid().optional().nullable(), + triggerId: z.string().guid().optional().nullable(), payload: z.record(z.string(), z.unknown()).optional().nullable(), variables: z.record(z.string(), routineVariableValueSchema).optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - projectWorkspaceId: z.string().uuid().optional().nullable(), - assigneeAgentId: z.string().uuid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + projectWorkspaceId: z.string().guid().optional().nullable(), + assigneeAgentId: z.string().guid().optional().nullable(), idempotencyKey: z.string().trim().max(255).optional().nullable(), source: z.enum(["manual", "api"]).optional().default("manual"), - executionWorkspaceId: z.string().uuid().optional().nullable(), + executionWorkspaceId: z.string().guid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), }); diff --git a/packages/shared/src/validators/secret.ts b/packages/shared/src/validators/secret.ts index b0d55d8287..746be54ac0 100644 --- a/packages/shared/src/validators/secret.ts +++ b/packages/shared/src/validators/secret.ts @@ -19,7 +19,7 @@ export const envBindingPlainSchema = z.object({ export const envBindingSecretRefSchema = z.object({ type: z.literal("secret_ref"), - secretId: z.string().uuid(), + secretId: z.string().guid(), version: secretVersionSelectorSchema.optional(), projectionClass: z.enum(SECRET_PROJECTION_CLASSES).optional(), projectionAllowlistKey: z.string().trim().min(1).max(160).optional().nullable(), @@ -47,7 +47,7 @@ export const createSecretSchema = z.object({ name: z.string().min(1), key: secretKeySchema.optional(), provider: z.enum(SECRET_PROVIDERS).optional(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), managedMode: z.enum(SECRET_MANAGED_MODES).optional(), value: z.string().min(1).optional().nullable(), description: z.string().optional().nullable(), @@ -110,7 +110,7 @@ export const rotateSecretSchema = z.object({ value: z.string().min(1).optional().nullable(), externalRef: z.string().optional().nullable(), providerVersionRef: z.string().optional().nullable(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), }).superRefine(requireSecretRotationInput); export type RotateSecret = z.infer; @@ -119,7 +119,7 @@ export const updateSecretSchema = z.object({ name: z.string().min(1).optional(), key: secretKeySchema.optional(), status: z.enum(SECRET_STATUSES).optional(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), description: z.string().optional().nullable(), externalRef: z.string().optional().nullable(), providerMetadata: z.record(z.string(), z.unknown()).optional().nullable(), @@ -134,7 +134,7 @@ export const secretBindingTargetSchema = z.object({ }); export const createSecretBindingSchema = secretBindingTargetSchema.extend({ - secretId: z.string().uuid(), + secretId: z.string().guid(), versionSelector: secretVersionSelectorSchema.default("latest"), required: z.boolean().default(true), label: z.string().optional().nullable(), @@ -150,7 +150,7 @@ export const createUserSecretDefinitionSchema = z.object({ description: z.string().trim().max(500).optional().nullable(), status: creatableSecretStatusSchema.optional(), provider: z.enum(SECRET_PROVIDERS).optional(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), managedMode: z.enum(SECRET_MANAGED_MODES).optional(), providerMetadata: z.record(z.string(), z.unknown()).optional().nullable(), usageGuidance: z.string().trim().max(1000).optional().nullable(), @@ -162,7 +162,7 @@ export const updateUserSecretDefinitionSchema = z.object({ name: z.string().trim().min(1).max(160).optional(), description: z.string().trim().max(500).optional().nullable(), status: z.enum(SECRET_STATUSES).optional(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), providerMetadata: z.record(z.string(), z.unknown()).optional().nullable(), usageGuidance: z.string().trim().max(1000).optional().nullable(), }); @@ -171,11 +171,11 @@ export type UpdateUserSecretDefinition = z.infer { if (!value.definitionKey && !value.definitionId) { ctx.addIssue({ @@ -200,7 +200,7 @@ export const updateUserSecretValueSchema = z.object({ value: z.string().min(1).optional().nullable(), externalRef: z.string().min(1).optional().nullable(), providerVersionRef: z.string().min(1).optional().nullable(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), }); export type UpdateUserSecretValue = z.infer; @@ -209,7 +209,7 @@ export const rotateUserSecretValueSchema = z.object({ value: z.string().min(1).optional().nullable(), externalRef: z.string().min(1).optional().nullable(), providerVersionRef: z.string().min(1).optional().nullable(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), }).superRefine(requireSecretRotationInput); export type RotateUserSecretValue = z.infer; @@ -376,7 +376,7 @@ export const updateSecretProviderConfigSchema = z.object({ export type UpdateSecretProviderConfig = z.infer; export const remoteSecretImportPreviewSchema = z.object({ - providerConfigId: z.string().uuid(), + providerConfigId: z.string().guid(), query: z.string().trim().max(200).optional().nullable(), nextToken: z.string().trim().min(1).max(4096).optional().nullable(), pageSize: z.number().int().min(1).max(100).optional(), @@ -386,7 +386,7 @@ export type RemoteSecretImportPreview = z.infer new Set(values).size === values.length, "Agent IDs must be unique"), }).strict(), z.object({ type: z.literal("roles"), roles: nonEmptyUniqueStrings }).strict(), ]); export const skillPolicyResourceSelectorSchema = z.object({ - skillIds: z.array(z.string().uuid()).min(1).max(500).optional(), + skillIds: z.array(z.string().guid()).min(1).max(500).optional(), skillKeys: nonEmptyUniqueStrings.optional(), sourceTypes: z.array(skillPolicySourceTypeSchema).min(1) .refine((values) => new Set(values).size === values.length, "Source types must be unique") @@ -116,7 +116,7 @@ export const replaceSkillPolicySchema = skillPolicyDocumentSchema.extend({ }).strict(); export const skillPolicyEvaluationResourceSchema = z.object({ - skillId: z.string().uuid().optional(), + skillId: z.string().guid().optional(), skillKey: z.string().trim().min(1).max(512).optional(), sourceType: skillPolicySourceTypeSchema.optional(), sourceLocator: skillPolicySourceLocatorSchema.optional(), @@ -125,7 +125,7 @@ export const skillPolicyEvaluationResourceSchema = z.object({ export const evaluateSkillPolicySchema = z.object({ action: skillPolicyActionSchema, resource: skillPolicyEvaluationResourceSchema.default({}), - principal: z.object({ agentId: z.string().uuid() }).strict().optional(), + principal: z.object({ agentId: z.string().guid() }).strict().optional(), }).strict(); export type SkillPolicyAction = z.infer; diff --git a/packages/shared/src/validators/status-card.ts b/packages/shared/src/validators/status-card.ts index 327d78cbf7..f6b6f1d3f6 100644 --- a/packages/shared/src/validators/status-card.ts +++ b/packages/shared/src/validators/status-card.ts @@ -32,7 +32,7 @@ export const statusCardRefreshPolicySchema = z intervalMinutes: z.number().int().positive().optional(), debounceSeconds: z.number().int().positive().optional(), maxUpdatesPerHour: z.number().int().positive().optional(), - triggers: statusCardRefreshTriggersSchema.default({}), + triggers: statusCardRefreshTriggersSchema.prefault({}), activeHours: z .object({ start: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/), @@ -61,41 +61,41 @@ export const statusCardFingerprintSchema = z.record( latestHumanCommentAt: z.string().datetime().nullable().optional(), identifier: z.string().nullable().optional(), title: z.string().optional(), - assigneeAgentId: z.string().uuid().nullable().optional(), + assigneeAgentId: z.string().guid().nullable().optional(), assigneeUserId: z.string().nullable().optional(), }), ); export const statusCardSchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), + id: z.string().guid(), + companyId: z.string().guid(), createdByUserId: z.string().nullable(), - createdByAgentId: z.string().uuid().nullable(), + createdByAgentId: z.string().guid().nullable(), title: z.string().nullable(), titlePinned: z.boolean(), interestPrompt: z.string(), queries: z.array(companySearchQuerySchema), queryVersion: z.number().int().nonnegative(), queryCompiledAt: z.string().datetime().nullable(), - queryCompiledByAgentId: z.string().uuid().nullable(), - agentId: z.string().uuid().nullable(), + queryCompiledByAgentId: z.string().guid().nullable(), + agentId: z.string().guid().nullable(), refreshPolicy: statusCardRefreshPolicySchema, state: statusCardStateSchema, pendingChangeCount: z.number().int().nonnegative(), lastChangeAt: z.string().datetime().nullable(), fingerprint: statusCardFingerprintSchema.nullable(), fingerprintAt: z.string().datetime().nullable(), - mentionedIssueIds: z.array(z.string().uuid()).default([]), - documentId: z.string().uuid().nullable(), + mentionedIssueIds: z.array(z.string().guid()).default([]), + documentId: z.string().guid().nullable(), lastUpdateRunKind: z.enum(["full", "incremental"]).nullable(), lastGeneratedAt: z.string().datetime().nullable(), lastModel: z.string().nullable(), - generatingIssueId: z.string().uuid().nullable(), + generatingIssueId: z.string().guid().nullable(), failureReason: z.string().nullable(), nextEvalAt: z.string().datetime().nullable(), archivedAt: z.string().datetime().nullable(), archivedByUserId: z.string().nullable(), - archivedByAgentId: z.string().uuid().nullable(), + archivedByAgentId: z.string().guid().nullable(), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), summaryBody: z.string().nullable().optional(), @@ -105,7 +105,7 @@ export const statusCardSchema = z.object({ }); export const statusCardUpdateChangeSchema = z.object({ - issueId: z.string().uuid(), + issueId: z.string().guid(), identifier: z.string(), from: z.string().nullable(), to: z.string().nullable(), @@ -113,12 +113,12 @@ export const statusCardUpdateChangeSchema = z.object({ }); export const statusCardUpdateSchema = z.object({ - id: z.string().uuid(), - cardId: z.string().uuid(), + id: z.string().guid(), + cardId: z.string().guid(), kind: statusCardUpdateKindSchema, trigger: statusCardUpdateTriggerSchema, - generationIssueId: z.string().uuid().nullable(), - runId: z.string().uuid().nullable(), + generationIssueId: z.string().guid().nullable(), + runId: z.string().guid().nullable(), changes: z.array(statusCardUpdateChangeSchema), inputTokens: z.number().int().nonnegative(), outputTokens: z.number().int().nonnegative(), @@ -133,7 +133,7 @@ export const statusCardUpdateSchema = z.object({ }); export const statusCardSummaryRevisionSchema = z.object({ - id: z.string().uuid(), + id: z.string().guid(), revisionNumber: z.number().int().positive(), title: z.string().nullable(), body: z.string(), @@ -152,7 +152,7 @@ export const createStatusCardSchema = z.object({ interestPrompt: z.string().trim().min(1).max(20_000), title: z.string().trim().min(1).max(300).optional(), titlePinned: z.boolean().default(false), - agentId: z.string().uuid().nullable().optional(), + agentId: z.string().guid().nullable().optional(), refreshPolicy: statusCardRefreshPolicySchema.default(defaultStatusCardRefreshPolicy), }); @@ -161,7 +161,7 @@ export const patchStatusCardSchema = z interestPrompt: z.string().trim().min(1).max(20_000).optional(), title: z.string().trim().min(1).max(300).nullable().optional(), titlePinned: z.boolean().optional(), - agentId: z.string().uuid().nullable().optional(), + agentId: z.string().guid().nullable().optional(), refreshPolicy: statusCardRefreshPolicySchema.optional(), archived: z.boolean().optional(), }) @@ -175,14 +175,14 @@ export const writeStatusCardQuerySchema = z.object({ queries: z.array(companySearchQuerySchema).min(1).max(10), title: z.string().trim().min(1).max(300), changeSummary: z.string().trim().min(1).max(2_000), - generationIssueId: z.string().uuid(), + generationIssueId: z.string().guid(), }); export const writeStatusCardSummarySchema = z.object({ markdown: z.string().trim().min(1).max(200_000), title: z.string().trim().min(1).max(300).optional(), changeSummary: z.string().trim().min(1).max(2_000), - generationIssueId: z.string().uuid(), + generationIssueId: z.string().guid(), model: z.string().trim().min(1).max(200).optional().nullable(), }); diff --git a/packages/shared/src/validators/summary-slot.ts b/packages/shared/src/validators/summary-slot.ts index c2c950068f..44005a160d 100644 --- a/packages/shared/src/validators/summary-slot.ts +++ b/packages/shared/src/validators/summary-slot.ts @@ -5,7 +5,7 @@ import { SUMMARY_SLOT_STATUSES, } from "../constants.js"; -const optionalScopeIdSchema = z.string().uuid().optional().nullable(); +const optionalScopeIdSchema = z.string().guid().optional().nullable(); export const summarySlotScopeKindSchema = z.enum(SUMMARY_SLOT_SCOPE_KINDS); export const summarySlotKeySchema = z.enum(SUMMARY_SLOT_KEYS); @@ -53,8 +53,8 @@ export const writeSummarySlotSchema = z markdown: z.string().trim().min(1).max(200_000), title: z.string().trim().min(1).max(200).optional().nullable(), changeSummary: z.string().trim().min(1).max(1_000).optional().nullable(), - baseRevisionId: z.string().uuid().optional().nullable(), - generationIssueId: z.string().uuid().optional().nullable(), + baseRevisionId: z.string().guid().optional().nullable(), + generationIssueId: z.string().guid().optional().nullable(), model: z.string().trim().min(1).max(200).optional().nullable(), }) .strict(); diff --git a/packages/shared/src/validators/tool-access.ts b/packages/shared/src/validators/tool-access.ts index 01071fff7c..0dd7e0ca32 100644 --- a/packages/shared/src/validators/tool-access.ts +++ b/packages/shared/src/validators/tool-access.ts @@ -31,6 +31,7 @@ import { TOOL_RUNTIME_SLOT_STATUSES, } from "../constants.js"; import { jsonSchemaSchema } from "./plugin.js"; +import { objectWithoutDefaults } from "./partial.js"; export const toolApplicationTypeSchema = z.enum(TOOL_APPLICATION_TYPES); export const toolApplicationStatusSchema = z.enum(TOOL_APPLICATION_STATUSES); @@ -91,7 +92,7 @@ function rejectSensitiveConfigKeys(value: unknown, ctx: z.RefinementCtx, path: A } export const toolCredentialSecretRefSchema = z.object({ - secretId: z.string().uuid(), + secretId: z.string().guid(), versionSelector: z.union([z.literal("latest"), z.number().int().positive()]).optional(), configPath: z.string().trim().min(1).max(200), required: z.boolean().optional(), @@ -104,7 +105,7 @@ export const toolCredentialSecretRefSchema = z.object({ export const mcpConnectionCredentialRefSchema = z.object({ name: z.string().trim().min(1).max(120), - secretId: z.string().uuid(), + secretId: z.string().guid(), version: z.union([z.literal("latest"), z.number().int().positive()]).optional(), placement: toolCredentialPlacementSchema, key: z.string().trim().min(1).max(160), @@ -118,7 +119,7 @@ export const toolRedactedValueSummarySchema = z.object({ sizeBytes: z.number().int().min(0).optional().nullable(), sha256: z.string().trim().regex(/^[a-f0-9]{64}$/i).optional().nullable(), redactedFields: z.array(z.string().trim().min(1).max(200)).default([]).optional(), - artifactId: z.string().uuid().optional().nullable(), + artifactId: z.string().guid().optional().nullable(), }); export const createToolApplicationSchema = z.object({ @@ -127,8 +128,8 @@ export const createToolApplicationSchema = z.object({ description: z.string().max(4000).optional().nullable(), type: toolApplicationTypeSchema, status: toolApplicationStatusSchema.optional(), - pluginId: z.string().uuid().optional().nullable(), - ownerAgentId: z.string().uuid().optional().nullable(), + pluginId: z.string().guid().optional().nullable(), + ownerAgentId: z.string().guid().optional().nullable(), ownerUserId: z.string().optional().nullable(), metadata: z.record(z.string(), z.unknown()).optional().nullable(), }); @@ -143,7 +144,7 @@ export const updateToolApplicationSchema = createToolApplicationSchema.partial() export type UpdateToolApplication = z.infer; export const createToolConnectionSchema = z.object({ - applicationId: z.string().uuid().optional(), + applicationId: z.string().guid().optional(), applicationName: z.string().trim().min(1).max(160).optional(), name: z.string().trim().min(1).max(160), transport: toolConnectionTransportSchema.optional(), @@ -160,7 +161,7 @@ export const createToolConnectionSchema = z.object({ export type CreateToolConnection = z.infer; -export const updateToolConnectionSchema = createToolConnectionSchema.omit({ applicationId: true }).partial().refine( +export const updateToolConnectionSchema = objectWithoutDefaults(createToolConnectionSchema.omit({ applicationId: true })).partial().refine( (value) => Object.keys(value).length > 0, { message: "At least one tool connection field is required" }, ); @@ -168,9 +169,9 @@ export const updateToolConnectionSchema = createToolConnectionSchema.omit({ appl export type UpdateToolConnection = z.infer; export const connectionGrantSchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), - connectionId: z.string().uuid(), + id: z.string().guid(), + companyId: z.string().guid(), + connectionId: z.string().guid(), kind: connectionGrantKindSchema, subjectUserId: z.string().nullable(), providerTenant: z.object({ @@ -180,10 +181,10 @@ export const connectionGrantSchema = z.object({ credentialSecretRefs: z.array(toolCredentialSecretRefSchema), status: connectionGrantStatusSchema, isDefault: z.boolean(), - createdByAgentId: z.string().uuid().nullable(), + createdByAgentId: z.string().guid().nullable(), createdByUserId: z.string().nullable(), revokedAt: z.coerce.date().nullable(), - revokedByAgentId: z.string().uuid().nullable(), + revokedByAgentId: z.string().guid().nullable(), revokedByUserId: z.string().nullable(), lastUsedAt: z.coerce.date().nullable(), createdAt: z.coerce.date(), @@ -219,7 +220,7 @@ export const connectionTokenRequestSchema = z.object({ subject: connectionTokenSubjectSchema.optional().default({ type: "app" }), scope: connectionTokenScopeSchema.optional(), requestedTtlSeconds: z.number().int().positive().max(86_400).optional(), - grantId: z.string().uuid().optional(), + grantId: z.string().guid().optional(), }).strict(); export const startConnectionAuthorizationSchema = z.object({ @@ -264,7 +265,7 @@ export const connectToolAppSchema = z.object({ name: z.string().trim().min(1).max(160).optional(), credentialValues: z.record(z.string().trim().min(1).max(200), z.string().min(1)).optional(), configValues: z.record(z.string().trim().min(1).max(200), z.unknown()).optional(), - applicationId: z.string().uuid().optional(), + applicationId: z.string().guid().optional(), }).refine( (value) => Boolean(value.galleryKey) !== Boolean(value.link), { message: "Provide exactly one of galleryKey or link" }, @@ -279,20 +280,20 @@ export const reconnectToolAppSchema = z.object({ export type ReconnectToolApp = z.infer; export const finishToolAppSchema = z.object({ - enabledCatalogEntryIds: z.array(z.string().uuid()).max(500).default([]), - askFirstCatalogEntryIds: z.array(z.string().uuid()).max(500).default([]), - reviewedCatalogEntryIds: z.array(z.string().uuid()).max(500).optional(), + enabledCatalogEntryIds: z.array(z.string().guid()).max(500).default([]), + askFirstCatalogEntryIds: z.array(z.string().guid()).max(500).default([]), + reviewedCatalogEntryIds: z.array(z.string().guid()).max(500).optional(), access: z.union([ z.literal("all_agents"), - z.object({ agentIds: z.array(z.string().uuid()).min(1).max(250) }), + z.object({ agentIds: z.array(z.string().guid()).min(1).max(250) }), ]), }); export type FinishToolApp = z.infer; export const upsertToolCatalogEntrySchema = z.object({ - applicationId: z.string().uuid(), - connectionId: z.string().uuid(), + applicationId: z.string().guid(), + connectionId: z.string().guid(), entryKind: toolCatalogEntryKindSchema.default("tool"), toolName: z.string().trim().min(1).max(240), title: z.string().trim().max(240).optional().nullable(), @@ -322,7 +323,7 @@ export const createToolProfileSchema = z.object({ export type CreateToolProfile = z.infer; -export const updateToolProfileSchema = createToolProfileSchema.partial().refine( +export const updateToolProfileSchema = objectWithoutDefaults(createToolProfileSchema).partial().refine( (value) => Object.keys(value).length > 0, { message: "At least one tool profile field is required" }, ); @@ -330,12 +331,12 @@ export const updateToolProfileSchema = createToolProfileSchema.partial().refine( export type UpdateToolProfile = z.infer; export const createToolProfileEntrySchema = z.object({ - profileId: z.string().uuid(), + profileId: z.string().guid(), selectorType: toolProfileEntrySelectorTypeSchema, effect: toolProfileEntryEffectSchema.default("include"), - applicationId: z.string().uuid().optional().nullable(), - connectionId: z.string().uuid().optional().nullable(), - catalogEntryId: z.string().uuid().optional().nullable(), + applicationId: z.string().guid().optional().nullable(), + connectionId: z.string().guid().optional().nullable(), + catalogEntryId: z.string().guid().optional().nullable(), toolName: z.string().trim().min(1).max(240).optional().nullable(), riskLevel: toolRiskLevelSchema.optional().nullable(), conditions: z.record(z.string(), z.unknown()).optional().nullable(), @@ -347,7 +348,7 @@ export const createToolProfileEntryForProfileSchema = createToolProfileEntrySche export type CreateToolProfileEntryForProfile = z.infer; -export const updateToolProfileEntrySchema = createToolProfileEntryForProfileSchema.partial().refine( +export const updateToolProfileEntrySchema = objectWithoutDefaults(createToolProfileEntryForProfileSchema).partial().refine( (value) => Object.keys(value).length > 0, { message: "At least one tool profile entry field is required" }, ); @@ -367,7 +368,7 @@ export const duplicateToolProfileSchema = z.object({ export type DuplicateToolProfile = z.infer; -export const updateToolProfileWithEntriesSchema = createToolProfileSchema.partial().extend({ +export const updateToolProfileWithEntriesSchema = objectWithoutDefaults(createToolProfileSchema).partial().extend({ entries: z.array(createToolProfileEntryForProfileSchema).max(250).optional(), }).refine( (value) => Object.keys(value).length > 0, @@ -378,7 +379,7 @@ export type UpdateToolProfileWithEntries = z.infer; export const createToolProfileBindingSchema = z.object({ - profileId: z.string().uuid(), + profileId: z.string().guid(), targetType: toolProfileBindingTargetTypeSchema, targetId: z.string().trim().min(1).max(200), priority: z.number().int().min(0).max(10000).default(100), @@ -423,14 +424,14 @@ export const toolMcpGatewayAuthConfigSchema = z.object({ defaultTtlSeconds: z.number().int().positive().max(31_536_000).nullable().default(7_776_000), requireFiniteExpiry: z.boolean().default(true), longLivedTokenRequiresOverride: z.boolean().default(true), - }).default({}), + }).prefault({}), oauth: z.object({ enabled: z.literal(false).default(false), reservedFor: z.literal("v1_5").default("v1_5"), protectedResourceMetadataPath: z.string().trim().max(240).optional().nullable(), dynamicClientRegistration: z.literal(false).optional(), authorizationCodePkce: z.literal(false).optional(), - }).default({}), + }).prefault({}), }); export const toolMcpGatewayHeaderPolicySchema = z.object({ @@ -438,7 +439,7 @@ export const toolMcpGatewayHeaderPolicySchema = z.object({ callerPassthrough: z.object({ enabled: z.boolean().default(false), allowedHeaders: z.array(headerNameSchema).max(50).default([]), - }).default({}), + }).prefault({}), staticHeaders: z.array(z.object({ name: headerNameSchema, valueRef: z.string().trim().max(240).optional().nullable(), @@ -447,11 +448,11 @@ export const toolMcpGatewayHeaderPolicySchema = z.object({ generatedMetadata: z.object({ enabled: z.boolean().default(false), allowedHeaders: z.array(headerNameSchema).max(20).default([]), - }).default({}), + }).prefault({}), responseHeaders: z.object({ forwardMcpRequiredHeaders: z.boolean().default(true), forwardSafeCacheHeaders: z.boolean().default(true), - }).default({}), + }).prefault({}), }); export const toolMcpGatewayMetadataPolicySchema = z.object({ @@ -476,14 +477,14 @@ export const createToolMcpGatewaySchema = z.object({ slug: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(), displaySlug: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(), description: z.string().max(4000).optional().nullable(), - profileId: z.string().uuid(), + profileId: z.string().guid(), defaultProfileMode: toolMcpGatewayDefaultProfileModeSchema.default("gateway_only").optional(), contextScopeType: toolMcpGatewayContextScopeTypeSchema.default("none").optional(), contextScopeId: z.string().trim().min(1).max(200).optional().nullable(), - agentId: z.string().uuid().optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - issueId: z.string().uuid().optional().nullable(), - approvalIssueId: z.string().uuid().optional().nullable(), + agentId: z.string().guid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + issueId: z.string().guid().optional().nullable(), + approvalIssueId: z.string().guid().optional().nullable(), authConfig: toolMcpGatewayAuthConfigSchema.optional(), headerPolicy: toolMcpGatewayHeaderPolicySchema.optional(), metadataPolicy: toolMcpGatewayMetadataPolicySchema.optional(), @@ -493,7 +494,7 @@ export const createToolMcpGatewaySchema = z.object({ export type CreateToolMcpGateway = z.infer; -export const updateToolMcpGatewaySchema = createToolMcpGatewaySchema +export const updateToolMcpGatewaySchema = objectWithoutDefaults(createToolMcpGatewaySchema) .partial() .extend({ status: toolMcpGatewayStatusSchema.optional() }) .refine((value) => Object.keys(value).length > 0, { message: "At least one gateway field is required" }); @@ -554,29 +555,29 @@ const timeWindowConditionSchema = z.object({ const actorConditionSchema = z.object({ actorType: z.enum(["agent", "user", "system", "plugin"]).optional(), actorTypes: z.array(z.enum(["agent", "user", "system", "plugin"])).max(20).optional(), - agentId: z.string().uuid().optional(), - agentIds: z.array(z.string().uuid()).max(100).optional(), + agentId: z.string().guid().optional(), + agentIds: z.array(z.string().guid()).max(100).optional(), }).strict(); const contextConditionSchema = z.object({ - projectId: z.string().uuid().optional(), - projectIds: z.array(z.string().uuid()).max(100).optional(), - routineId: z.string().uuid().optional(), - routineIds: z.array(z.string().uuid()).max(100).optional(), - issueId: z.string().uuid().optional(), - issueIds: z.array(z.string().uuid()).max(100).optional(), + projectId: z.string().guid().optional(), + projectIds: z.array(z.string().guid()).max(100).optional(), + routineId: z.string().guid().optional(), + routineIds: z.array(z.string().guid()).max(100).optional(), + issueId: z.string().guid().optional(), + issueIds: z.array(z.string().guid()).max(100).optional(), requireIssue: z.boolean().optional(), requireProject: z.boolean().optional(), requireRoutine: z.boolean().optional(), }).strict(); const credentialScopeConditionSchema = z.object({ - applicationId: z.string().uuid().optional(), - applicationIds: z.array(z.string().uuid()).max(100).optional(), - connectionId: z.string().uuid().optional(), - connectionIds: z.array(z.string().uuid()).max(100).optional(), - catalogEntryId: z.string().uuid().optional(), - catalogEntryIds: z.array(z.string().uuid()).max(100).optional(), + applicationId: z.string().guid().optional(), + applicationIds: z.array(z.string().guid()).max(100).optional(), + connectionId: z.string().guid().optional(), + connectionIds: z.array(z.string().guid()).max(100).optional(), + catalogEntryId: z.string().guid().optional(), + catalogEntryIds: z.array(z.string().guid()).max(100).optional(), applicationKey: z.string().trim().min(1).max(160).optional(), applicationKeys: z.array(z.string().trim().min(1).max(160)).max(100).optional(), providerType: z.string().trim().min(1).max(160).optional(), @@ -622,7 +623,7 @@ export const createToolPolicySchema = z.object({ export type CreateToolPolicy = z.infer; -export const updateToolPolicySchema = createToolPolicySchema.partial().refine( +export const updateToolPolicySchema = objectWithoutDefaults(createToolPolicySchema).partial().refine( (value) => Object.keys(value).length > 0, { message: "At least one tool policy field is required" }, ); @@ -630,7 +631,7 @@ export const updateToolPolicySchema = createToolPolicySchema.partial().refine( export type UpdateToolPolicy = z.infer; export const reorderToolPoliciesSchema = z.object({ - policyIds: z.array(z.string().uuid()).min(1).max(500), + policyIds: z.array(z.string().guid()).min(1).max(500), }); export type ReorderToolPolicies = z.infer; @@ -643,11 +644,11 @@ export type DuplicateToolPolicy = z.infer; export const createToolInvocationSchema = z.object({ idempotencyKey: z.string().trim().min(1).max(300).optional().nullable(), - issueId: z.string().uuid().optional().nullable(), - runId: z.string().uuid().optional().nullable(), - applicationId: z.string().uuid().optional().nullable(), - connectionId: z.string().uuid().optional().nullable(), - catalogEntryId: z.string().uuid().optional().nullable(), + issueId: z.string().guid().optional().nullable(), + runId: z.string().guid().optional().nullable(), + applicationId: z.string().guid().optional().nullable(), + connectionId: z.string().guid().optional().nullable(), + catalogEntryId: z.string().guid().optional().nullable(), toolName: z.string().trim().min(1).max(240), argumentsHash: z.string().trim().max(128).optional().nullable(), argumentsSummary: toolRedactedValueSummarySchema.optional().nullable(), @@ -656,8 +657,8 @@ export const createToolInvocationSchema = z.object({ export type CreateToolInvocation = z.infer; export const createToolActionRequestSchema = z.object({ - invocationId: z.string().uuid(), - issueId: z.string().uuid().optional().nullable(), + invocationId: z.string().guid(), + issueId: z.string().guid().optional().nullable(), canonicalArgumentsHash: z.string().trim().min(1).max(128), canonicalArgumentsSummary: toolRedactedValueSummarySchema, signedArguments: z.string().trim().max(4096).optional().nullable(), @@ -668,7 +669,7 @@ export const createToolActionRequestSchema = z.object({ export type CreateToolActionRequest = z.infer; export const toolConnectionTestCallSchema = z.object({ - agentId: z.string().uuid(), + agentId: z.string().guid(), toolName: z.string().trim().min(1).max(240), parameters: z.unknown().optional(), }); @@ -683,31 +684,31 @@ export type ImportMcpJson = z.infer; export const toolAccessSelectorSchema = z.object({ actorType: z.enum(["agent", "user", "system", "plugin"]).optional(), - agentId: z.string().uuid().optional(), - agentIds: z.array(z.string().uuid()).optional(), - projectId: z.string().uuid().optional(), - projectIds: z.array(z.string().uuid()).optional(), - routineId: z.string().uuid().optional(), - routineIds: z.array(z.string().uuid()).optional(), - issueId: z.string().uuid().optional(), - issueIds: z.array(z.string().uuid()).optional(), - gatewayId: z.string().uuid().optional(), - gatewayIds: z.array(z.string().uuid()).optional(), + agentId: z.string().guid().optional(), + agentIds: z.array(z.string().guid()).optional(), + projectId: z.string().guid().optional(), + projectIds: z.array(z.string().guid()).optional(), + routineId: z.string().guid().optional(), + routineIds: z.array(z.string().guid()).optional(), + issueId: z.string().guid().optional(), + issueIds: z.array(z.string().guid()).optional(), + gatewayId: z.string().guid().optional(), + gatewayIds: z.array(z.string().guid()).optional(), gatewayPublicId: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(), gatewayPublicIds: z.array(z.string().trim().min(1).max(120).regex(safeKeyPattern)).optional(), - gatewayTokenId: z.string().uuid().optional(), - gatewayTokenIds: z.array(z.string().uuid()).optional(), + gatewayTokenId: z.string().guid().optional(), + gatewayTokenIds: z.array(z.string().guid()).optional(), clientSubjectType: toolMcpGatewayTokenSubjectTypeSchema.optional(), clientSubjectTypes: z.array(toolMcpGatewayTokenSubjectTypeSchema).optional(), clientName: z.string().trim().min(1).max(160).optional(), clientNames: z.array(z.string().trim().min(1).max(160)).optional(), externalClient: z.boolean().optional(), - applicationId: z.string().uuid().optional(), - applicationIds: z.array(z.string().uuid()).optional(), - connectionId: z.string().uuid().optional(), - connectionIds: z.array(z.string().uuid()).optional(), - catalogEntryId: z.string().uuid().optional(), - catalogEntryIds: z.array(z.string().uuid()).optional(), + applicationId: z.string().guid().optional(), + applicationIds: z.array(z.string().guid()).optional(), + connectionId: z.string().guid().optional(), + connectionIds: z.array(z.string().guid()).optional(), + catalogEntryId: z.string().guid().optional(), + catalogEntryIds: z.array(z.string().guid()).optional(), toolName: z.string().trim().min(1).max(240).optional(), toolNames: z.array(z.string().trim().min(1).max(240)).optional(), riskLevel: toolRiskLevelSchema.optional(), @@ -780,29 +781,29 @@ export const revokeToolTrustRuleSchema = z.object({ export type RevokeToolTrustRule = z.infer; export const toolPolicyTestRequestSchema = z.object({ - companyId: z.string().uuid(), + companyId: z.string().guid(), actor: z.object({ actorType: z.enum(["agent", "user", "system", "plugin"]), actorId: z.string().trim().min(1).max(240), - agentId: z.string().uuid().optional().nullable(), + agentId: z.string().guid().optional().nullable(), }), runContext: z.object({ - heartbeatRunId: z.string().uuid().optional().nullable(), - issueId: z.string().uuid().optional().nullable(), - projectId: z.string().uuid().optional().nullable(), - routineId: z.string().uuid().optional().nullable(), - gatewayId: z.string().uuid().optional().nullable(), + heartbeatRunId: z.string().guid().optional().nullable(), + issueId: z.string().guid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + routineId: z.string().guid().optional().nullable(), + gatewayId: z.string().guid().optional().nullable(), gatewayPublicId: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional().nullable(), - gatewayTokenId: z.string().uuid().optional().nullable(), + gatewayTokenId: z.string().guid().optional().nullable(), clientSubjectType: toolMcpGatewayTokenSubjectTypeSchema.optional().nullable(), clientSubjectId: z.string().trim().min(1).max(240).optional().nullable(), clientName: z.string().trim().min(1).max(160).optional().nullable(), externalClient: z.boolean().optional().nullable(), }).optional().nullable(), request: z.object({ - applicationId: z.string().uuid().optional().nullable(), - connectionId: z.string().uuid().optional().nullable(), - catalogEntryId: z.string().uuid().optional().nullable(), + applicationId: z.string().guid().optional().nullable(), + connectionId: z.string().guid().optional().nullable(), + catalogEntryId: z.string().guid().optional().nullable(), toolName: z.string().trim().min(1).max(240), arguments: z.unknown().optional(), idempotencyKey: z.string().trim().min(1).max(512).optional().nullable(), diff --git a/packages/shared/src/validators/trust-policy.ts b/packages/shared/src/validators/trust-policy.ts index 39496b5e43..f16b002db9 100644 --- a/packages/shared/src/validators/trust-policy.ts +++ b/packages/shared/src/validators/trust-policy.ts @@ -10,17 +10,17 @@ export const trustPresetSchema = z.enum(TRUST_PRESETS); export const lowTrustOutputPromotionTargetSchema = z.object({ type: z.literal("issue"), - issueId: z.string().uuid(), + issueId: z.string().guid(), }).strict(); export const lowTrustBoundarySchema = z.object({ mode: z.literal(LOW_TRUST_REVIEW_PRESET), - companyId: z.string().uuid().optional(), - projectIds: z.array(z.string().uuid()).optional(), - rootIssueId: z.string().uuid().optional(), - issueIds: z.array(z.string().uuid()).optional(), - allowedAgentIds: z.array(z.string().uuid()).optional(), - allowedSecretBindingIds: z.array(z.string().uuid()).optional(), + companyId: z.string().guid().optional(), + projectIds: z.array(z.string().guid()).optional(), + rootIssueId: z.string().guid().optional(), + issueIds: z.array(z.string().guid()).optional(), + allowedAgentIds: z.array(z.string().guid()).optional(), + allowedSecretBindingIds: z.array(z.string().guid()).optional(), allowedToolClasses: z.array(z.string().trim().min(1)).optional(), outputPromotionTarget: lowTrustOutputPromotionTargetSchema.optional(), }).strict(); @@ -58,13 +58,13 @@ export const sourceTrustArtifactKindSchema = z.enum(["issue", "comment", "docume export const sourceTrustMetadataSchema = z.object({ preset: trustPresetSchema, disposition: z.enum(["quarantined", "promoted"]), - sourceIssueId: z.string().uuid().nullable().optional(), - sourceRunId: z.string().uuid().nullable().optional(), - sourceAgentId: z.string().uuid().nullable().optional(), + sourceIssueId: z.string().guid().nullable().optional(), + sourceRunId: z.string().guid().nullable().optional(), + sourceAgentId: z.string().guid().nullable().optional(), promotedFrom: z.object({ artifactKind: sourceTrustArtifactKindSchema, - artifactId: z.string().uuid(), - issueId: z.string().uuid().nullable().optional(), + artifactId: z.string().guid(), + issueId: z.string().guid().nullable().optional(), }).strict().nullable().optional(), promotedByActorType: z.enum(["agent", "user", "system"]).nullable().optional(), promotedByActorId: z.string().trim().min(1).nullable().optional(), diff --git a/packages/shared/src/validators/work-product.ts b/packages/shared/src/validators/work-product.ts index 3ed27ba6d6..0934852449 100644 --- a/packages/shared/src/validators/work-product.ts +++ b/packages/shared/src/validators/work-product.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { workspaceFileRefSchema } from "./workspace-file-resource.js"; +import { objectWithoutDefaults } from "./partial.js"; function attachmentContentPath(attachmentId: string): string { return `/api/attachments/${attachmentId}/content`; @@ -35,7 +36,7 @@ export const issueWorkProductReviewStateSchema = z.enum([ ]); export const attachmentArtifactWorkProductMetadataSchema = z.object({ - attachmentId: z.string().uuid(), + attachmentId: z.string().guid(), contentType: z.string().min(1), byteSize: z.number().int().nonnegative(), contentPath: z.string().min(1), @@ -78,9 +79,9 @@ export const issueWorkProductMetadataSchema = z export type IssueWorkProductMetadata = z.infer; export const createIssueWorkProductSchema = z.object({ - projectId: z.string().uuid().optional().nullable(), - executionWorkspaceId: z.string().uuid().optional().nullable(), - runtimeServiceId: z.string().uuid().optional().nullable(), + projectId: z.string().guid().optional().nullable(), + executionWorkspaceId: z.string().guid().optional().nullable(), + runtimeServiceId: z.string().guid().optional().nullable(), type: issueWorkProductTypeSchema, provider: z.string().min(1), externalId: z.string().optional().nullable(), @@ -92,11 +93,13 @@ export const createIssueWorkProductSchema = z.object({ healthStatus: z.enum(["unknown", "healthy", "unhealthy"]).optional().default("unknown"), summary: z.string().optional().nullable(), metadata: issueWorkProductMetadataSchema.optional().nullable(), - createdByRunId: z.string().uuid().optional().nullable(), + createdByRunId: z.string().guid().optional().nullable(), }); export type CreateIssueWorkProduct = z.infer; -export const updateIssueWorkProductSchema = createIssueWorkProductSchema.partial(); +export const updateIssueWorkProductSchema = objectWithoutDefaults( + createIssueWorkProductSchema, +).partial(); export type UpdateIssueWorkProduct = z.infer; diff --git a/packages/shared/src/validators/workspace-file-resource.ts b/packages/shared/src/validators/workspace-file-resource.ts index 9efe45b57b..8ee89c6356 100644 --- a/packages/shared/src/validators/workspace-file-resource.ts +++ b/packages/shared/src/validators/workspace-file-resource.ts @@ -14,11 +14,11 @@ export const workspaceFileResourceKindSchema = z.enum(["file", "directory", "rem export const workspaceFileRefSchema = z.object({ kind: z.literal("workspace_file"), - issueId: z.string().uuid().optional(), - projectId: z.string().uuid().optional(), + issueId: z.string().guid().optional(), + projectId: z.string().guid().optional(), projectName: z.string().min(1).optional(), workspaceKind: workspaceFileWorkspaceKindSchema, - workspaceId: z.string().uuid(), + workspaceId: z.string().guid(), relativePath: z.string().min(1), line: z.number().int().positive().nullable().optional(), column: z.number().int().positive().nullable().optional(), @@ -26,8 +26,8 @@ export const workspaceFileRefSchema = z.object({ }); export const workspaceFileResourceQuerySchema = z.object({ - projectId: z.string().uuid().optional(), - workspaceId: z.string().uuid().optional(), + projectId: z.string().guid().optional(), + workspaceId: z.string().guid().optional(), path: z .string() .min(1) @@ -47,8 +47,8 @@ export const workspaceFileAvailabilityRequestSchema = z.object({ }); export const workspaceFileListQuerySchema = z.object({ - projectId: z.string().uuid().optional(), - workspaceId: z.string().uuid().optional(), + projectId: z.string().guid().optional(), + workspaceId: z.string().guid().optional(), workspace: workspaceFileSelectorSchema.optional(), path: z .string() @@ -85,8 +85,8 @@ export const resolvedWorkspaceResourceSchema = z.object({ displayPath: z.string().min(1), workspaceLabel: z.string().min(1), workspaceKind: workspaceFileWorkspaceKindSchema, - workspaceId: z.string().uuid(), - projectId: z.string().uuid().nullable().optional(), + workspaceId: z.string().guid(), + projectId: z.string().guid().nullable().optional(), projectName: z.string().min(1).nullable().optional(), contentType: z.string().nullable().optional(), byteSize: z.number().int().nonnegative().nullable().optional(), @@ -100,8 +100,8 @@ export const resolvedWorkspaceResourceSchema = z.object({ }); export const normalizedWorkspaceFileAvailabilityQuerySchema = z.object({ - projectId: z.string().uuid().nullable(), - workspaceId: z.string().uuid().nullable(), + projectId: z.string().guid().nullable(), + workspaceId: z.string().guid().nullable(), path: z.string().min(1), workspace: workspaceFileSelectorSchema, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1440620d74..dd78ca4a5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,13 +390,13 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.30.0 - version: 1.30.0(zod@3.25.76) + version: 1.30.0(zod@4.4.3) googleapis: specifier: ^174.0.1 version: 174.0.1 zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/node': specifier: ^22.20.1 @@ -412,10 +412,10 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.30.0 - version: 1.30.0(zod@3.25.76) + version: 1.30.0(zod@4.4.3) zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/node': specifier: ^22.20.1 @@ -431,13 +431,13 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.30.0 - version: 1.30.0(zod@3.25.76) + version: 1.30.0(zod@4.4.3) '@paperclipai/shared': specifier: workspace:* version: link:../shared zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/node': specifier: ^22.20.1 @@ -703,8 +703,8 @@ importers: specifier: ^19.2.8 version: 19.2.8 zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/node': specifier: ^22.20.1 @@ -719,8 +719,8 @@ importers: packages/shared: dependencies: zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/node': specifier: ^22.20.1 @@ -871,8 +871,8 @@ importers: specifier: ^8.21.3 version: 8.21.3 zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@types/express': specifier: ^5.0.0 @@ -9546,7 +9546,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.18 - '@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0)': + '@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0)': dependencies: '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -9560,38 +9560,38 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@better-auth/drizzle-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7))': + '@better-auth/drizzle-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7))': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 optionalDependencies: drizzle-orm: 0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7) - '@better-auth/kysely-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(kysely@0.29.5)': + '@better-auth/kysely-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(kysely@0.29.5)': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 optionalDependencies: kysely: 0.29.5 - '@better-auth/memory-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': + '@better-auth/memory-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': + '@better-auth/mongo-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 - '@better-auth/prisma-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': + '@better-auth/prisma-adapter@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 - '@better-auth/telemetry@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -11110,6 +11110,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)': + dependencies: + '@hono/node-server': 2.1.0(hono@4.13.2) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.1.1 + express: 5.2.1 + express-rate-limit: 8.6.2(express@5.2.1) + hono: 4.13.2 + jose: 6.2.8 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true @@ -13697,13 +13719,13 @@ snapshots: better-auth@1.6.28(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7))(pg@8.18.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.12))): dependencies: - '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) - '@better-auth/drizzle-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7)) - '@better-auth/kysely-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(kysely@0.29.5) - '@better-auth/memory-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) - '@better-auth/prisma-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) - '@better-auth/telemetry': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@3.25.76))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) + '@better-auth/core': 1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0) + '@better-auth/drizzle-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.1)(kysely@0.29.5)(pg@8.18.0)(postgres@3.4.9)(sqlite3@5.1.7)) + '@better-auth/kysely-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(kysely@0.29.5) + '@better-auth/memory-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) + '@better-auth/prisma-adapter': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2) + '@better-auth/telemetry': 1.6.28(@better-auth/core@1.6.28(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@noble/ciphers': 2.3.0 @@ -17660,6 +17682,10 @@ snapshots: dependencies: zod: 3.25.76 + zod-to-json-schema@3.25.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + zod@3.25.76: {} zod@4.4.3: {} diff --git a/server/package.json b/server/package.json index 1fb357fcb3..a3fc20d0e8 100644 --- a/server/package.json +++ b/server/package.json @@ -80,7 +80,7 @@ "sharp": "^0.35.3", "ssh2": "^1.17.0", "ws": "^8.21.3", - "zod": "^3.24.2" + "zod": "^4.4.3" }, "devDependencies": { "@types/express": "^5.0.0", diff --git a/server/src/routes/activity.ts b/server/src/routes/activity.ts index 85e8b20690..47592cfe50 100644 --- a/server/src/routes/activity.ts +++ b/server/src/routes/activity.ts @@ -95,17 +95,17 @@ const createActivitySchema = z.object({ action: z.string().min(1), entityType: z.string().min(1), entityId: z.string().min(1), - agentId: z.string().uuid().optional().nullable(), - details: z.record(z.unknown()).optional().nullable(), + agentId: z.string().guid().optional().nullable(), + details: z.record(z.string(), z.unknown()).optional().nullable(), }); const agentActionAuditActorScopeSchema = z.enum(["agents", "all"]); const agentActionAuditQuerySchema = z.object({ actorScope: agentActionAuditActorScopeSchema.default("agents"), - agentId: z.string().uuid().optional(), + agentId: z.string().guid().optional(), responsibleUserId: z.string().min(1).optional(), - runId: z.string().uuid().optional(), + runId: z.string().guid().optional(), entityType: z.string().min(1).optional(), entityId: z.string().min(1).optional(), action: z.string().min(1).optional(), diff --git a/server/src/routes/cases.ts b/server/src/routes/cases.ts index 55bdcba072..248d74d70e 100644 --- a/server/src/routes/cases.ts +++ b/server/src/routes/cases.ts @@ -48,29 +48,29 @@ const caseKeySchema = z.string().trim().min(1).max(512); const documentKeySchema = z.string().trim().min(1).max(120).regex(/^[A-Za-z0-9_.:-]+$/); const createCaseSchema = z.object({ - projectId: z.string().uuid().nullable().optional(), + projectId: z.string().guid().nullable().optional(), caseType: caseTypeSchema, key: caseKeySchema.nullable().optional(), title: z.string().trim().min(1).max(500), summary: z.string().max(8_000).nullable().optional(), status: caseStatusSchema.optional(), fields: jsonObjectSchema.optional(), - parentCaseId: z.string().uuid().nullable().optional(), + parentCaseId: z.string().guid().nullable().optional(), }).strict(); const patchCaseSchema = z.object({ - projectId: z.string().uuid().nullable().optional(), + projectId: z.string().guid().nullable().optional(), title: z.string().trim().min(1).max(500).optional(), summary: z.string().max(8_000).nullable().optional(), status: caseStatusSchema.optional(), fields: jsonObjectSchema.optional(), - parentCaseId: z.string().uuid().nullable().optional(), - labels: z.array(z.string().uuid()).max(100).optional(), - labelIds: z.array(z.string().uuid()).max(100).optional(), + parentCaseId: z.string().guid().nullable().optional(), + labels: z.array(z.string().guid()).max(100).optional(), + labelIds: z.array(z.string().guid()).max(100).optional(), }).strict(); const createIssueLinkSchema = z.object({ - issueId: z.string().uuid(), + issueId: z.string().guid(), role: z.enum(CASE_LINK_ROLES), }).strict(); @@ -79,7 +79,7 @@ const upsertCaseDocumentSchema = z.object({ format: z.string().trim().min(1).max(80).optional().default("markdown"), body: z.string().max(200_000), changeSummary: z.string().trim().max(1_000).nullable().optional(), - baseRevisionId: z.string().uuid().nullable().optional(), + baseRevisionId: z.string().guid().nullable().optional(), }).strict(); const queryListParamSchema = z.union([z.string(), z.array(z.string())]).optional(); @@ -89,13 +89,13 @@ const listCasesQuerySchema = z.object({ types: queryListParamSchema, status: z.string().trim().min(1).max(120).optional(), statuses: queryListParamSchema, - project: z.string().uuid().optional(), - projectId: z.string().uuid().optional(), + project: z.string().guid().optional(), + projectId: z.string().guid().optional(), projectIds: queryListParamSchema, includeNoProject: z.enum(["true", "false", "1", "0"]).optional(), - label: z.string().uuid().optional(), - labelId: z.string().uuid().optional(), - parent: z.string().uuid().optional(), + label: z.string().guid().optional(), + labelId: z.string().guid().optional(), + parent: z.string().guid().optional(), q: z.string().trim().min(1).max(200).optional(), includeAncestors: z.enum(["true", "false", "1", "0"]).optional(), limit: z.coerce.number().int().min(1).max(200).optional().default(100), diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index 87ff144ad8..7497da3617 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -316,9 +316,9 @@ export function companyRoutes(db: Db, storage?: StorageService, options?: Compan from: z.string().optional(), to: z.string().optional(), userId: z.string().min(1).optional(), - goalId: z.string().uuid().optional(), - projectId: z.string().uuid().optional(), - issueId: z.string().uuid().optional(), + goalId: z.string().guid().optional(), + projectId: z.string().guid().optional(), + issueId: z.string().guid().optional(), limit: z.string().optional(), offset: z.string().optional(), }).passthrough(); diff --git a/server/src/routes/decision-training.ts b/server/src/routes/decision-training.ts index 6e91477ef2..3191ccb936 100644 --- a/server/src/routes/decision-training.ts +++ b/server/src/routes/decision-training.ts @@ -6,18 +6,18 @@ import { decisionTrainingService, logActivity } from "../services/index.js"; import { assertBoard, assertCompanyAccess, getActorInfo, hasCompanyAccess } from "./authz.js"; const sourceKindSchema = z.enum(["interaction", "approval", "execution_decision"]); -const exampleIdSchema = z.string().uuid(); +const exampleIdSchema = z.string().guid(); const createSchema = z.object({ sourceKind: sourceKindSchema, - sourceId: z.string().uuid(), - issueId: z.string().uuid(), + sourceId: z.string().guid(), + issueId: z.string().guid(), notes: z.string().max(100_000).default(""), }).strict(); const updateSchema = z.object({ notes: z.string().max(100_000) }).strict(); const previewSchema = z.object({ sourceKind: sourceKindSchema, - sourceId: z.string().uuid(), - issueId: z.string().uuid(), + sourceId: z.string().guid(), + issueId: z.string().guid(), }).strict(); function requireHumanUser(req: Request, res: Response) { @@ -116,7 +116,7 @@ export function decisionTrainingRoutes(db: Db) { assertBoard(req); assertCompanyAccess(req, companyId); const parsed = z.object({ - project: z.string().uuid().optional(), + project: z.string().guid().optional(), kind: sourceKindSchema.optional(), author: z.string().optional(), q: z.string().trim().max(500).optional(), diff --git a/server/src/routes/decisions.ts b/server/src/routes/decisions.ts index 4a2fee4c5d..e18134f36e 100644 --- a/server/src/routes/decisions.ts +++ b/server/src/routes/decisions.ts @@ -28,14 +28,14 @@ const createSchema = z.object({ expiresAt: z.coerce.date().optional(), idempotencyKey: z.string().trim().min(1).max(500).nullable().optional(), continuationPolicy: z.enum(["none", "wake_origin_agent"]).optional(), - metadata: z.record(z.unknown()).optional(), + metadata: z.record(z.string(), z.unknown()).optional(), }).strict(); const bundleSchema = z.object({ title: z.string().trim().min(1).max(500), summary: z.string().max(100_000), decisions: z.array(createSchema).min(1).max(50) }).strict(); -const decideSchema = z.object({ optionId: z.string().trim().min(1).max(120), inputValues: z.record(z.string().max(20_000)).optional(), idempotencyKey: z.string().trim().min(1).max(500).nullable().optional() }).strict(); +const decideSchema = z.object({ optionId: z.string().trim().min(1).max(120), inputValues: z.record(z.string(), z.string().max(20_000)).optional(), idempotencyKey: z.string().trim().min(1).max(500).nullable().optional() }).strict(); const dismissSchema = z.object({ reason: z.string().max(20_000).nullable().optional() }).strict(); const statsQuerySchema = z.object({ groupBy: z.literal("ruleKey"), - originAgentId: z.string().uuid().optional(), + originAgentId: z.string().guid().optional(), since: z.coerce.date().optional(), }).strict(); @@ -147,7 +147,7 @@ export function decisionRoutes(db: Db, options: DecisionServiceOptions) { }); router.get("/companies/:companyId/decisions", async (req, res) => { const companyId = req.params.companyId as string; assertBoard(req); assertCompanyAccess(req, companyId); - const query = z.object({ status: z.enum(["open", "decided", "expired", "cancelled"]).optional(), bundleId: z.string().uuid().optional(), targetIssueId: z.string().uuid().optional(), originAgentId: z.string().uuid().optional(), limit: z.coerce.number().int().positive().max(100).optional() }).safeParse(req.query); + const query = z.object({ status: z.enum(["open", "decided", "expired", "cancelled"]).optional(), bundleId: z.string().guid().optional(), targetIssueId: z.string().guid().optional(), originAgentId: z.string().guid().optional(), limit: z.coerce.number().int().positive().max(100).optional() }).safeParse(req.query); if (!query.success) { res.status(400).json({ error: "Invalid decision filters", details: query.error.flatten() }); return; } res.json(await svc.list(companyId, query.data)); }); diff --git a/server/src/routes/file-resources.ts b/server/src/routes/file-resources.ts index 7adcf10340..4f852756af 100644 --- a/server/src/routes/file-resources.ts +++ b/server/src/routes/file-resources.ts @@ -171,7 +171,7 @@ function readQuery(query: unknown) { parsed = workspaceFileResourceQuerySchema.parse(query); } catch (error) { if (error instanceof ZodError) { - const refinement = error.errors.find((issue) => { + const refinement = error.issues.find((issue) => { const code = (issue as { params?: { code?: string } }).params?.code; return code === "invalid_path" || code === "invalid_target"; }); @@ -194,7 +194,7 @@ function readListQuery(query: unknown) { parsed = workspaceFileListQuerySchema.parse(query); } catch (error) { if (error instanceof ZodError) { - const refinement = error.errors.find((issue) => { + const refinement = error.issues.find((issue) => { const code = (issue as { params?: { code?: string } }).params?.code; return code === "invalid_query" || code === "invalid_target" || code === "invalid_path"; }); diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index 2413acaa9e..7872a17749 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -265,18 +265,18 @@ function prefersMinimalIssueUpdateResponse(req: Request) { } const refreshExternalObjectsSchema = z.object({ - objectIds: z.array(z.string().uuid()).max(50).optional(), + objectIds: z.array(z.string().guid()).max(50).optional(), }).strict(); const inboxArchiveBodySchema = z.object({ userId: z.string().trim().min(1).optional(), }).strict().default({}); const externalObjectSummariesSchema = z.object({ - issueIds: z.array(z.string().uuid()).max(1000), + issueIds: z.array(z.string().guid()).max(1000), }).strict(); const promoteLowTrustOutputSchema = z.object({ sourceArtifactKind: z.enum(["comment", "document", "work_product", "issue"]), - sourceArtifactId: z.string().uuid(), + sourceArtifactId: z.string().guid(), title: z.string().trim().min(1).max(200), summary: z.string().trim().min(1).max(8_000), }); @@ -460,7 +460,7 @@ function requiresPaperclipAttachmentMetadata(input: { } const attachmentArtifactMetadataInputSchema = z.object({ - attachmentId: z.string().uuid(), + attachmentId: z.string().guid(), }).passthrough(); function buildCreateIssueActivityStatusDetails( diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index d70249047b..49c9699544 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -255,148 +255,183 @@ type OpenApiPathRegistration = { [key: string]: unknown; }; -const zodTypeName = (schema: z.ZodTypeAny) => schema._def.typeName as string; +// Zod 4 stores each schema definition on `_def` with a lowercase `type` +// discriminator and moves the wrapped members onto that def. This loose view +// lets the converter read those members, because Zod 4 does not export a +// public type for every internal def shape. +type ZodDefAny = Record & { type: string }; + +const zodDef = (schema: z.ZodTypeAny): ZodDefAny => schema._def as unknown as ZodDefAny; +const zodTypeName = (schema: z.ZodTypeAny): string => zodDef(schema).type; function unwrapSchema(schema: z.ZodTypeAny): z.ZodTypeAny { - const typeName = zodTypeName(schema); - if (typeName === "ZodOptional" || typeName === "ZodDefault" || typeName === "ZodCatch") { - return unwrapSchema(schema._def.innerType); + const def = zodDef(schema); + if (def.type === "optional" || def.type === "default" || def.type === "catch") { + return unwrapSchema(def.innerType as z.ZodTypeAny); } - if (typeName === "ZodEffects") { - return unwrapSchema(schema._def.schema); + // A `.transform()` or `.pipe()` becomes a pipe. Read the input schema so the + // published contract describes the value a client sends. + if (def.type === "pipe") { + return unwrapSchema(def.in as z.ZodTypeAny); } return schema; } function isOptionalSchema(schema: z.ZodTypeAny): boolean { - const typeName = zodTypeName(schema); - if (typeName === "ZodOptional" || typeName === "ZodDefault" || typeName === "ZodCatch") { + const def = zodDef(schema); + if (def.type === "optional" || def.type === "default" || def.type === "catch") { return true; } - if (typeName === "ZodEffects") { - return isOptionalSchema(schema._def.schema); + if (def.type === "pipe") { + return isOptionalSchema(def.in as z.ZodTypeAny); } - if (typeName === "ZodNullable") { - return isOptionalSchema(schema._def.innerType); + if (def.type === "nullable") { + return isOptionalSchema(def.innerType as z.ZodTypeAny); } return false; } -function applyStringChecks(jsonSchema: JsonSchema, checks: Array>) { +// Zod 4 stores each check as an object with a `_zod.def` that carries a `check` +// name and the check members. Read that def to describe the constraint. +function checkDef(check: unknown): Record | undefined { + return (check as { _zod?: { def?: Record } })._zod?.def; +} + +function applyStringChecks(jsonSchema: JsonSchema, checks: ReadonlyArray) { for (const check of checks) { - if (check.kind === "min") jsonSchema.minLength = check.value; - if (check.kind === "max") jsonSchema.maxLength = check.value; - if (check.kind === "email") jsonSchema.format = "email"; - if (check.kind === "url") jsonSchema.format = "uri"; - if (check.kind === "uuid") jsonSchema.format = "uuid"; - if (check.kind === "datetime") jsonSchema.format = "date-time"; - if (check.kind === "regex" && check.regex instanceof RegExp) { - jsonSchema.pattern = check.regex.source; + const def = checkDef(check); + if (!def) continue; + if (def.check === "min_length") jsonSchema.minLength = def.minimum; + else if (def.check === "max_length") jsonSchema.maxLength = def.maximum; + else if (def.check === "string_format") { + if (def.format === "email") jsonSchema.format = "email"; + else if (def.format === "url") jsonSchema.format = "uri"; + // Zod 3 `.uuid()` maps to `.guid()` in zod 4 to keep the loose UUID + // format. Publish both as the OpenAPI `uuid` format so the spec does not + // change. + else if (def.format === "uuid" || def.format === "guid") jsonSchema.format = "uuid"; + else if (def.format === "datetime") jsonSchema.format = "date-time"; + // Zod 4 stores a `.regex()` pattern as a `RegExp`; publish its source. + else if (def.format === "regex") { + if (def.pattern instanceof RegExp) jsonSchema.pattern = def.pattern.source; + else if (typeof def.pattern === "string") jsonSchema.pattern = def.pattern; + } } } } -function applyNumberChecks(jsonSchema: JsonSchema, checks: Array>) { +function applyNumberChecks(jsonSchema: JsonSchema, checks: ReadonlyArray) { for (const check of checks) { - if (check.kind === "int") jsonSchema.type = "integer"; - if (check.kind === "min") { - jsonSchema.minimum = check.value; - if (!check.inclusive) jsonSchema.exclusiveMinimum = true; - } - if (check.kind === "max") { - jsonSchema.maximum = check.value; - if (!check.inclusive) jsonSchema.exclusiveMaximum = true; + const def = checkDef(check); + if (!def) continue; + // `.int()` records a number-format check such as `safeint`. + if (def.check === "number_format") { + if (typeof def.format === "string" && def.format.includes("int")) { + jsonSchema.type = "integer"; + } + } else if (def.check === "greater_than") { + jsonSchema.minimum = def.value; + if (!def.inclusive) jsonSchema.exclusiveMinimum = true; + } else if (def.check === "less_than") { + jsonSchema.maximum = def.value; + if (!def.inclusive) jsonSchema.exclusiveMaximum = true; } } } function zodToOpenApiSchema(schema: z.ZodTypeAny): JsonSchema { const unwrapped = unwrapSchema(schema); - const typeName = zodTypeName(unwrapped); + const def = zodDef(unwrapped); + const typeName = def.type; - if (typeName === "ZodString") { + if (typeName === "string") { const jsonSchema: JsonSchema = { type: "string" }; - applyStringChecks(jsonSchema, unwrapped._def.checks ?? []); + applyStringChecks(jsonSchema, (def.checks as unknown[]) ?? []); return jsonSchema; } - if (typeName === "ZodNumber") { + if (typeName === "number") { const jsonSchema: JsonSchema = { type: "number" }; - applyNumberChecks(jsonSchema, unwrapped._def.checks ?? []); + applyNumberChecks(jsonSchema, (def.checks as unknown[]) ?? []); return jsonSchema; } - if (typeName === "ZodBoolean") return { type: "boolean" }; - if (typeName === "ZodDate") return { type: "string", format: "date-time" }; - if (typeName === "ZodAny" || typeName === "ZodUnknown") return {}; + if (typeName === "boolean") return { type: "boolean" }; + if (typeName === "date") return { type: "string", format: "date-time" }; + if (typeName === "any" || typeName === "unknown") return {}; - if (typeName === "ZodLiteral") { - const value = unwrapped._def.value; - return { type: typeof value, enum: [value] }; + if (typeName === "literal") { + const values = def.values as unknown[]; + return { type: typeof values[0], enum: values }; } - if (typeName === "ZodEnum") { - return { type: "string", enum: unwrapped._def.values }; - } - - if (typeName === "ZodNativeEnum") { - const values = Object.values(unwrapped._def.values).filter( - (value) => typeof value === "string" || typeof value === "number", + // Zod 4 merges string enums and native enums into one `enum` type and stores + // the members on `entries`. A pure string enum keeps `type: "string"`; a + // native enum can hold numbers, so it publishes the values without a type. + if (typeName === "enum") { + const values = Array.from( + new Set( + Object.values(def.entries as Record).filter( + (value) => typeof value === "string" || typeof value === "number", + ), + ), ); - return { enum: Array.from(new Set(values)) }; + if (values.every((value) => typeof value === "string")) { + return { type: "string", enum: values }; + } + return { enum: values }; } - if (typeName === "ZodArray") { - return { type: "array", items: zodToOpenApiSchema(unwrapped._def.type) }; + if (typeName === "array") { + return { type: "array", items: zodToOpenApiSchema(def.element as z.ZodTypeAny) }; } - if (typeName === "ZodRecord") { + if (typeName === "record") { return { type: "object", - additionalProperties: zodToOpenApiSchema(unwrapped._def.valueType), + additionalProperties: zodToOpenApiSchema(def.valueType as z.ZodTypeAny), }; } - if (typeName === "ZodNullable") { - return { ...zodToOpenApiSchema(unwrapped._def.innerType), nullable: true }; + if (typeName === "nullable") { + return { ...zodToOpenApiSchema(def.innerType as z.ZodTypeAny), nullable: true }; } - if (typeName === "ZodUnion") { - return { oneOf: unwrapped._def.options.map((option: z.ZodTypeAny) => zodToOpenApiSchema(option)) }; - } - - if (typeName === "ZodDiscriminatedUnion") { + // Zod 4 represents a plain union and a discriminated union as one `union` + // type with the members on `options`. + if (typeName === "union") { return { - oneOf: Array.from(unwrapped._def.options.values()).map((option) => - zodToOpenApiSchema(option as z.ZodTypeAny), - ), + oneOf: (def.options as z.ZodTypeAny[]).map((option) => zodToOpenApiSchema(option)), }; } - if (typeName === "ZodIntersection") { + if (typeName === "intersection") { return { allOf: [ - zodToOpenApiSchema(unwrapped._def.left), - zodToOpenApiSchema(unwrapped._def.right), + zodToOpenApiSchema(def.left as z.ZodTypeAny), + zodToOpenApiSchema(def.right as z.ZodTypeAny), ], }; } - if (typeName === "ZodObject") { - const shape = unwrapped._def.shape(); + if (typeName === "object") { + const shape = def.shape as Record; const properties: Record = {}; const required: string[] = []; for (const [key, value] of Object.entries(shape)) { - const propertySchema = value as z.ZodTypeAny; - properties[key] = zodToOpenApiSchema(propertySchema); - if (!isOptionalSchema(propertySchema)) required.push(key); + properties[key] = zodToOpenApiSchema(value); + if (!isOptionalSchema(value)) required.push(key); } const jsonSchema: JsonSchema = { type: "object", properties }; if (required.length > 0) jsonSchema.required = required; - // A `.strict()` Zod object forbids an unknown key. Publish that constraint - // as `additionalProperties: false`, so a client, a gateway, or a handler - // that treats the contract as authoritative rejects an extra property too. - if (unwrapped._def.unknownKeys === "strict") jsonSchema.additionalProperties = false; + // A `.strict()` Zod object forbids an unknown key. Zod 4 records that as a + // `never` catchall. Publish the constraint as `additionalProperties: false`, + // so a client, a gateway, or a handler that treats the contract as + // authoritative rejects an extra property too. + const catchall = def.catchall as z.ZodTypeAny | undefined; + if (catchall && zodDef(catchall).type === "never") { + jsonSchema.additionalProperties = false; + } return jsonSchema; } @@ -445,13 +480,13 @@ function normalizeResponses(responses: Record = {}) { function parametersFromSchema(schema: z.ZodTypeAny, location: "path" | "query") { const objectSchema = unwrapSchema(schema); - if (zodTypeName(objectSchema) !== "ZodObject") return []; - const shape = objectSchema._def.shape(); + if (zodTypeName(objectSchema) !== "object") return []; + const shape = zodDef(objectSchema).shape as Record; return Object.entries(shape).map(([name, value]) => ({ name, in: location, - required: location === "path" ? true : !isOptionalSchema(value as z.ZodTypeAny), - schema: zodToOpenApiSchema(value as z.ZodTypeAny), + required: location === "path" ? true : !isOptionalSchema(value), + schema: zodToOpenApiSchema(value), })); } @@ -511,7 +546,7 @@ const ErrorSchema = registry.register( ); const responses = { - ok: (schema: z.ZodTypeAny = z.record(z.unknown())) => ({ + ok: (schema: z.ZodTypeAny = z.record(z.string(), z.unknown())) => ({ description: "Success", content: { "application/json": { schema } }, }), @@ -600,11 +635,11 @@ const importRequestBody = (schema: z.ZodTypeAny) => ({ const r = responses; const externalObjectSummariesBodySchema = z.object({ - issueIds: z.array(z.string().uuid()).max(1000), + issueIds: z.array(z.string().guid()).max(1000), }).strict(); const refreshExternalObjectsBodySchema = z.object({ - objectIds: z.array(z.string().uuid()).max(50).optional(), + objectIds: z.array(z.string().guid()).max(50).optional(), }).strict(); // The start route reads the body directly, so document the accepted fields @@ -653,9 +688,9 @@ const workTimelineQuerySchema = z.object({ from: z.string().optional(), to: z.string().optional(), userId: z.string().optional(), - goalId: z.string().uuid().optional(), - projectId: z.string().uuid().optional(), - issueId: z.string().uuid().optional(), + goalId: z.string().guid().optional(), + projectId: z.string().guid().optional(), + issueId: z.string().guid().optional(), limit: z.string().optional(), offset: z.string().optional(), }).strict(); @@ -709,7 +744,7 @@ const workTimelineResponseSchema = z.object({ function paramsSchemaFromPath(routePath: string): z.ZodObject | undefined { const names = [...routePath.matchAll(/\{([A-Za-z0-9_]+)\}/g)].map((match) => match[1]); if (names.length === 0) return undefined; - const shape: z.ZodRawShape = {}; + const shape: Record = {}; for (const name of names) { shape[name] = z.string(); } @@ -1713,7 +1748,7 @@ registry.registerPath({ const AgentSecretListResponseSchema = z.object({ secrets: z.array(z.object({ - secretRef: z.string().uuid(), + secretRef: z.string().guid(), key: z.string(), name: z.string(), description: z.string().nullable(), @@ -1735,10 +1770,10 @@ const createAgentSecretProposalSchema = z.discriminatedUnion("kind", [ }), z.object({ kind: z.literal("binding"), - secretId: z.string().uuid().optional(), + secretId: z.string().guid().optional(), sourceConfigPath: z.string().min(1).optional(), - secretProposalId: z.string().uuid().optional(), - targetAgentId: z.string().uuid().optional(), + secretProposalId: z.string().guid().optional(), + targetAgentId: z.string().guid().optional(), configPath: z.string().min(1), justification: z.string().min(1), }), @@ -1760,7 +1795,7 @@ const approveSecretProposalSchema = z.object({ overrides: z.object({ name: z.string().min(1).optional(), description: z.string().optional().nullable(), - providerConfigId: z.string().uuid().optional().nullable(), + providerConfigId: z.string().guid().optional().nullable(), }).optional(), }); @@ -1788,7 +1823,7 @@ registry.registerPath({ path: "/api/agents/me/secret-proposals/{id}", tags: ["secrets"], summary: "Withdraw a pending secret proposal", - request: { params: z.object({ id: z.string().uuid() }) }, + request: { params: z.object({ id: z.string().guid() }) }, responses: { 200: r.ok(), 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict }, }); @@ -3026,7 +3061,7 @@ registry.registerPath({ tags: ["secrets"], summary: "List company secret proposals for board review", request: { - params: z.object({ companyId: z.string().uuid() }), + params: z.object({ companyId: z.string().guid() }), query: z.object({ status: z.enum(["pending", "approved", "rejected", "withdrawn", "expired"]).optional() }), }, responses: { 200: r.ok(), 401: r.unauthorized, 403: r.forbidden }, @@ -3038,7 +3073,7 @@ registry.registerPath({ tags: ["secrets"], summary: "Approve and execute a secret proposal as the approving board user", request: { - params: z.object({ companyId: z.string().uuid(), id: z.string().uuid() }), + params: z.object({ companyId: z.string().guid(), id: z.string().guid() }), body: jsonBody(approveSecretProposalSchema), }, responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict, 422: r.unprocessable }, @@ -3050,7 +3085,7 @@ registry.registerPath({ tags: ["secrets"], summary: "Reject a pending secret proposal and dependent bindings", request: { - params: z.object({ companyId: z.string().uuid(), id: z.string().uuid() }), + params: z.object({ companyId: z.string().guid(), id: z.string().guid() }), body: jsonBody(rejectSecretProposalSchema), }, responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict, 422: r.unprocessable }, @@ -3423,9 +3458,9 @@ registry.registerPath({ request: { params: z.object({ companyId: z.string() }), query: z.object({ - agentId: z.string().uuid().optional(), + agentId: z.string().guid().optional(), responsibleUserId: z.string().min(1).optional(), - runId: z.string().uuid().optional(), + runId: z.string().guid().optional(), entityType: z.string().min(1).optional(), entityId: z.string().min(1).optional(), action: z.string().min(1).optional(), @@ -3447,9 +3482,9 @@ registry.registerPath({ request: { params: z.object({ companyId: z.string() }), query: z.object({ - agentId: z.string().uuid().optional(), + agentId: z.string().guid().optional(), responsibleUserId: z.string().min(1).optional(), - runId: z.string().uuid().optional(), + runId: z.string().guid().optional(), entityType: z.string().min(1).optional(), entityId: z.string().min(1).optional(), action: z.string().min(1).optional(), @@ -3484,8 +3519,8 @@ registry.registerPath({ action: z.string().min(1), entityType: z.string().min(1), entityId: z.string().min(1), - agentId: z.string().uuid().optional().nullable(), - details: z.record(z.unknown()).optional().nullable(), + agentId: z.string().guid().optional().nullable(), + details: z.record(z.string(), z.unknown()).optional().nullable(), })), }, responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized }, @@ -3850,9 +3885,9 @@ registerCurrentRoute({ summary: "List decisions", query: z.object({ status: z.enum(["open", "decided", "expired", "cancelled"]).optional(), - bundleId: z.string().uuid().optional(), - targetIssueId: z.string().uuid().optional(), - originAgentId: z.string().uuid().optional(), + bundleId: z.string().guid().optional(), + targetIssueId: z.string().guid().optional(), + originAgentId: z.string().guid().optional(), limit: z.coerce.number().int().positive().max(100).optional(), }), responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden }, @@ -3865,7 +3900,7 @@ registerCurrentRoute({ summary: "Get decision telemetry grouped by rule key", query: z.object({ groupBy: z.literal("ruleKey"), - originAgentId: z.string().uuid().optional(), + originAgentId: z.string().guid().optional(), since: z.string().datetime().optional(), }), responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden }, @@ -3920,8 +3955,8 @@ registerCurrentRoute({ summary: "Capture a decision training example", body: z.object({ sourceKind: decisionTrainingSourceKindSchema, - sourceId: z.string().uuid(), - issueId: z.string().uuid(), + sourceId: z.string().guid(), + issueId: z.string().guid(), notes: z.string().max(100_000).default(""), }).strict(), responses: { 201: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict }, @@ -3934,8 +3969,8 @@ registerCurrentRoute({ summary: "Preview a decision training snapshot", body: z.object({ sourceKind: decisionTrainingSourceKindSchema, - sourceId: z.string().uuid(), - issueId: z.string().uuid(), + sourceId: z.string().guid(), + issueId: z.string().guid(), }).strict(), responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict }, }); @@ -3946,7 +3981,7 @@ registerCurrentRoute({ tags: ["decision-training"], summary: "List decision training examples", query: z.object({ - project: z.string().uuid().optional(), + project: z.string().guid().optional(), kind: decisionTrainingSourceKindSchema.optional(), author: z.string().optional(), q: z.string().max(500).optional(), @@ -5813,7 +5848,7 @@ registry.registerPath({ request: { body: jsonBody(z.object({ tool: z.string(), - parameters: z.record(z.unknown()).optional(), + parameters: z.record(z.string(), z.unknown()).optional(), runContext: z.object({ agentId: z.string(), runId: z.string(), @@ -5922,7 +5957,7 @@ registry.registerPath({ summary: "Set company-scoped plugin config", request: { params: z.object({ pluginId: z.string() }), - body: jsonBody(z.object({ companyId: z.string(), configJson: z.record(z.unknown()) })), + body: jsonBody(z.object({ companyId: z.string(), configJson: z.record(z.string(), z.unknown()) })), }, responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized }, }); @@ -5934,7 +5969,7 @@ registry.registerPath({ summary: "Test company-scoped plugin config", request: { params: z.object({ pluginId: z.string() }), - body: jsonBody(z.object({ companyId: z.string(), configJson: z.record(z.unknown()) })), + body: jsonBody(z.object({ companyId: z.string(), configJson: z.record(z.string(), z.unknown()) })), }, responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized }, }); @@ -5996,7 +6031,7 @@ registry.registerPath({ body: jsonBody(z.object({ key: z.string(), companyId: z.string().optional(), - params: z.record(z.unknown()).optional(), + params: z.record(z.string(), z.unknown()).optional(), })), }, responses: { 200: r.ok(), 401: r.unauthorized }, @@ -6012,7 +6047,7 @@ registry.registerPath({ body: jsonBody(z.object({ key: z.string(), companyId: z.string().optional(), - params: z.record(z.unknown()).optional(), + params: z.record(z.string(), z.unknown()).optional(), })), }, responses: { 200: r.ok(), 401: r.unauthorized }, @@ -6027,7 +6062,7 @@ registry.registerPath({ params: z.object({ pluginId: z.string(), key: z.string() }), body: jsonBody(z.object({ companyId: z.string().optional(), - params: z.record(z.unknown()).optional(), + params: z.record(z.string(), z.unknown()).optional(), })), }, responses: { 200: r.ok(), 401: r.unauthorized }, @@ -6042,7 +6077,7 @@ registry.registerPath({ params: z.object({ pluginId: z.string(), key: z.string() }), body: jsonBody(z.object({ companyId: z.string().optional(), - params: z.record(z.unknown()).optional(), + params: z.record(z.string(), z.unknown()).optional(), })), }, responses: { 200: r.ok(), 401: r.unauthorized }, @@ -6810,7 +6845,7 @@ for (const route of [ path: route[1], tags: ["skills"], summary: route[2], - ...(route[0] === "post" ? { body: z.record(z.unknown()).optional() } : {}), + ...(route[0] === "post" ? { body: z.record(z.string(), z.unknown()).optional() } : {}), }); } @@ -6884,7 +6919,7 @@ registerCurrentRoute({ summary: "Promote quarantined low-trust output", body: z.object({ sourceArtifactKind: z.enum(["comment", "document", "work_product", "issue"]), - sourceArtifactId: z.string().uuid(), + sourceArtifactId: z.string().guid(), title: z.string().trim().min(1).max(200), summary: z.string().trim().min(1).max(8_000), }), @@ -7600,7 +7635,7 @@ const toolGatewaySessionSchema = z.object({ const toolGatewayCallSchema = z.object({ tool: z.string(), - parameters: z.record(z.unknown()).optional(), + parameters: z.record(z.string(), z.unknown()).optional(), timeoutMs: z.number().int().positive().optional(), approvedActionRequestId: z.string().optional(), idempotencyKey: z.string().optional(), @@ -7613,7 +7648,7 @@ const toolGatewayCompanyBodySchema = z.object({ companyId: z.string(), }).passthrough(); -const mcpGatewayProtocolSchema = z.record(z.unknown()); +const mcpGatewayProtocolSchema = z.record(z.string(), z.unknown()); registerCurrentRoute({ method: "get", diff --git a/server/src/routes/pipelines.ts b/server/src/routes/pipelines.ts index 587a6f7047..e9e6094086 100644 --- a/server/src/routes/pipelines.ts +++ b/server/src/routes/pipelines.ts @@ -94,9 +94,9 @@ const casePatchSchema = z.object({ summary: z.string().max(8_000).nullable().optional(), fields: jsonObjectSchema.optional(), workspaceRef: jsonObjectSchema.nullable().optional(), - parentCaseId: z.string().uuid().nullable().optional(), + parentCaseId: z.string().guid().nullable().optional(), expectedVersion: z.number().int().positive().optional(), - leaseToken: z.string().uuid().nullable().optional(), + leaseToken: z.string().guid().nullable().optional(), }); const ingestCaseSchema = z.object({ caseKey: z.string().max(1_024).nullable().optional(), @@ -104,17 +104,17 @@ const ingestCaseSchema = z.object({ summary: z.string().max(8_000).nullable().optional(), fields: jsonObjectSchema.optional(), stageKey: z.string().trim().min(1).max(120).optional(), - parentCaseId: z.string().uuid().nullable().optional(), + parentCaseId: z.string().guid().nullable().optional(), requestKey: z.string().trim().min(1).max(512).optional(), workspaceRef: jsonObjectSchema.nullable().optional(), - blockedByCaseIds: z.array(z.string().uuid()).max(100).optional(), + blockedByCaseIds: z.array(z.string().guid()).max(100).optional(), blockedByCaseKeys: z.array(z.string().max(1_024)).max(100).optional(), }); const createPipelineSchema = z.object({ key: z.string().trim().min(1).max(120), name: z.string().trim().min(1).max(200), description: z.string().max(8_000).nullable().optional(), - projectId: z.string().uuid().nullable().optional(), + projectId: z.string().guid().nullable().optional(), enforceTransitions: z.boolean().optional(), stages: z.array(z.object({ key: z.string().trim().min(1).max(120), @@ -146,7 +146,7 @@ const updateStageSchema = z.object({ }); const updateStageAutomationEnvSchema = z.object({ env: envConfigSchema.nullable(), - baseRoutineRevisionId: z.string().uuid().nullable().optional(), + baseRoutineRevisionId: z.string().guid().nullable().optional(), }); const replaceTransitionsSchema = z.object({ transitions: z.array(z.object({ @@ -167,16 +167,16 @@ const breakdownCaseSchema = z.object({ }); const claimCaseSchema = z.object({ leaseSeconds: z.number().int().positive().max(86_400).optional() }); const releaseCaseSchema = z.object({ - leaseToken: z.string().uuid().nullable().optional(), + leaseToken: z.string().guid().nullable().optional(), force: z.boolean().optional(), }); const transitionCaseSchema = z.object({ toStageKey: z.string().trim().min(1).max(120), expectedVersion: z.number().int().positive(), - leaseToken: z.string().uuid().nullable().optional(), + leaseToken: z.string().guid().nullable().optional(), reason: z.string().max(4_000).nullable().optional(), force: z.boolean().optional(), - acceptSuggestionId: z.string().uuid().optional(), + acceptSuggestionId: z.string().guid().optional(), }); const suggestTransitionSchema = z.object({ toStageKey: z.string().trim().min(1).max(120), @@ -184,52 +184,52 @@ const suggestTransitionSchema = z.object({ confidence: z.number().min(0).max(1).optional(), }); const resolveSuggestionSchema = z.object({ - suggestionId: z.string().uuid(), + suggestionId: z.string().guid(), resolution: z.enum(["accept", "dismiss"]), expectedVersion: z.number().int().positive().optional(), reason: z.string().max(4_000).nullable().optional(), - leaseToken: z.string().uuid().nullable().optional(), + leaseToken: z.string().guid().nullable().optional(), }); const acknowledgeDriftSchema = z.object({ expectedVersion: z.number().int().positive().optional(), }); const retryAutomationQuerySchema = z.object({ scope: pipelineAutomationRetryScopeSchema.default("previous_stage"), - targetStageId: z.string().uuid().optional(), + targetStageId: z.string().guid().optional(), }); const reviewEditsSchema = z.object({ title: z.string().trim().min(1).max(500).optional(), summary: z.string().max(8_000).nullable().optional(), fields: jsonObjectSchema.optional(), - parentCaseId: z.string().uuid().nullable().optional(), + parentCaseId: z.string().guid().nullable().optional(), }); const reviewCaseSchema = z.object({ decision: z.enum(["approve", "reject", "request_changes"]), reason: z.string().max(4_000).nullable().optional(), edits: reviewEditsSchema.optional(), expectedVersion: z.number().int().positive(), - leaseToken: z.string().uuid().nullable().optional(), + leaseToken: z.string().guid().nullable().optional(), }); -const blockersSchema = z.object({ blockedByCaseIds: z.array(z.string().uuid()).max(100) }); +const blockersSchema = z.object({ blockedByCaseIds: z.array(z.string().guid()).max(100) }); const issueLinkRoleSchema = z.enum(["origin", "conversation", "work", "automation"]); const createIssueLinkSchema = z.object({ - issueId: z.string().uuid(), + issueId: z.string().guid(), role: issueLinkRoleSchema, }); const bulkReviewSchema = z.object({ - items: z.array(reviewCaseSchema.extend({ caseId: z.string().uuid() })).max(100), + items: z.array(reviewCaseSchema.extend({ caseId: z.string().guid() })).max(100), }); const upsertPipelineDocumentSchema = z.object({ title: z.string().trim().min(1).max(200).optional(), body: z.string().max(200_000), - baseRevisionId: z.string().uuid().nullable().optional(), + baseRevisionId: z.string().guid().nullable().optional(), }); const upsertPipelineCaseDocumentSchema = z.object({ title: z.string().trim().min(1).max(200).optional(), format: z.string().trim().min(1).max(80).optional().default("markdown"), body: z.string().max(200_000), changeSummary: z.string().trim().max(1_000).nullable().optional(), - baseRevisionId: z.string().uuid().nullable().optional(), + baseRevisionId: z.string().guid().nullable().optional(), }); const intakeFieldTypes = new Set(["select", "text", "multiline"]); diff --git a/server/src/services/agent-action-audit.ts b/server/src/services/agent-action-audit.ts index f3ac6cb0fc..474a57f06d 100644 --- a/server/src/services/agent-action-audit.ts +++ b/server/src/services/agent-action-audit.ts @@ -26,7 +26,7 @@ type CursorValue = { createdAt: string; id: string }; const cursorValueSchema = z.object({ createdAt: z.string().datetime({ offset: true }), - id: z.string().uuid(), + id: z.string().guid(), }); function decodeCursor(cursor: string | undefined): CursorValue | null { diff --git a/server/src/services/environment-config.ts b/server/src/services/environment-config.ts index 7ebb31a506..887272ddc3 100644 --- a/server/src/services/environment-config.ts +++ b/server/src/services/environment-config.ts @@ -33,16 +33,16 @@ import { resolveActiveEnvironmentCustomImageTemplateForRuntime } from "./environ const secretRefSchema = z.object({ type: z.literal("secret_ref"), - secretId: z.string().uuid(), + secretId: z.string().guid(), version: z.union([z.literal("latest"), z.number().int().positive()]).optional().default("latest"), }).strict(); const sshEnvironmentConfigSchema = z.object({ - host: z.string({ required_error: "SSH environments require a host." }).trim().min(1, "SSH environments require a host."), + host: z.string({ error: "SSH environments require a host." }).trim().min(1, "SSH environments require a host."), port: z.coerce.number().int().min(1).max(65535).default(22), - username: z.string({ required_error: "SSH environments require a username." }).trim().min(1, "SSH environments require a username."), + username: z.string({ error: "SSH environments require a username." }).trim().min(1, "SSH environments require a username."), remoteWorkspacePath: z - .string({ required_error: "SSH environments require a remote workspace path." }) + .string({ error: "SSH environments require a remote workspace path." }) .trim() .min(1, "SSH environments require a remote workspace path.") .refine((value) => value.startsWith("/"), "SSH remote workspace path must be absolute."), @@ -102,7 +102,7 @@ const pluginEnvironmentConfigSchema = z.object({ /^[a-z0-9][a-z0-9._-]*$/, "Environment driver key must start with a lowercase alphanumeric and contain only lowercase letters, digits, dots, hyphens, or underscores", ), - driverConfig: z.record(z.unknown()).optional().default({}), + driverConfig: z.record(z.string(), z.unknown()).optional().default({}), }).strict(); export type ParsedEnvironmentConfig = diff --git a/server/src/services/plugin-manifest-validator.ts b/server/src/services/plugin-manifest-validator.ts index 9af6b61c3c..23d04ec727 100644 --- a/server/src/services/plugin-manifest-validator.ts +++ b/server/src/services/plugin-manifest-validator.ts @@ -128,8 +128,13 @@ export function pluginManifestValidator(): PluginManifestValidator { }; } - const details = result.error.errors.map((issue) => ({ - path: issue.path, + const details = result.error.issues.map((issue) => ({ + // Zod 4 types an issue path as `PropertyKey[]`, which can include a + // symbol. A manifest path only has string or number segments, so map a + // rare symbol segment to its string form for a stable message. + path: issue.path.map((segment) => + typeof segment === "symbol" ? segment.toString() : segment, + ), message: issue.message, }));