From 92780a4acb0da02266126e7e4b1e61d99daa07c0 Mon Sep 17 00:00:00 2001 From: chaewon-huh Date: Mon, 6 Jul 2026 14:39:20 +0900 Subject: [PATCH] fix: expose avatar handle schema constraints --- src/lib/tool-registry.ts | 38 +++++++++++++++++++++++++++----------- test/cli.test.ts | 26 +++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/lib/tool-registry.ts b/src/lib/tool-registry.ts index 9e42504..976ed00 100644 --- a/src/lib/tool-registry.ts +++ b/src/lib/tool-registry.ts @@ -62,6 +62,26 @@ type StaticToolDefinition = { const booleanProperty = { type: "boolean" }; const stringProperty = { type: "string" }; +const avatarHandlePattern = + "^@?(?![._])(?!.*[._]$)(?!.*[._]{2})[A-Za-z0-9._]{2,30}$"; +const avatarHandleProperty = { + ...stringProperty, + minLength: 2, + maxLength: 31, + pattern: avatarHandlePattern, + description: + "Public avatar handle, with optional leading @. Sume normalizes handles to lowercase. Use letters, numbers, dot, or underscore; do not start/end with dot or underscore; do not use consecutive dots/underscores.", +}; +const avatarCreateHandleProperty = { + ...avatarHandleProperty, + description: + `${avatarHandleProperty.description} User-created Avatar handles cannot use the reserved sume_ prefix.`, +}; +const avatarCreateHandleAliasProperty = { + ...avatarCreateHandleProperty, + description: + "Alias for avatar_handle with the same constraints: optional leading @, lowercase normalization, letters/numbers/dot/underscore, no first/last/consecutive dot or underscore, and no reserved sume_ prefix for user-created Avatars.", +}; const agentOutputProperties = { agent: { ...booleanProperty, @@ -178,9 +198,7 @@ const mcpAvatarCreateBaseProperties = { "Optional exact Avatar model-run id. Omit to use the primary Avatar 1.0 generate route.", }, avatar_handle: { - ...stringProperty, - minLength: 1, - description: "Desired canonical public avatar handle, without @.", + ...avatarCreateHandleProperty, }, ...mcpAvatarCommunicationProperties, }; @@ -1023,12 +1041,10 @@ const staticToolDefinitions: Record = { description: "Avatar request type.", }, avatar_handle: { - ...stringProperty, - description: "Desired canonical public avatar handle, without @.", + ...avatarCreateHandleProperty, }, handle: { - ...stringProperty, - description: "Alias for avatar_handle.", + ...avatarCreateHandleAliasProperty, }, prompt: { ...stringProperty, @@ -1354,8 +1370,8 @@ const staticToolDefinitions: Record = { description: "Optional product/reference image URL for the public API request.", }, avatar_handle: { - ...stringProperty, - description: "Ready avatar handle to use in the video.", + ...avatarHandleProperty, + description: `${avatarHandleProperty.description} Ready avatar handle to use in the video.`, }, scene_prompt: { ...stringProperty, @@ -1397,8 +1413,8 @@ const staticToolDefinitions: Record = { "Optional public HTTPS product/reference image URL.", }, avatar_handle: { - ...stringProperty, - description: "Ready avatar handle, with or without @.", + ...avatarHandleProperty, + description: `${avatarHandleProperty.description} Ready avatar handle for the video.`, }, scene_prompt: { ...stringProperty, diff --git a/test/cli.test.ts b/test/cli.test.ts index 09d5d83..4e10565 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -8,6 +8,8 @@ import { describe, expect, it } from "vitest"; const tsx = "node_modules/.bin/tsx"; const execFileAsync = promisify(execFile); +const avatarHandlePattern = + "^@?(?![._])(?!.*[._]$)(?!.*[._]{2})[A-Za-z0-9._]{2,30}$"; type ApiRequest = { body: unknown; @@ -660,8 +662,20 @@ describe("CLI", () => { "sume/avatar/v1", ]), }), - avatar_handle: expect.objectContaining({ type: "string" }), - handle: expect.objectContaining({ type: "string" }), + avatar_handle: expect.objectContaining({ + type: "string", + minLength: 2, + maxLength: 31, + pattern: avatarHandlePattern, + description: expect.stringContaining("reserved sume_ prefix"), + }), + handle: expect.objectContaining({ + type: "string", + minLength: 2, + maxLength: 31, + pattern: avatarHandlePattern, + description: expect.stringContaining("Alias for avatar_handle"), + }), image_url: expect.objectContaining({ type: "string" }), }, }, @@ -883,7 +897,13 @@ describe("CLI", () => { "sume/avatar/v1", ]), }), - avatar_handle: expect.objectContaining({ type: "string" }), + avatar_handle: expect.objectContaining({ + type: "string", + minLength: 2, + maxLength: 31, + pattern: avatarHandlePattern, + description: expect.stringContaining("optional leading @"), + }), handle: expect.objectContaining({ type: "string" }), image_url: expect.objectContaining({ type: "string" }), type: expect.objectContaining({