diff --git a/packages/protocol/src/paseo-config-schema.test.ts b/packages/protocol/src/paseo-config-schema.test.ts index 49d5dd0169..a34f19178b 100644 --- a/packages/protocol/src/paseo-config-schema.test.ts +++ b/packages/protocol/src/paseo-config-schema.test.ts @@ -33,6 +33,23 @@ describe("paseo config schema", () => { }); }); + it("parses worktree config when only one lifecycle hook is present", () => { + expect( + PaseoConfigSchema.parse({ + worktree: { + setup: "npm install", + terminals: [{ name: "Dev", command: "npm run dev" }], + }, + }), + ).toEqual({ + worktree: { + setup: ["npm install"], + teardown: [], + terminals: [{ name: "Dev", command: "npm run dev" }], + }, + }); + }); + it("parses all metadata generation instruction entries", () => { expect( PaseoConfigSchema.parse({ diff --git a/packages/protocol/src/paseo-config-schema.ts b/packages/protocol/src/paseo-config-schema.ts index 534f24060f..5061940745 100644 --- a/packages/protocol/src/paseo-config-schema.ts +++ b/packages/protocol/src/paseo-config-schema.ts @@ -56,8 +56,8 @@ export const PaseoConfigRawSchema = z .passthrough(); export const WorktreeConfigSchema = PaseoWorktreeConfigRawSchema.extend({ - setup: z.unknown().transform(normalizeLifecycleCommands), - teardown: z.unknown().transform(normalizeLifecycleCommands), + setup: z.unknown().optional().transform(normalizeLifecycleCommands), + teardown: z.unknown().optional().transform(normalizeLifecycleCommands), }) .passthrough() .catch({ setup: [], teardown: [] }); diff --git a/packages/protocol/src/schedule/types.test.ts b/packages/protocol/src/schedule/types.test.ts index afeed7c90a..b89e0ee0f8 100644 --- a/packages/protocol/src/schedule/types.test.ts +++ b/packages/protocol/src/schedule/types.test.ts @@ -1,16 +1,16 @@ -import { describe, expect, test } from "vitest"; +import { describe, expect, it } from "vitest"; -import { ScheduleCadenceSchema } from "./types.js"; +import { ScheduleCadenceSchema, ScheduleRunSchema, ScheduleTargetSchema } from "./types.js"; -describe("ScheduleCadenceSchema", () => { - test("accepts existing UTC cron cadence without a time zone", () => { +describe("schedule schemas", () => { + it("accepts existing UTC cron cadence without a time zone", () => { expect(ScheduleCadenceSchema.parse({ type: "cron", expression: "0 9 * * *" })).toEqual({ type: "cron", expression: "0 9 * * *", }); }); - test("accepts timezone-aware cron cadence", () => { + it("accepts timezone-aware cron cadence", () => { expect( ScheduleCadenceSchema.parse({ type: "cron", @@ -23,4 +23,25 @@ describe("ScheduleCadenceSchema", () => { timezone: "America/New_York", }); }); + + it("accepts legacy uuid-shaped agent ids with version 0", () => { + const legacyAgentId = "00000000-0000-0000-0000-000000000001"; + + expect(ScheduleTargetSchema.parse({ type: "agent", agentId: legacyAgentId })).toEqual({ + type: "agent", + agentId: legacyAgentId, + }); + expect( + ScheduleRunSchema.parse({ + id: "run-1", + scheduledFor: "2026-01-01T00:00:00.000Z", + startedAt: "2026-01-01T00:00:00.000Z", + endedAt: null, + status: "succeeded", + agentId: legacyAgentId, + output: null, + error: null, + }).agentId, + ).toBe(legacyAgentId); + }); }); diff --git a/packages/protocol/src/schedule/types.ts b/packages/protocol/src/schedule/types.ts index 5ff7991930..02503009a4 100644 --- a/packages/protocol/src/schedule/types.ts +++ b/packages/protocol/src/schedule/types.ts @@ -17,10 +17,17 @@ export const ScheduleCadenceSchema = z.discriminatedUnion("type", [ ]); export type ScheduleCadence = z.infer; +const LegacyUuidSchema = z + .string() + .regex( + /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, + "Invalid UUID", + ); + export const ScheduleTargetSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("agent"), - agentId: z.string().uuid(), + agentId: LegacyUuidSchema, }), z.object({ type: z.literal("new-agent"), @@ -56,7 +63,7 @@ export const ScheduleRunSchema = z.object({ startedAt: z.string(), endedAt: z.string().nullable(), status: z.enum(["running", "succeeded", "failed"]), - agentId: z.string().uuid().nullable(), + agentId: LegacyUuidSchema.nullable(), output: z.string().nullable(), error: z.string().nullable(), });