import * as v from 'valibot' export const VISUAL_GAME_NOTE_IMPORTANCE_VALUES = Object.freeze([ 'minor', 'major', 'critical', ]) export const VISUAL_GAME_SECTION_DEFINITIONS = Object.freeze([ { id: 'meta', title: '基础信息', file: 'meta.json', description: '关卡身份、列表展示、开场文字和关联素材清单。', defaultValue: { id: '', title: '', tagline: '', assetManifestId: '', portrait_cover_tag: null, opening: [], openingOptions: {} }, }, { id: 'flow', title: '场景流程', file: 'flow.json', description: '关卡初始位置和初始剧情阶段。地点目录由 map.json 维护。', defaultValue: { initialLocation: '', initialPhase: 'entry' }, }, { id: 'stats', title: '核心数值', file: 'stats.json', description: '初始数值、数值含义和 KP 调整数值时的规则。', defaultValue: { initialStats: {}, definitions: [], rules: [] }, }, { id: 'state', title: '初始状态', file: 'state.json', description: '开局 flags、NPC 状态、任务状态和场景事件状态初值。', defaultValue: { initialFlags: {}, initialNpcStates: {}, initialQuestStates: {}, initialSceneEventStates: {} }, }, { id: 'stateRegistry', title: '状态字典', file: 'state_registry.json', description: '登记运行状态 key 的来源、写入者、读取者和运行时影响。', defaultValue: { entries: [] }, }, { id: 'map', title: '地图', file: 'map.json', description: '多场景地图、连接关系、场景内 NPC / 物件挂载和解锁条件。', defaultValue: { initialLocation: '', locations: [] }, }, { id: 'quests', title: '任务', file: 'quests.json', description: '主线 / 支线任务、步骤、完成条件、失败条件和影响。', defaultValue: { mainQuestId: '', quests: [] }, }, { id: 'sceneEvents', title: '场景事件', file: 'scene_events.json', description: '进入场景、首次调查、阶段变化等场景事件。', defaultValue: { events: [] }, }, { id: 'kp', title: 'KP 规则', file: 'kp.json', description: '写给 KP Agent 的剧本级裁判原则。', defaultValue: { keeperRole: '', principles: [], forbidden: [] }, }, { id: 'dynamicStory', title: '动态剧情', file: 'dynamic_story.json', description: '单场景自由剧情的语义追踪、旁白事实、生成结局和一致性边界。', defaultValue: { enabled: false, single_scene: false, min_distinct_turns: 2, max_active_branches: 1, max_generated_endings_per_run: 1, tracker_expires_after_turns: 8, principles: [], forbidden: [], reference_endings: [], }, }, { id: 'npcs', title: 'NPC', file: 'npcs.json', description: 'NPC 配置。', defaultValue: [] }, { id: 'objects', title: '道具', file: 'objects.json', description: '物件配置。', defaultValue: [] }, { id: 'clues', title: '线索', file: 'clues.json', description: '线索定义;剧本不需要线索时可为空数组。importance 可留空,非空时只能为 minor、major 或 critical。', defaultValue: [], }, { id: 'conclusions', title: '结论', file: 'conclusions.json', description: '由一个或多个线索指向的调查结论,用于笔记页归纳已发现信息;剧本不需要结论时可为空数组。importance 可留空,非空时只能为 minor、major 或 critical。', defaultValue: [], }, { id: 'infoBlocks', title: '可得知信息', file: 'info_blocks.json', description: 'NPC 可说信息、隐藏事实和解锁条件。', defaultValue: [], }, { id: 'actionRules', title: '行动规则', file: 'action_rules.json', files: ['action_rules/core.json', 'action_rules/social.json'], description: '玩家明确做某些事时产生稳定 delta。', defaultValue: [], }, { id: 'endings', title: '结局', file: 'endings.json', description: '结局定义和触发条件。', defaultValue: [] }, { id: 'players', title: '玩家角色', file: 'players.json', description: '本关可选玩家人物卡。', defaultValue: { defaultCharacterId: '', characters: [] }, }, { id: 'media', title: '素材清单', file: 'media.json', description: '图片、视频、音效和 BGM 的 tag 到文件映射。', defaultValue: { id: '', tags: [] }, }, ]) export const VisualGameScenarioListItem = v.object({ id: v.string(), title: v.string(), tagline: v.optional(v.string()), summary: v.string(), tags: v.array(v.string()), ending_count: v.number(), play_count: v.number(), like_count: v.number(), author_id: v.nullable(v.string()), author_name: v.nullable(v.string()), author_avatar_url: v.nullable(v.string()), status: v.optional(v.picklist(['published', 'coming_soon'])), asset_manifest_id: v.optional(v.string()), play_state: v.picklist(['available', 'coming_soon']), cover_url: v.optional(v.string()), portrait_cover_url: v.nullable(v.string()), series_id: v.nullable(v.string()), series_title: v.nullable(v.string()), series_cover_url: v.optional(v.nullable(v.string())), episode_no: v.nullable(v.number()), created_at: v.optional(v.nullable(v.unknown())), updated_at: v.optional(v.nullable(v.unknown())), }) export const VisualGameScenarioPage = v.object({ items: v.array(VisualGameScenarioListItem), page: v.pipe(v.number(), v.integer(), v.minValue(1)), page_size: v.pipe(v.number(), v.integer(), v.minValue(1)), total: v.pipe(v.number(), v.integer(), v.minValue(0)), has_more: v.boolean(), }) export const VisualGameAsset = v.object({ tag: v.string(), type: v.string(), file: v.string(), public_url: v.optional(v.string()), object_key: v.optional(v.string()), mime: v.optional(v.string()), size: v.optional(v.nullable(v.number())), hash: v.optional(v.string()), desc: v.optional(v.string()), }) export const VisualGameAssetManifest = v.object({ id: v.string(), stage_rules: v.optional(v.unknown()), audio_rules: v.optional(v.unknown()), tags: v.array(VisualGameAsset), }) export const VisualGameTtsVoice = v.object({ voice_id: v.string(), display_name: v.string(), voice_type: v.picklist(['native', 'clone']), model: v.string(), provider: v.string(), }) export const VisualGameTtsVoiceList = v.object({ voices: v.array(VisualGameTtsVoice), }) export const VisualGameAssetIndexItem = v.object({ id: v.optional(v.string()), manifest_id: v.string(), tag: v.string(), type: v.string(), file: v.optional(v.string()), public_url: v.optional(v.string()), object_key: v.optional(v.nullable(v.string())), mime: v.optional(v.nullable(v.string())), size: v.optional(v.nullable(v.number())), width: v.optional(v.nullable(v.number())), height: v.optional(v.nullable(v.number())), hash: v.optional(v.nullable(v.string())), }) export const VisualGameCreateRunResponse = v.object({ run_id: v.string(), scenario: v.unknown(), run: v.unknown(), unlocked_info: v.array(v.unknown()), available_clues: v.array(v.unknown()), known_clues: v.array(v.unknown()), available_conclusions: v.optional(v.array(v.unknown())), known_conclusions: v.optional(v.array(v.unknown())), }) export const VisualGameRunDetail = v.object({ run: v.unknown(), scenario: v.unknown(), unlocked_info: v.array(v.unknown()), available_clues: v.array(v.unknown()), known_clues: v.array(v.unknown()), available_conclusions: v.optional(v.array(v.unknown())), known_conclusions: v.optional(v.array(v.unknown())), ending_records: v.optional(v.array(v.unknown())), turn_video_stage: v.nullable(v.object({ turn_id: v.string(), end_frame: v.object({ id: v.string(), url: v.string(), mime: v.string(), hash: v.string(), }), })), }) export const VisualGameRunVideo = v.object({ id: v.string(), run_id: v.string(), ending_id: v.string(), variant: v.string(), status: v.picklist(['queued', 'running', 'ready', 'failed']), generation_request_id: v.nullable(v.string()), video_copy_status: v.picklist(['pending', 'running', 'ready', 'failed']), video_copy_error: v.nullable(v.string()), video_title: v.nullable(v.string()), video_summary: v.nullable(v.string()), video_copy_generated_at: v.optional(v.unknown()), is_public: v.boolean(), public_url: v.optional(v.nullable(v.string())), duration_ms: v.optional(v.nullable(v.number())), width: v.optional(v.nullable(v.number())), height: v.optional(v.nullable(v.number())), error_message: v.optional(v.nullable(v.string())), requested_at: v.optional(v.unknown()), started_at: v.optional(v.unknown()), completed_at: v.optional(v.unknown()), created_at: v.optional(v.unknown()), updated_at: v.optional(v.unknown()), }) export const VisualGameRunVideoQuote = v.object({ action: v.literal('full_run_video'), required_points: v.number(), current_balance: v.number(), shortfall_points: v.number(), balance_after: v.number(), }) export const VisualGameRunVideoGeneration = v.object({ video: v.nullable(VisualGameRunVideo), quote: VisualGameRunVideoQuote, }) export const VisualGameRunVideoGenerationResult = v.object({ video: VisualGameRunVideo, quote: VisualGameRunVideoQuote, replayed: v.boolean(), }) export const VisualGameNotification = v.object({ id: v.string(), type: v.picklist(['video_generation_ready', 'video_generation_failed']), resource_type: v.literal('run_video'), resource_id: v.string(), video_title: v.nullable(v.string()), scenario_title: v.string(), series_title: v.nullable(v.string()), season_no: v.nullable(v.number()), action_href: v.string(), read_at: v.nullable(v.string()), created_at: v.string(), }) export const VisualGameNotificationList = v.object({ notifications: v.array(VisualGameNotification), }) export const VisualGameRunListItem = v.object({ run_id: v.string(), scenario_id: v.string(), scenario_title: v.string(), scenario_cover_url: v.string(), series_id: v.nullable(v.string()), series_title: v.nullable(v.string()), episode_no: v.nullable(v.number()), season_id: v.nullable(v.string()), season_no: v.nullable(v.number()), parent_season_id: v.nullable(v.string()), fork_episode_no: v.nullable(v.number()), scenario_version_id: v.optional(v.nullable(v.string())), status: v.picklist(['active', 'completed']), current_location_id: v.optional(v.nullable(v.string())), phase: v.optional(v.nullable(v.string())), turn_no: v.number(), ending_id: v.optional(v.nullable(v.string())), ending_title: v.optional(v.nullable(v.string())), ending_description: v.optional(v.nullable(v.string())), last_action: v.string(), created_at: v.optional(v.unknown()), updated_at: v.optional(v.unknown()), completed_at: v.optional(v.unknown()), video: v.nullable(VisualGameRunVideo), }) export const VisualGameRunList = v.object({ runs: v.array(VisualGameRunListItem), }) export const VisualGameScenarioVideo = v.object({ id: v.string(), run_id: v.string(), scenario_id: v.string(), season_id: v.nullable(v.string()), season_no: v.nullable(v.number()), season_title: v.nullable(v.string()), season_summary: v.nullable(v.string()), video_title: v.string(), video_summary: v.string(), scenario_title: v.string(), series_id: v.nullable(v.string()), series_title: v.nullable(v.string()), episode_no: v.nullable(v.number()), scenario_version_id: v.optional(v.nullable(v.string())), ending_id: v.string(), ending_title: v.optional(v.nullable(v.string())), variant: v.string(), is_public: v.boolean(), player_id: v.nullable(v.string()), player_name: v.string(), series_summary: v.string(), run_status: v.picklist(['active', 'completed']), source_url: v.string(), poster_url: v.optional(v.nullable(v.string())), duration_ms: v.optional(v.nullable(v.number())), width: v.optional(v.nullable(v.number())), height: v.optional(v.nullable(v.number())), play_count: v.number(), like_count: v.number(), favorite_count: v.number(), created_at: v.optional(v.unknown()), completed_at: v.optional(v.unknown()), }) export const VisualGameScenarioVideoList = v.object({ videos: v.array(VisualGameScenarioVideo), }) export const VisualGameShowcaseVideoPage = v.object({ videos: v.array(VisualGameScenarioVideo), next_cursor: v.nullable(v.string()), }) export const VisualGameVideoEngagement = v.object({ video_id: v.string(), play_count: v.number(), like_count: v.number(), favorite_count: v.number(), is_liked: v.boolean(), is_favorited: v.boolean(), }) export const VisualGameSeason = v.object({ id: v.string(), series_id: v.string(), season_no: v.number(), status: v.picklist(['active', 'completed', 'abandoned']), previous_completed_at: v.unknown(), created_at: v.unknown(), updated_at: v.unknown(), }) export const VisualGameSeasonList = v.object({ seasons: v.array(VisualGameSeason), }) export const VisualGameRunEvent = v.object({ id: v.optional(v.string()), run_id: v.optional(v.string()), turn_id: v.optional(v.nullable(v.string())), seq: v.number(), type: v.string(), payload: v.unknown(), created_at: v.optional(v.unknown()), }) export const VisualGameAgentCall = v.object({ id: v.string(), run_id: v.string(), turn_id: v.optional(v.nullable(v.string())), agent_role: v.string(), model: v.optional(v.nullable(v.string())), prompt_hash: v.optional(v.nullable(v.string())), request: v.optional(v.unknown()), response: v.optional(v.unknown()), prompt_tokens: v.nullable(v.number()), completion_tokens: v.nullable(v.number()), cached_tokens: v.nullable(v.number()), cache_write_tokens: v.nullable(v.number()), cost_cents: v.optional(v.nullable(v.number())), status: v.string(), error_message: v.optional(v.nullable(v.string())), created_at: v.optional(v.unknown()), completed_at: v.optional(v.unknown()), }) export const VisualGameAgentCallRoleSummary = v.object({ role: v.string(), total_calls: v.number(), failed_calls: v.number(), total_prompt_tokens: v.number(), total_completion_tokens: v.number(), total_cost_cents: v.number(), }) export const VisualGameAgentCallModelSummary = v.object({ model: v.string(), total_calls: v.number(), failed_calls: v.number(), total_prompt_tokens: v.number(), total_completion_tokens: v.number(), total_cost_cents: v.number(), }) export const VisualGameAgentCallSummary = v.object({ total_calls: v.number(), failed_calls: v.number(), total_prompt_tokens: v.number(), total_completion_tokens: v.number(), total_cost_cents: v.number(), roles: v.array(VisualGameAgentCallRoleSummary), models: v.array(VisualGameAgentCallModelSummary), }) export const VisualGameAgentCallList = v.object({ calls: v.array(VisualGameAgentCall), summary: VisualGameAgentCallSummary, }) export const VisualGameProgress = v.nullable(v.object({ user_id: v.string(), scenario_id: v.string(), current_run_id: v.optional(v.nullable(v.string())), selected_character_id: v.optional(v.nullable(v.string())), onboarding_dismissed: v.optional(v.boolean()), ending_records: v.optional(v.array(v.unknown())), prefs: v.optional(v.unknown()), created_at: v.optional(v.unknown()), updated_at: v.optional(v.unknown()), })) export const VisualGameProgressPatch = v.object({ current_run_id: v.optional(v.nullable(v.string())), selected_character_id: v.optional(v.nullable(v.string())), onboarding_dismissed: v.optional(v.boolean()), prefs: v.optional(v.unknown()), }) export const VisualGamePaywallTarget = v.object({ kind: v.picklist(['episode', 'branch', 'ending', 'continue']), key: v.union([v.string(), v.number()]), }) export const VisualGamePaywallInfo = v.object({ scenario_id: v.string(), target: VisualGamePaywallTarget, unlocked: v.boolean(), reason: v.string(), price_points: v.optional(v.number()), free_quota_left: v.optional(v.number()), balance: v.optional(v.number()), }) export const VisualGameSaveSlot = v.object({ id: v.optional(v.string()), user_id: v.optional(v.string()), scenario_id: v.optional(v.string()), run_id: v.string(), slot_no: v.number(), label: v.optional(v.nullable(v.string())), snapshot: v.optional(v.unknown()), created_at: v.optional(v.unknown()), updated_at: v.optional(v.unknown()), }) export const VisualGameDeleteSaveSlotResponse = v.object({ deleted: v.number(), }) export const VisualGameTurnVideo = v.object({ turn_id: v.string(), match_method: v.picklist(['exact_transition', 'semantic_reuse', 'intent_video']), similarity: v.nullable(v.number()), speech: v.array(v.object({ start: v.number(), speaker: v.picklist(['player', 'npc']), })), asset: v.object({ id: v.string(), url: v.string(), mime: v.string(), hash: v.string(), duration_ms: v.number(), }), start_frame: v.object({ id: v.string(), url: v.string(), mime: v.string(), hash: v.string() }), end_frame: v.object({ id: v.string(), url: v.string(), mime: v.string(), hash: v.string() }), }) const VisualGamePlaybackAsset = v.object({ id: v.string(), url: v.string(), mime: v.string(), hash: v.string(), }) const VisualGamePlaybackVideo = v.object({ asset: VisualGamePlaybackAsset, start_frame: VisualGamePlaybackAsset, end_frame: VisualGamePlaybackAsset, }) const VisualGameActionPlaybackSegment = v.object({ order: v.number(), kind: v.literal('action'), match_method: v.picklist(['exact_transition', 'semantic_reuse', 'intent_video']), similarity: v.nullable(v.number()), video: VisualGamePlaybackVideo, }) const VisualGameSpeechPlaybackSegment = v.object({ order: v.number(), kind: v.literal('speech'), actor_type: v.picklist(['player', 'npc']), actor_id: v.string(), expression_state: v.string(), text: v.string(), loopable: v.boolean(), video: VisualGamePlaybackVideo, }) export const VisualGameTurnPlaybackPlan = v.object({ turn_id: v.string(), segments: v.array(v.union([ VisualGameActionPlaybackSegment, VisualGameSpeechPlaybackSegment, ])), }) const VisualGameReplayPlan = v.object({ turn_id: v.string(), segments: v.array(v.union([ VisualGameActionPlaybackSegment, v.object({ order: v.number(), kind: v.literal('speech'), actor_type: v.picklist(['player', 'npc']), actor_id: v.string(), expression_state: v.string(), text: v.string(), loopable: v.boolean(), video: VisualGamePlaybackVideo, audio: VisualGamePlaybackAsset, tts_call_id: v.string(), }), ])), }) export const VisualGameStoryVideoLog = v.object({ run_id: v.string(), run: v.unknown(), turns: v.array(v.object({ turn_no: v.number(), playback_plan: VisualGameTurnPlaybackPlan, replay_plan: v.nullable(VisualGameReplayPlan), })), }) const VisualGameEngineTurnEvent = v.object({ type: v.picklist([ 'error', 'action_rejected', 'player_kp', 'dynamic_story', 'npc_response', 'npc_action_kp', 'final', ]), kp_output: v.optional(v.unknown()), action: v.optional(v.unknown()), judgement: v.optional(v.unknown()), scene_result: v.optional(v.unknown()), response: v.optional(v.unknown()), run: v.optional(v.unknown()), ending: v.optional(v.unknown()), npc_action_kp_output: v.optional(v.unknown()), npc_action_judgement: v.optional(v.unknown()), npc_action_result: v.optional(v.unknown()), newly_unlocked: v.optional(v.array(v.unknown())), newly_unlocked_clues: v.optional(v.array(v.unknown())), unlocked_info: v.optional(v.array(v.unknown())), available_clues: v.optional(v.array(v.unknown())), known_clues: v.optional(v.array(v.unknown())), available_conclusions: v.optional(v.array(v.unknown())), known_conclusions: v.optional(v.array(v.unknown())), ending_records: v.optional(v.array(v.unknown())), scenario: v.optional(v.unknown()), error: v.optional(v.string()), error_code: v.optional(v.picklist([ 'content_filter', 'BAD_REQUEST', 'NOT_FOUND', 'FORBIDDEN', 'CONFLICT', 'SAVE_SLOT_CONFLICT', 'INSUFFICIENT_BALANCE', 'PRODUCT_NOT_FOUND', 'RULE_NOT_FOUND', 'ALREADY_UNLOCKED', 'AI_OUTPUT_INVALID', 'internal_error', ])), required_points: v.optional(v.number()), current_balance: v.optional(v.number()), shortfall_points: v.optional(v.number()), turn_playback_plan: v.optional(VisualGameTurnPlaybackPlan), }) const VisualGameTurnPlaybackPlanEvent = v.object({ type: v.literal('turn_playback_plan'), turn_playback_plan: VisualGameTurnPlaybackPlan, }) const VisualGameTurnPlaybackPhaseEvent = v.object({ type: v.literal('turn_playback_phase'), phase: v.picklist(['player', 'npc']), turn_playback_plan: VisualGameTurnPlaybackPlan, }) export const VisualGameTurnEvent = v.union([ VisualGameEngineTurnEvent, VisualGameTurnPlaybackPlanEvent, VisualGameTurnPlaybackPhaseEvent, ]) export const VisualGameEndingText = v.object({ title: v.string(), summary: v.string(), poem: v.string(), stats: v.unknown(), generated_at: v.optional(v.string()), }) export const VisualGamePackSection = v.object({ id: v.string(), title: v.string(), file: v.string(), description: v.optional(v.string()), value: v.optional(v.unknown()), }) export const VisualGameScenarioVersion = v.object({ id: v.string(), pack_id: v.string(), version: v.number(), status: v.string(), title: v.string(), tagline: v.optional(v.string()), asset_manifest_id: v.optional(v.string()), scenario: v.optional(v.unknown()), compile_result: v.optional(v.unknown()), published_at: v.optional(v.nullable(v.unknown())), created_at: v.optional(v.nullable(v.unknown())), is_current: v.optional(v.boolean()), }) export const VisualGamePack = v.object({ id: v.string(), title: v.string(), tagline: v.optional(v.string()), tags: v.array(v.string()), owner_id: v.optional(v.nullable(v.string())), locale: v.optional(v.string()), status: v.optional(v.string()), visibility: v.optional(v.string()), current_version_id: v.optional(v.nullable(v.string())), series_id: v.optional(v.nullable(v.string())), series_title: v.optional(v.nullable(v.string())), episode_no: v.optional(v.nullable(v.number())), created_at: v.optional(v.nullable(v.unknown())), updated_at: v.optional(v.nullable(v.unknown())), versions: v.optional(v.array(VisualGameScenarioVersion)), assets: v.optional(v.array(VisualGameAssetIndexItem)), sections: v.optional(v.record(v.string(), v.unknown())), }) export const VisualGamePackList = v.object({ sections: v.array(VisualGamePackSection), packs: v.array(v.unknown()), }) export const VisualGamePackCompileResponse = v.object({ ok: v.boolean(), compile_result: v.unknown(), pack: VisualGamePack, }) export const VisualGameContentTranslationLocaleStatus = v.object({ locale: v.picklist(['zh-CN', 'en', 'ja', 'ko', 'es', 'pt']), status: v.picklist(['source', 'missing', 'ready', 'translating', 'saving', 'unsupported']), updated_at: v.nullable(v.unknown()), reusable_text_count: v.number(), pending_text_count: v.number(), base_version_id: v.nullable(v.string()), replace_required: v.boolean(), }) export const VisualGameContentTranslationStatus = v.object({ pack_id: v.string(), version_id: v.string(), source_locale: v.picklist(['zh-CN', 'en', 'ja', 'ko', 'es', 'pt']), supported: v.boolean(), source_text_count: v.number(), locales: v.array(VisualGameContentTranslationLocaleStatus), }) export const VisualGameContentTranslationResult = v.object({ pack_id: v.string(), version_id: v.string(), locale: v.picklist(['zh-CN', 'en', 'ja', 'ko', 'es', 'pt']), status: v.picklist(['skipped', 'translated']), text_count: v.optional(v.number()), reused_text_count: v.optional(v.number()), translated_text_count: v.optional(v.number()), base_version_id: v.optional(v.nullable(v.string())), }) export const VisualGameContentTranslationInventoryItem = v.object({ pointer: v.string(), section: v.string(), field: v.string(), source_text: v.string(), translated_text: v.nullable(v.string()), status: v.picklist(['source', 'pending', 'reused', 'translated']), }) export const VisualGameContentTranslationInventory = v.object({ pack_id: v.string(), version_id: v.string(), locale: v.picklist(['zh-CN', 'en', 'ja', 'ko', 'es', 'pt']), status: v.picklist(['source', 'missing', 'ready', 'unsupported']), base_version_id: v.nullable(v.string()), items: v.array(VisualGameContentTranslationInventoryItem), }) export const VisualGameContentTranslationProgress = v.object({ pack_id: v.string(), version_id: v.string(), locale: v.picklist(['zh-CN', 'en', 'ja', 'ko', 'es', 'pt']), status: v.picklist(['idle', 'translating', 'saving', 'completed', 'failed']), completed_text_count: v.number(), total_text_count: v.number(), error_message: v.optional(v.nullable(v.string())), }) export const VisualGamePackAssetUploadResponse = v.object({ ok: v.boolean(), asset: VisualGameAssetIndexItem, pack: VisualGamePack, }) export const VisualGamePackDeleteResponse = v.object({ mode: v.picklist(['archived', 'deleted']), pack: v.optional(v.nullable(VisualGamePack)), }) export const VisualGamePreviewTokenResponse = v.object({ token: v.string(), expires_at: v.string(), }) export const VisualGameEditAuditLog = v.object({ id: v.string(), draft_id: v.string(), actor_id: v.optional(v.nullable(v.string())), action: v.string(), target_type: v.string(), target_id: v.optional(v.nullable(v.string())), before: v.optional(v.unknown()), after: v.optional(v.unknown()), created_at: v.optional(v.unknown()), }) export const VisualGameEditAuditLogList = v.object({ logs: v.array(VisualGameEditAuditLog), }) export const VisualGamePublishCheck = v.object({ id: v.string(), version_id: v.string(), check_type: v.string(), status: v.string(), message: v.optional(v.string()), target_ref: v.optional(v.string()), updated_at: v.optional(v.unknown()), }) export const VisualGamePublishCheckList = v.object({ checks: v.array(VisualGamePublishCheck), }) export const VisualGameCreatorProject = v.object({ id: v.string(), title: v.string(), idea_text: v.string(), language: v.picklist(['en', 'zh', 'ja', 'ko', 'es', 'pt']), status: v.picklist(['queued', 'generating', 'generation_failed', 'pending_review', 'review_rejected', 'published', 'archived']), current_task_id: v.nullable(v.string()), current_candidate_pack_id: v.nullable(v.string()), current_candidate_version_id: v.nullable(v.string()), published_version_id: v.nullable(v.string()), review_rejection_reason: v.nullable(v.string()), cover_url: v.nullable(v.string()), created_at: v.string(), updated_at: v.nullable(v.string()), }) export const VisualGameCreatorProjectList = v.object({ projects: v.array(VisualGameCreatorProject) }) /** * @typedef {import('valibot').InferOutput} VisualGameCreateRunResponseT * @typedef {import('valibot').InferOutput} VisualGameRunDetailT * @typedef {import('valibot').InferOutput} VisualGameRunEventT * @typedef {import('valibot').InferOutput} VisualGameAgentCallT * @typedef {import('valibot').InferOutput} VisualGameAgentCallSummaryT * @typedef {import('valibot').InferOutput} VisualGameAgentCallListT * @typedef {import('valibot').InferOutput} VisualGameProgressT * @typedef {import('valibot').InferOutput} VisualGameProgressPatchT * @typedef {import('valibot').InferOutput} VisualGamePaywallInfoT * @typedef {import('valibot').InferOutput} VisualGameSaveSlotT * @typedef {import('valibot').InferOutput} VisualGameTurnEventT * @typedef {import('valibot').InferOutput} VisualGameAssetManifestT * @typedef {import('valibot').InferOutput} VisualGameAssetIndexItemT * @typedef {import('valibot').InferOutput} VisualGameTtsVoiceT * @typedef {import('valibot').InferOutput} VisualGameTtsVoiceListT * @typedef {import('valibot').InferOutput} VisualGameEndingTextT * @typedef {import('valibot').InferOutput} VisualGamePackAssetUploadResponseT * @typedef {import('valibot').InferOutput} VisualGamePackDeleteResponseT * @typedef {import('valibot').InferOutput} VisualGamePreviewTokenResponseT * @typedef {import('valibot').InferOutput} VisualGamePublishCheckT * @typedef {import('valibot').InferOutput} VisualGamePublishCheckListT */