From 3abe9e213433cdd9a9d710c600dec8c059f6df25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:04:14 -0700 Subject: [PATCH] build(deps): bump zod from 3.25.76 to 4.4.3 (#11719) Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.4.3.
Release notes

Sourced from zod's releases.

v4.4.3

Commits:

v4.4.2

Commits:

v4.4.1

Commits:

v4.4.0

4.4.0

This is a minor release with a wide set of correctness and soundness fixes. Some fixes intentionally make Zod stricter, so code that depended on previously accepted invalid or ambiguous inputs may need small updates.

Potentially breaking bug fixes

... (truncated)

Commits
Maintainer changes

This version was pushed to npm by GitHub Actions, a new releaser for zod since your current version.


--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Priya Raman Co-authored-by: Paperclip --- .../google-sheets-mcp-server/package.json | 2 +- .../google-sheets-mcp-server/src/tools.ts | 4 +- packages/kv-demo-mcp-server/package.json | 2 +- packages/kv-demo-mcp-server/src/tools.ts | 4 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/tools.ts | 50 ++-- packages/plugins/sdk/package.json | 2 +- packages/shared/package.json | 2 +- packages/shared/src/config-schema.ts | 14 +- packages/shared/src/frontmatter.ts | 4 +- packages/shared/src/validators/access.ts | 10 +- .../src/validators/adapter-auth-session.ts | 6 +- .../shared/src/validators/adapter-registry.ts | 2 +- .../shared/src/validators/adapter-skills.ts | 6 +- packages/shared/src/validators/agent.ts | 28 +- packages/shared/src/validators/approval.ts | 4 +- packages/shared/src/validators/artifact.ts | 12 +- packages/shared/src/validators/budget.ts | 2 +- .../validators/claude-setup-token-session.ts | 8 +- .../src/validators/company-portability.ts | 4 +- .../shared/src/validators/company-skill.ts | 126 ++++---- packages/shared/src/validators/company.ts | 35 ++- packages/shared/src/validators/cost.ts | 10 +- packages/shared/src/validators/decision.ts | 18 +- .../src/validators/document-annotation.ts | 6 +- .../validators/environment-custom-images.ts | 22 +- .../src/validators/execution-workspace.ts | 26 +- .../shared/src/validators/external-object.ts | 2 +- packages/shared/src/validators/feedback.ts | 2 +- packages/shared/src/validators/finance.ts | 12 +- packages/shared/src/validators/folder.ts | 14 +- packages/shared/src/validators/goal.ts | 7 +- .../src/validators/inbox-agent-policy.ts | 2 +- packages/shared/src/validators/instance.ts | 41 ++- packages/shared/src/validators/issue.ts | 177 +++++------ packages/shared/src/validators/partial.ts | 41 +++ packages/shared/src/validators/pipeline.ts | 22 +- packages/shared/src/validators/plugin.ts | 8 +- packages/shared/src/validators/project.ts | 17 +- packages/shared/src/validators/routine.ts | 41 +-- packages/shared/src/validators/secret.ts | 28 +- .../src/validators/sidebar-preferences.ts | 2 +- .../shared/src/validators/skill-policy.ts | 8 +- packages/shared/src/validators/status-card.ts | 42 +-- .../shared/src/validators/summary-slot.ts | 6 +- packages/shared/src/validators/tool-access.ts | 191 ++++++------ .../shared/src/validators/trust-policy.ts | 24 +- .../shared/src/validators/work-product.ts | 15 +- .../src/validators/workspace-file-resource.ts | 22 +- pnpm-lock.yaml | 96 +++--- server/package.json | 2 +- server/src/routes/activity.ts | 8 +- server/src/routes/cases.ts | 26 +- server/src/routes/companies.ts | 6 +- server/src/routes/decision-training.ts | 12 +- server/src/routes/decisions.ts | 8 +- server/src/routes/file-resources.ts | 4 +- server/src/routes/issues.ts | 8 +- server/src/routes/openapi.ts | 277 ++++++++++-------- server/src/routes/pipelines.ts | 38 +-- server/src/services/agent-action-audit.ts | 2 +- server/src/services/environment-config.ts | 10 +- .../src/services/plugin-manifest-validator.ts | 9 +- 63 files changed, 889 insertions(+), 752 deletions(-) create mode 100644 packages/shared/src/validators/partial.ts 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, }));