import type { VehiclePackageArtifact } from './universal-vehicles.js'; import type { DeviceDefinitionV1 } from './devices.js'; export declare const PROTOCOL_VERSION = 2; /** Additive, versioned presentation intent carried on `helix:init`. */ export declare const RUNTIME_FIDELITY_VERSION: 1; export declare const RUNTIME_FIDELITY_VISUAL_TIERS: readonly ["auto", "minimal", "low", "medium", "high", "ultra"]; export declare const RUNTIME_FIDELITY_RENDER_SCALES: readonly ["1.5", "1", "0.77", "0.59"]; export declare const RUNTIME_PROFILE_QUALITIES: readonly ["minimum", "balanced", "high", "ultra"]; export type RuntimeFidelityVisualTier = (typeof RUNTIME_FIDELITY_VISUAL_TIERS)[number]; export type RuntimeFidelityRenderScale = (typeof RUNTIME_FIDELITY_RENDER_SCALES)[number]; export type RuntimeProfileQuality = (typeof RUNTIME_PROFILE_QUALITIES)[number]; /** * Player presentation request frozen by the shell for one World load. * * This intentionally carries no resolved tier, device capability, subsystem * budget, physics setting, authority decision, tick rate, or network cadence. * The engine resolves Auto and owns those policies. */ export type RuntimeFidelityIntent = { version: typeof RUNTIME_FIDELITY_VERSION; requestedVisualTier: RuntimeFidelityVisualTier; renderScaleOverride: RuntimeFidelityRenderScale | null; runtimeProfileQuality: RuntimeProfileQuality | null; }; /** * Validate untrusted handshake data and copy it into SDK-owned launch state. * Unknown versions, unexpected fields, or invalid members fail closed to null; * consumers then use their engine's backward-compatible Auto policy. */ export declare function normalizeRuntimeFidelityIntent(value: unknown): RuntimeFidelityIntent | null; export type HelixUser = { id: string; username: string; displayName: string | null; }; export type HelixWorldContext = { id: string; slug: string; title: string; }; export type TrialAvatar = { source: 'trial'; inventoryItemId: null; itemId: string; glbUrl: string; contentHash: string | null; skeleton: 'helix-humanoid'; }; /** Server-sealed, page-scoped package configuration for a local World customizer. */ export type PackageCustomizerSubject = { readonly kind: 'package'; readonly hostKind: string; readonly hostId: string; readonly packageId: string; readonly version: string; readonly renditions: readonly Readonly<{ id: string; url: string; }>[]; readonly board: Readonly<{ id: string; }>; /** Slot instance id to page-scoped package choice id; never inventory authority. */ readonly assignments: Readonly>; readonly hiddenSlotIds?: readonly string[]; /** Existing shell EquipBoard projection, validated but intentionally not duplicated by the SDK. */ readonly equipBoard: Readonly>; }; /** Exact page-scoped vehicle trial identity; never an owned inventory identity. */ export type PackageCustomizerSource = Readonly<{ kind: 'trial'; hostId: string; itemId: string; }>; export interface PackageCustomizerContext { readonly source: PackageCustomizerSource | null; readonly subject: PackageCustomizerSubject | null; } /** A universal item temporarily placed in the test grounds for this session. */ export type WorldSpawnedItem = { placementId: string; instanceId: string; itemId: string; worldId: string; ownerUserId: string; ownerUsername: string | null; kind: string | null; usage: string | null; name: string; thumbnailUrl: string | null; modelUrl: string | null; /** Checksum-bound authored Universal Vehicle package, when this is a vehicle. */ vehiclePackage: VehiclePackageArtifact | null; transform: { position: { x: number; y: number; z: number; }; rotationEulerDeg?: { x: number; y: number; z: number; }; scale?: number; } | null; spawnedAt: string; /** * THE INSTANCE IS OUT — the platform holds it at `location_type = 'equipped'`, so it is with a * player somewhere rather than standing here. A world draws the placement as a faint ghost * (0.4 opacity) instead of a solid object, for every viewer and not only its owner. * * It is a flag on the row rather than an absence from the list because the placement row is never * deleted on activate: the bay stays yours while the thing is out, it keeps counting against slot * limits, and the world still has to draw something there. * * OPTIONAL: a shell that predates the field omits it, and `undefined` reads as "not out" — which * is exactly the behaviour every published world has today. Read it STRICTLY (`=== true`), the * same rule the engine's `WorldSpawnedVehicle.equipped` applies. */ equipped?: boolean; /** True for a page-scoped Marketplace trial with no owned inventory instance. */ trial?: true; /** Whether the shell should expose the owned-instance customizer for this row. */ canCustomize?: boolean; /** Server-sealed package subject for a page-scoped trial; absent for ordinary owned rows. */ customizerSubject?: PackageCustomizerSubject; }; /** One authenticated, session-only Device placement projected by Try Now. */ export type DeviceTrialSpawn = Readonly<{ placementId: string; itemId: string; kind: 'device'; name: string; /** Optimized visual rendition; authored named-node behavior remains bound to definition.model. */ modelUrl: string | null; transform: Readonly<{ position: readonly [number, number, number]; yawDeg: number; scale: number; }>; /** Trial expiry as Unix epoch seconds. */ expiresAt: number; deviceDefinition: Readonly<{ id: string; version: string; }>; definition: DeviceDefinitionV1; definitionChecksumSha256: string; functionalPin: Readonly<{ continuumPackageId: string; continuumPackageVersion: string; continuumManifestCid: string; }>; /** Exact server-resolved add-on and appearance receipt, validated by the presentation adapter. */ composition: unknown; }>; export type HelixTrial = { id: string; itemId: string; kind: 'avatar' | 'vehicle' | 'device'; /** Trial expiry as Unix epoch seconds. */ expiresAt: number; avatar?: TrialAvatar; spawnedItem?: WorldSpawnedItem | DeviceTrialSpawn; }; export type HelixSession = { token: string; expiresAt: string; scopes: string[]; trial?: HelixTrial; user: HelixUser; }; export type DebugLogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug'; export type DebugLogEntry = { level: DebugLogLevel; args: string[]; t: number; }; export type Balance = { lix: number; coins: number; updatedAt?: string; }; export type PurchaseStatus = 'Pending' | 'Granted' | 'Claimed' | 'AlreadyOwned' | 'InsufficientFunds' | 'MaxPerUserReached' | 'ProductInactive' | 'ProductNotFound' | 'NotInWorld' | 'Unauthorized' | 'RateLimited' | 'Cancelled' | 'Failed' | 'Timeout' | 'NotFound' | 'PriceChanged'; export type PurchaseStatusMeaning = { money: 'charged' | 'not-charged' | 'unknown'; ownsAfter: boolean; }; export declare const PURCHASE_STATUS_MEANING: Record; export type PurchaseResult = { status: PurchaseStatus; completed: boolean; reason?: string; itemId?: string | null; balanceAfterLix?: number; priceLix?: number; balanceLix?: number; shortfallLix?: number; ownedCount?: number; maxPerPlayer?: number | null; maxPerUser?: number | null; requestedQuantity?: number; fulfilledQuantity?: number; distributionId?: string | null; listingId?: string | null; inventoryInstanceIds?: string[]; serialNumbers?: number[]; resaleEligibleAt?: string | null; receipt?: string; }; export type PurchaseOptions = { /** Whole-number quantity. Collectible listings always require exactly 1. */ quantity?: number; /** Stable caller-owned retry key. Omit to let the SDK persist one safely. */ idempotencyKey?: string; }; export type ItemEditionMode = 'standard' | 'collectible'; export type AccountVerificationLevel = 'basic' | 'verified' | 'identity_verified' | 'business_verified'; export type InventoryItem = { id?: string; itemId: string; title: string; kind?: string; category?: string; subCategory?: string | null; quantity: number; description?: string | null; thumbnailUrl?: string | null; equipped?: boolean; avatarSlot?: string | null; socket?: string | null; metadata?: Record; }; export type EquippedAvatar = { source: 'equipped' | 'auto' | 'default' | 'trial'; inventoryItemId?: string | null; itemId: string | null; glbUrl: string | null; contentHash?: string | null; skeleton: string | null; /** Immutable public render recipe for this configured avatar instance. */ composition?: AvatarComposition | null; }; export type AvatarComposition = { compositionDigest: string; appearanceDigest: string; hostKind: 'character'; appearance: { hiddenSlotIds: string[]; materialOverrides: Record; colorOverrides: Record; }; instanceThumbnailUrl: string | null; base: { itemId: string; modelUrl: string | null; }; materials: Record; visibleAddOns: Array<{ slotId: string; itemId: string; modelUrl: string | null; visualOps: unknown[]; mountSocket?: string | null; fill?: 'all' | 'each'; }>; }; export type EquippedEmote = { /** Wheel position: 'r0'..'r7' are the eight radial slots, 'sit'/'lie' the two posture emotes. */ slot: 'r0' | 'r1' | 'r2' | 'r3' | 'r4' | 'r5' | 'r6' | 'r7' | 'sit' | 'lie'; /** The universal-item id this emote came from. */ itemId: string; /** Animation clip URL the world fetches to play it. */ clip: string; playback: 'oneshot' | 'loop'; /** The emote's sound URL; absent when it is silent. */ audio?: string; /** The item's display title for the wheel's petal; absent when the platform named it nothing. */ label?: string; }; export type EquippedEmotes = { emotes: EquippedEmote[]; }; export type EquippedLocomotionPack = { /** Servable pack-manifest base URL the seat moves on; '' = the platform locomotion set. */ pack: string; }; export type HelixPresenceActivity = { /** Short activity line (≤128 chars), e.g. "Racing — lap 3/5". Required. */ state: string; /** Optional secondary detail line (≤128 chars). */ details?: string; /** Optional custom image (asset key/URL). Omit → world thumbnail. */ image?: string; /** Optional party sizing shown on the presence card. */ party?: { current: number; max: number; }; /** Hint that this activity is joinable (gated on the MP instance runtime). */ joinable?: boolean; }; export type AvatarBaseShape = 'male' | 'female' | 'custom'; export type AvatarLoadoutSlot = { slot: string; itemId: string; inventoryItemId: string; socket?: string | null; inventoryItem: InventoryItem; }; export type UniversalAvatarBody = { mode: 'preset'; baseMeshId: 'helix:humanoid:male' | 'helix:humanoid:female'; } | { mode: 'customMesh'; avatarItemId: string; inventoryItemId: string; glbUrl: string | null; skeleton: string | null; }; export type AvatarLoadout = { version: 1; body: UniversalAvatarBody; baseShape: AvatarBaseShape; customAvatar: EquippedAvatar; equippedSlots: AvatarLoadoutSlot[]; inventory: InventoryItem[]; updatedAt: string; }; export type AvatarLoadoutPatch = { baseShape?: AvatarBaseShape; customAvatarInventoryItemId?: string | null; equippedSlots?: Record; }; export type MarketplaceListing = { /** Exact secondary listing capability used by purchaseListing(). */ listingId?: string; itemId: string; title: string; description?: string | null; priceLix: number; isFree: boolean; thumbnailUrl?: string | null; kind?: string; category?: string; }; export type MarketplaceQuery = { kind?: string; category?: string; search?: string; limit?: number; }; export type CameraAspect = 'portrait' | 'landscape' | 'square' | 'free'; export type CameraSavePayload = { bytes: ArrayBuffer; contentType: string; caption?: string; aspect?: CameraAspect; }; export type SavedPhoto = { assetId: string; url: string; thumbnailUrl: string; worldName: string; createdAt: string; }; /** Where the camera currently is. `device-hold` is the avatar holding the phone before it is raised. */ export type CameraSessionState = 'inactive' | 'device-hold' | 'rear' | 'selfie' | 'drone'; export type CameraMediaMode = 'photo' | 'video'; /** Where the camera sits in space. Distinct from CameraSessionState, which also carries `inactive`. */ export type CameraSpatialMode = 'rear' | 'selfie' | 'drone'; /** Which body the player's look/move input drives while the camera is up. */ export type CameraControlTarget = 'camera' | 'avatar'; /** * The lens buttons, in the exact order HELIX OS renders them. * * These are PRESETS, not the zoom domain: effective zoom is CONTINUOUS between * the first and last entry, and a preset is only where a tap lands. A runtime * that clamps to these five is wrong. */ export declare const CAMERA_LENS_PRESETS: readonly [0.5, 1, 2, 3, 5]; export type CameraLensPreset = (typeof CAMERA_LENS_PRESETS)[number]; export type CameraFilterId = 'original' | 'vivid' | 'warm' | 'cool' | 'mono'; /** Self-timer settings. 0 = no timer (the shutter commits on the press). */ export type CameraTimerSeconds = 0 | 3 | 10; /** Camera aim in degrees, relative to the avatar's facing. */ export type CameraAim = { yaw: number; pitch: number; }; /** Tap-to-focus target in normalized viewport coordinates: 0,0 = top-left, 1,1 = bottom-right. */ export type CameraFocusPoint = { x: number; y: number; }; /** * What THIS runtime can actually do, right now. * * The runtime contract's `runtimeBindings.*.availability` is a STATIC claim * about a method's existence; this is a LIVE one about a running instance, and * it is the only thing that can answer "this build has photo but not video" or * "the player just revoked the microphone, so world audio is gone". HELIX OS * disables the controls this says are false — it must never grey out a control * on a guess, and must never present a control whose call it knows will fail. * * All-false is the correct answer for a runtime that has not reported yet: a * capability is claimed, never assumed. `unavailableReason` is a human line the * shell may show next to a disabled control ("this world ships photo only"). */ export type HelixCameraCapability = { photo: boolean; video: boolean; drone: boolean; /** The avatar can hold the device in a two-handed pose (drives `device-hold`). */ twoHandPose: boolean; focusPoint: boolean; exposureBias: boolean; /** Video can record the world's audio bed, not just a silent picture. */ worldAudio: boolean; unavailableReason?: string; }; /** * The shutter as an EDGE, not a pulse. * * `sequence` is monotonic and increments once per committed capture. A runtime * fires on the CHANGE, so a duplicated or replayed helix:camera-state (the shell * re-sends its snapshot after the handshake) cannot double-expose — which a * boolean `shutterPressed` would do every single time. */ export type HelixCameraShutter = { sequence: number; /** True while a video recording is actually running. */ recording: boolean; /** Seconds left on the self-timer; 0 when no countdown is running. */ timerRemainingSeconds: number; }; export type CameraVector3 = { x: number; y: number; z: number; }; /** Drone pose in world space. Rotation is degrees. */ export type HelixCameraDroneTransform = { position: CameraVector3; yaw: number; pitch: number; roll: number; }; /** How the drone treats its subject: free flight, held framing, or a circling orbit. */ export type CameraDroneLockMode = 'free' | 'lock' | 'orbit'; /** * Phase-2 vocabulary. Contracted now so HELIX OS, the web engine and Unreal * agree on the names before anyone implements them; `camera.flyDrone` is * `unavailable` until the flight-authority question is answered (a drone is a * second body in a shared world, so who may move it is not a client decision). */ export type HelixCameraDroneState = { /** Stable per-session drone identity — a world may see more than one. */ droneId: string; transform: HelixCameraDroneTransform; /** Metres per second, world axes. */ velocity: CameraVector3; /** Seconds the runtime should smooth toward `transform`; 0 = snap. */ interpolationSeconds: number; lockMode: CameraDroneLockMode; /** Platform userId the drone locks/orbits; '' while free-flying. */ subjectUserId: string; /** True while the drone is flying itself back to its owner. */ returning: boolean; /** Metres between the drone and its return point. */ returnDistance: number; }; /** A request to move the drone. Every field is optional; omitted fields are left alone. */ export type HelixCameraDroneCommand = { lockMode?: CameraDroneLockMode; subjectUserId?: string; transform?: HelixCameraDroneTransform; velocity?: CameraVector3; interpolationSeconds?: number; returnToAvatar?: boolean; }; /** * The whole camera, as one shell-owned snapshot pushed to the runtime. * * Sent WHOLE rather than as deltas: the shell re-sends it after helix:init so a * runtime that booted mid-session is never left guessing, and a whole snapshot * makes that replay idempotent (see HelixCameraShutter for the one field where * that needed thought). */ export type HelixCameraState = { session: CameraSessionState; media: CameraMediaMode; spatial: CameraSpatialMode; controlTarget: CameraControlTarget; /** The lens button that is lit — the preset nearest `zoom`, not the zoom itself. */ lens: CameraLensPreset; /** Effective zoom, continuous within the preset range. */ zoom: number; filter: CameraFilterId; timerSeconds: CameraTimerSeconds; aim: CameraAim; shutter: HelixCameraShutter; /** The capability the shell currently believes the runtime has (see reportCapability). */ capability: HelixCameraCapability; /** Absent until the player taps to focus, and whenever capability.focusPoint is false. */ focusPoint?: CameraFocusPoint; /** Exposure compensation in stops. Absent whenever capability.exposureBias is false. */ exposureBias?: number; /** Absent for every handheld mode; present only while `session` is 'drone'. */ drone?: HelixCameraDroneState; }; /** * A WORLD SCRIPT asking HELIX OS to change the camera — the one camera surface * that is world-driven rather than player-driven, and the reason the * `camera.control` manifest capability exists. Every field is optional; the * shell applies the ones present and answers with the resulting state. * * `shutter: true` requests ONE capture (it increments the sequence); it is not a * level. `recording` is a level — set it false to stop. */ export type HelixCameraControl = { session?: CameraSessionState; media?: CameraMediaMode; spatial?: CameraSpatialMode; controlTarget?: CameraControlTarget; zoom?: number; filter?: CameraFilterId; timerSeconds?: CameraTimerSeconds; aim?: CameraAim; focusPoint?: CameraFocusPoint; exposureBias?: number; shutter?: boolean; recording?: boolean; /** Record the world's audio bed with a video (requires capability.worldAudio). */ worldAudio?: boolean; }; export type PurchaseContext = { ref: string; title: string; description: string | null; thumbnailUrl: string | null; priceLix: number; isFree: boolean; balanceLix: number; balanceAfterLix: number; owned: boolean; ownedCount: number; maxPerPlayer?: number | null; maxPerUser?: number | null; eligible: boolean; eligibilityStatus: PurchaseStatus | null; quantity?: number; unitPriceLix?: number; totalPriceLix?: number; editionMode?: ItemEditionMode; supply?: number | null; minted?: number | null; remaining?: number | null; serialNumber?: number | null; distributionId?: string | null; listingId?: string | null; creatorName?: string | null; sellerName?: string | null; maxClaims?: number | null; startsAt?: string | null; endsAt?: string | null; verificationLevel?: AccountVerificationLevel; requiredVerificationLevel?: AccountVerificationLevel | null; resaleEligibleAt?: string | null; holdUntil?: string | null; grants?: ProductGrant[]; }; export type ProductGrant = { kind: 'item'; itemId: string; quantity?: number; } | { kind: 'pass'; passKey?: string; durationSeconds?: number; } | { kind: 'currency'; code: string; amount: number; display?: 'wallet' | 'uses'; }; export type Entitlements = { v: 1; passes: Record; balances: Record; owned: Record; }; export type ConsumeResult = { applied: boolean; balance: number; reason?: 'insufficient'; }; export type DataStoreListPayload = { prefix?: string; cursor?: string; limit?: number; }; export type DataStoreListPage = { keys: string[]; nextCursor?: string; }; export type DataStoreWriteOptions = { expectedVersion?: number; }; export type DataStoreListOptions = { cursor?: string; limit?: number; }; export type LeaderboardOrder = 'desc' | 'asc'; export type LeaderboardEntry = { subject: string; displayName: string; score: number; source: string; updatedAt: string; }; export type LeaderboardPage = { entries: LeaderboardEntry[]; truncated: boolean; }; export type LeaderboardSubmitOptions = { order?: LeaderboardOrder; }; export type LeaderboardTopOptions = { limit?: number; order?: LeaderboardOrder; }; export type LeaderboardSubmitResult = { best: number | null; updated: boolean; }; export type AchievementSummary = { id: string; key: string; name: string; description: string | null; points: number; hidden: boolean; earned: boolean; rarityTier: string; earnPct: number | null; iconUrl: string | null; }; export type AchievementListResult = { achievements: AchievementSummary[]; total: number; earned: number; }; export type AchievementMineResult = { achievements: AchievementSummary[]; }; export type HelixRequestMethod = 'wallet.getBalance' | 'listen.consume' | 'listen.release' | 'listen.heartbeat' | 'listen.close' | 'marketplace.purchase' | 'marketplace.getListings' | 'inventory.hasItem' | 'inventory.getQuantity' | 'inventory.getMyItems' | 'inventory.equipItem' | 'avatar.getEquipped' | 'avatar.getLoadout' | 'avatar.updateLoadout' | 'avatar.openCreator' | 'emotes.openCustomizer' | 'emotes.getEquipped' | 'quickSlots.get' | 'quickSlots.patch' | 'quickSlots.activate' | 'quickSlots.deactivate' | 'quickSlots.openCustomizer' | 'quickSlots.browseAll' | 'iwp.getContext' | 'iwp.getPurchase' | 'iwp.getEntitlements' | 'iwp.consumeEntitlement' | 'dataStore.get' | 'dataStore.getEntry' | 'dataStore.set' | 'dataStore.delete' | 'dataStore.list' | 'leaderboard.submit' | 'leaderboard.top' | 'achievements.list' | 'achievements.mine' | 'camera.savePhoto' | 'camera.control'; export type HelixEvent = 'balance-changed' | 'inventory-changed' | 'entitlements-changed' | 'avatar-changed' | 'equipment-changed' | 'emotes-changed' | 'items-changed' | 'overlay-open' | 'overlay-closed'; export type HelixMultiplayerRosterEntry = { /** Room-scoped seat key (the RoomState.players map key / colyseus sessionId). */ sessionId: string; /** Platform user id (PlayerState.userId, contract v18) — '' when the room predates v18. */ userId: string; displayName: string; /** Equipped-avatar GLB URL ('' = default body). NOT a profile picture — resolve those via userId. */ avatarUrl: string; isSelf: boolean; /** False while the seat sits in the server's reconnection grace window. */ connected: boolean; }; export type HelixMultiplayerState = { /** The joined Colyseus room instance id — the platform's only true instance identity. */ roomId: string; /** This client's seat key (also flagged isSelf in `players`). */ sessionId: string; playerCount: number; /** The room's seat cap (credential maxPlayers), or null when unknown (e.g. resumed via reload). */ maxPlayers: number | null; players: HelixMultiplayerRosterEntry[]; /** The room's type (manifest `multiplayer.roomTypes`). Absent for the default room. */ roomType?: string; /** * True when the room is a TICKETED instance: entered by redeeming a ticket, private to its ticket holders. * Its `roomId` must not be offered as a public join target (`?instance=`). Absent otherwise. */ ticketed?: boolean; }; export type HelixVoiceMicMode = 'ptt' | 'open' | 'muted'; export type HelixVoicePermission = 'prompt' | 'granted' | 'denied'; export type HelixVoiceDevice = { deviceId: string; label: string; }; export type HelixVoiceSettings = { micMode: HelixVoiceMicMode; /** Preferred audioinput deviceId — a hint; the world falls back to default when it no longer resolves. */ micDeviceId?: string; /** The preferred mic's human label. deviceIds are origin-scoped, so when micDeviceId doesn't resolve * where the mic is captured, the world re-targets the device whose label matches. */ micDeviceLabel?: string; /** Platform userIds this player has locally muted. Always the full list, never a delta. */ mutedUserIds: string[]; /** Outgoing mic gain, 0..2 (1 = as-captured, Discord-style 0–200%). */ micVolume?: number; /** Incoming voice master gain, 0..2 (1 = as-received). Composes with the engine's proximity gain. */ outputVolume?: number; /** True (default): no manual gate — transmit whatever the mic hears (browser noise suppression still applies). */ micAutoSensitivity?: boolean; /** Manual input-sensitivity gate threshold in meter units (0..1): input below it is cut. */ micSensitivity?: number; }; export type HelixVoiceState = { /** False when this session has no voice (world lacks voice.* permission / no server configured). */ available: boolean; connected: boolean; micMode: HelixVoiceMicMode; /** Effective KeyboardEvent.key used by this world while push-to-talk is active. */ pttKey?: string; /** True while the local microphone's mode gate is open. In PTT this follows the held key; in * open-mic mode it stays true. The live level remains the source of detected loudness. */ transmitting: boolean; permission: HelixVoicePermission; /** The device actually in use — may differ from the requested micDeviceId after fallback. */ activeDeviceId?: string; devices: HelixVoiceDevice[]; /** Platform userIds currently speaking (muted-by-me and out-of-channel players never appear). Throttled world-side. */ speakingUserIds: string[]; /** The player's current exclusive voice channel (absent = the ambient space). */ channelId?: string; /** True until the one-time autoplay unlock gesture inside the world — while set, remote audio is * silent AND the local microphone pipeline cannot run, so nothing is sent either. */ audioBlocked?: boolean; error?: string; }; export type HelixNotificationKind = 'success' | 'failure' | 'notification' | 'message'; export type HelixNotification = { kind: HelixNotificationKind; title: string; message?: string; timeoutMs?: number; actionLabel?: string; actionHref?: string; }; export type HelixPromptState = 'default' | 'available' | 'locked' | 'busy'; export type HelixDeviceFormFactor = 'phone' | 'tablet'; /** Shell-owned Helix OS visibility mirrored into every embedded world. */ export type HelixDeviceState = { open: boolean; form?: HelixDeviceFormFactor; }; export type HelixInteractionPrompt = { id: string; title: string; description?: string; input?: { key?: string; button?: string; label?: string; }; actionLabel?: string; state?: HelixPromptState; priority?: number; }; /** * Dispatched by the ENGINE XR module on every session state change; the SDK forwards it to the shell. * detail: { state: 'unsupported'|'idle'|'entering'|'active'|'exiting'; reason?: string; source: 'humanoid-character' } * Sticky store: document.__helixXrSession = { state, reason } */ export declare const XR_SESSION_EVENT = "helix:xr-session"; /** * Dispatched by the SDK when Helix.prompts.set/clear is called; the engine XR module draws it in-scene. * detail: { prompt: { id: string; label: string; state?: 'default'|'available'|'locked'|'busy' } | null; source: 'helix-sdk' } * Sticky store: document.__helixInteractionPrompt = detail */ export declare const XR_INTERACTION_PROMPT_EVENT = "helix:interaction-prompt"; export type WorldToShellMessage = { type: 'helix:ready'; protocolVersion: number; } | { type: 'helix:request-login'; requestId: string; } | { type: 'helix:log'; entry: DebugLogEntry; } | { type: 'helix:open-device'; form?: HelixDeviceFormFactor; } | { type: 'helix:multiplayer-state'; state: HelixMultiplayerState | null; } | { type: 'helix:multiplayer-join-error'; instanceId: string; message: string; } | { type: 'helix:voice-state'; state: HelixVoiceState | null; } | { type: 'helix:voice-level'; level: number; } | { type: 'helix:camera-capability'; capability: HelixCameraCapability; } | { type: 'helix:request'; requestId: string; method: HelixRequestMethod; payload?: unknown; } | { type: 'helix:notify'; notification: HelixNotification; } | { type: 'helix:set-interaction-prompt'; prompt: HelixInteractionPrompt; } | { type: 'helix:clear-interaction-prompt'; promptId?: string; } | { type: 'helix:xr-session'; state: 'entering' | 'active' | 'exiting' | 'idle' | 'unsupported'; reason?: string; } /** * ADR-0003: the answer to `helix:distribution-capability-request`, from the world's room. Either the minted * single-use capability or the room's reason (`not_in_room` when this world never joined a room). */ | { type: 'helix:distribution-capability-result'; requestId: string; capability?: string; expiresAt?: string; ref?: string; error?: string; }; export type ShellToWorldMessage = { type: 'helix:init'; protocolVersion: number; world: HelixWorldContext; session: HelixSession | null; debug?: boolean; instanceId?: string; apiBaseUrl?: string | null; /** * Versioned presentation intent. Additive: older shells omit it and the * SDK exposes null so the engine retains its Auto fallback. */ runtimeFidelity?: RuntimeFidelityIntent; } | { type: 'helix:session'; session: HelixSession | null; } | { type: 'helix:login-result'; requestId: string; ok: boolean; reason?: 'dismissed' | 'unavailable'; } | { type: 'helix:response'; requestId: string; ok: boolean; result?: unknown; error?: string; errorCode?: string; } | { type: 'helix:event'; event: HelixEvent; data?: unknown; } | { type: 'helix:voice-settings'; settings: HelixVoiceSettings; } | { type: 'helix:voice-ptt'; down: boolean; } | { type: 'helix:device-state'; open: boolean; form?: HelixDeviceFormFactor; } | { type: 'helix:camera-state'; state: HelixCameraState; } | { type: 'helix:leave'; } /** * ADR-0003: the shell's confirmed Claim/Buy of a World route asks the world to obtain a one-time acquisition * capability from its room (the only party that can mint one). Answered with helix:distribution-capability-result. */ | { type: 'helix:distribution-capability-request'; requestId: string; ref: string; }; export declare function isShellMessage(data: unknown): data is ShellToWorldMessage;