From 14e059862b546cc3360b6f0f91a4bf6dcd475966 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Sat, 4 Apr 2026 01:22:55 +0000 Subject: [PATCH] feat(36-02): add voiceMode field to createMessageSchema and ChatMessage interface - Add VOICE_MODES constant and VoiceMode type to shared validators/chat.ts - Extend createMessageSchema with optional voiceMode enum field - Add voiceMode optional field to ChatMessage interface in types/chat.ts - Add 36-voice-schema.test.ts with 6 passing tests for voiceMode validation --- packages/shared/src/types/chat.ts | 1 + packages/shared/src/validators/chat.ts | 4 ++ server/src/__tests__/36-voice-schema.test.ts | 63 ++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 server/src/__tests__/36-voice-schema.test.ts diff --git a/packages/shared/src/types/chat.ts b/packages/shared/src/types/chat.ts index 36ff15e3..1ddeb689 100644 --- a/packages/shared/src/types/chat.ts +++ b/packages/shared/src/types/chat.ts @@ -67,6 +67,7 @@ export interface ChatMessage { content: string; agentId: string | null; messageType: string | null; + voiceMode?: "text" | "voice_input" | "full_voice" | null; createdAt: string; updatedAt: string | null; files?: ChatFile[]; diff --git a/packages/shared/src/validators/chat.ts b/packages/shared/src/validators/chat.ts index 98b15a43..d6b4b957 100644 --- a/packages/shared/src/validators/chat.ts +++ b/packages/shared/src/validators/chat.ts @@ -12,11 +12,15 @@ export const updateConversationSchema = z.object({ archivedAt: z.string().datetime().nullable().optional(), }); +export const VOICE_MODES = ["text", "voice_input", "full_voice"] as const; +export type VoiceMode = (typeof VOICE_MODES)[number]; + export const createMessageSchema = z.object({ role: z.enum(["user", "assistant", "system"]), content: z.string().min(1).max(100_000), agentId: z.string().uuid().optional(), messageType: z.string().optional(), + voiceMode: z.enum(VOICE_MODES).optional(), }); export const handoffSchema = z.object({ diff --git a/server/src/__tests__/36-voice-schema.test.ts b/server/src/__tests__/36-voice-schema.test.ts new file mode 100644 index 00000000..090eef06 --- /dev/null +++ b/server/src/__tests__/36-voice-schema.test.ts @@ -0,0 +1,63 @@ +// [nexus] Schema validation tests for voiceMode field (Plan 36-02) +import { describe, it, expect } from "vitest"; +import { createMessageSchema } from "@paperclipai/shared/validators/chat"; + +describe("createMessageSchema — voiceMode field", () => { + it("parses voiceMode 'full_voice' and returns the value", () => { + const result = createMessageSchema.parse({ + role: "user", + content: "hi", + voiceMode: "full_voice", + }); + expect(result.voiceMode).toBe("full_voice"); + }); + + it("parses voiceMode 'voice_input' and returns the value", () => { + const result = createMessageSchema.parse({ + role: "user", + content: "hi", + voiceMode: "voice_input", + }); + expect(result.voiceMode).toBe("voice_input"); + }); + + it("parses voiceMode 'text' and returns the value", () => { + const result = createMessageSchema.parse({ + role: "user", + content: "hi", + voiceMode: "text", + }); + expect(result.voiceMode).toBe("text"); + }); + + it("parses without voiceMode and returns undefined for voiceMode", () => { + const result = createMessageSchema.parse({ + role: "user", + content: "hi", + }); + expect(result.voiceMode).toBeUndefined(); + }); + + it("throws ZodError when voiceMode is invalid", () => { + expect(() => + createMessageSchema.parse({ + role: "user", + content: "hi", + voiceMode: "invalid", + }) + ).toThrow(); + }); + + it("preserves existing fields — role, content, agentId, messageType", () => { + const result = createMessageSchema.parse({ + role: "assistant", + content: "hello world", + agentId: "00000000-0000-0000-0000-000000000001", + messageType: "markdown", + }); + expect(result.role).toBe("assistant"); + expect(result.content).toBe("hello world"); + expect(result.agentId).toBe("00000000-0000-0000-0000-000000000001"); + expect(result.messageType).toBe("markdown"); + }); +});