@runwayml/avatars 0.16.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @runwayml/avatars
2
+
3
+ Framework-agnostic SDK for real-time AI avatar interactions. Works with any JavaScript framework or none at all.
4
+
5
+ ## Quick start
6
+
7
+ ```javascript
8
+ import { streamTo } from '@runwayml/avatars';
9
+
10
+ // Fetch credentials from your server
11
+ const res = await fetch('/api/avatar/connect', {
12
+ method: 'POST',
13
+ headers: { 'Content-Type': 'application/json' },
14
+ body: JSON.stringify({ avatarId: 'influencer' }),
15
+ });
16
+ const credentials = await res.json();
17
+
18
+ // Start streaming to a video element
19
+ const session = await streamTo({ credentials, target: document.getElementById('avatar') });
20
+
21
+ // Control the session
22
+ session.mic.toggle();
23
+ session.end();
24
+ ```
25
+
26
+ ## API
27
+
28
+ ### Entry points
29
+
30
+ - **`streamTo({ credentials, target })`** -- Start a session and stream video to an element. Handles the consume call, LiveKit connection, and media setup. Returns an `AvatarSession`.
31
+ - **`connect({ credentials })`** -- Start a session without a video element (headless). Use `session.streamTo(element)` later to attach video.
32
+
33
+ ### `AvatarSession`
34
+
35
+ Returned by `streamTo` and `connect`. Provides:
36
+
37
+ - **`session.state`** -- `'idle' | 'connecting' | 'active' | 'ending' | 'ended' | 'error'`
38
+ - **`session.sessionId`** -- The session identifier
39
+ - **`session.mic`** -- `{ isEnabled, enable(), disable(), toggle() }`
40
+ - **`session.camera`** -- `{ isEnabled, enable(), disable(), toggle() }`
41
+ - **`session.screenShare`** -- `{ isActive, start(), stop(), toggle() }`
42
+ - **`session.end()`** -- End the session
43
+ - **`session.transcript(options?)`** -- Create a transcript accumulator
44
+ - **`session.onClientEvent(tool, handler)`** -- Listen for typed client events
45
+ - **`session.on(event, handler)`** / **`session.off(event, handler)`** -- Event emitter
46
+
47
+ ### Events
48
+
49
+ ```javascript
50
+ import { AvatarEvent } from '@runwayml/avatars';
51
+
52
+ session.on(AvatarEvent.StateChanged, (state) => {});
53
+ session.on(AvatarEvent.Transcript, (entry) => {});
54
+ session.on(AvatarEvent.ClientEvent, (event) => {});
55
+ session.on(AvatarEvent.Error, (error) => {});
56
+ session.on(AvatarEvent.AvatarVideoReady, (track) => {});
57
+ session.on(AvatarEvent.AvatarAudioReady, (track) => {});
58
+ session.on(AvatarEvent.MediaChanged, () => {});
59
+ ```
60
+
61
+ ### Error handling
62
+
63
+ All errors are instances of `AvatarError` with a typed `code` and optional `cause`:
64
+
65
+ ```javascript
66
+ import { AvatarError } from '@runwayml/avatars';
67
+
68
+ try {
69
+ const session = await streamTo({ credentials, target: el });
70
+ } catch (err) {
71
+ if (err instanceof AvatarError) {
72
+ console.log(err.code, err.message, err.cause);
73
+ }
74
+ }
75
+
76
+ session.on(AvatarEvent.Error, (err) => {
77
+ if (err instanceof AvatarError && err.code === 'MEDIA_PERMISSION_DENIED') {
78
+ showPermissionPrompt();
79
+ }
80
+ });
81
+ ```
82
+
83
+ | Code | When |
84
+ |------|------|
85
+ | `CONSUME_FAILED` | Session consume HTTP request failed |
86
+ | `CONNECTION_FAILED` | LiveKit room connection failed |
87
+ | `MEDIA_PERMISSION_DENIED` | Mic or camera permission denied on connect |
88
+ | `MEDIA_DEVICE_ERROR` | Mic or camera toggle failed |
89
+ | `SCREEN_SHARE_FAILED` | Screen share toggle failed |
90
+ | `PUBLISH_FAILED` | Track publish failed |
91
+ | `UNKNOWN` | Unexpected error |
92
+
93
+ ### Server subpath
94
+
95
+ ```javascript
96
+ import { consumeSession, clientTool, pageActionTools } from '@runwayml/avatars/api';
97
+ ```
98
+
99
+ Server-safe utilities with no browser APIs. Use in Next.js API routes, Express handlers, etc.
100
+
101
+ ## React
102
+
103
+ For React apps, use [`@runwayml/avatars-react`](../react/) which provides components and hooks on top of this package.
package/dist/api.cjs ADDED
@@ -0,0 +1,189 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
+
7
+ // src/tools.ts
8
+ var toolSchemas = /* @__PURE__ */ new WeakMap();
9
+ var asyncSchemaWarned = /* @__PURE__ */ new WeakSet();
10
+ function getClientToolSchema(tool) {
11
+ return toolSchemas.get(tool);
12
+ }
13
+ function validateClientToolArgs(tool, args) {
14
+ const schema = toolSchemas.get(tool);
15
+ if (!schema) {
16
+ return args;
17
+ }
18
+ const result = schema["~standard"].validate(args);
19
+ if (result instanceof Promise) {
20
+ if (!asyncSchemaWarned.has(tool) && typeof console !== "undefined") {
21
+ asyncSchemaWarned.add(tool);
22
+ console.warn(
23
+ `[@runwayml/avatars-react] Async Standard Schema validation is not supported for client events (tool "${tool.name}"); subsequent events for this tool will be dropped silently.`
24
+ );
25
+ }
26
+ return null;
27
+ }
28
+ return isSuccess(result) ? result.value : null;
29
+ }
30
+ function isSuccess(result) {
31
+ return result.issues == null;
32
+ }
33
+ function clientTool(name, config) {
34
+ const tool = {
35
+ type: "client_event",
36
+ name,
37
+ description: config.description
38
+ };
39
+ if ("schema" in config) {
40
+ toolSchemas.set(tool, config.schema);
41
+ }
42
+ return tool;
43
+ }
44
+
45
+ // src/error.ts
46
+ var AvatarError = class extends Error {
47
+ constructor(code, message, cause) {
48
+ super(message);
49
+ __publicField(this, "code");
50
+ __publicField(this, "cause");
51
+ this.name = "AvatarError";
52
+ this.code = code;
53
+ this.cause = cause;
54
+ }
55
+ };
56
+
57
+ // src/api/config.ts
58
+ var DEFAULT_BASE_URL = "https://api.dev.runwayml.com";
59
+
60
+ // src/api/consume.ts
61
+ async function consumeSession(options) {
62
+ const { sessionId, sessionKey, baseUrl = DEFAULT_BASE_URL } = options;
63
+ const url = `${baseUrl}/v1/realtime_sessions/${sessionId}/consume`;
64
+ const response = await fetch(url, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ Authorization: `Bearer ${sessionKey}`
69
+ }
70
+ });
71
+ if (!response.ok) {
72
+ const errorText = await response.text();
73
+ throw new AvatarError(
74
+ "CONSUME_FAILED",
75
+ `Failed to consume session: ${response.status} ${errorText}`
76
+ );
77
+ }
78
+ return response.json();
79
+ }
80
+
81
+ // src/api/page-actions.ts
82
+ var clickTool = clientTool("click", {
83
+ description: "Clicks an interactive element on the page by its target ID",
84
+ args: {}
85
+ });
86
+ var scrollToTool = clientTool("scroll_to", {
87
+ description: "Scrolls the page to an element by its target ID",
88
+ args: {}
89
+ });
90
+ var highlightTool = clientTool("highlight", {
91
+ description: "Highlights an element on the page to draw attention to it by its target ID",
92
+ args: {}
93
+ });
94
+ var pageActionTools = [
95
+ {
96
+ ...clickTool,
97
+ parameters: [
98
+ {
99
+ name: "target",
100
+ type: "string",
101
+ description: "The ID or data-avatar-target value of the element to click"
102
+ }
103
+ ]
104
+ },
105
+ {
106
+ ...scrollToTool,
107
+ parameters: [
108
+ {
109
+ name: "target",
110
+ type: "string",
111
+ description: "The ID or data-avatar-target value of the element to scroll to"
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ ...highlightTool,
117
+ parameters: [
118
+ {
119
+ name: "target",
120
+ type: "string",
121
+ description: "The ID or data-avatar-target value of the element to highlight"
122
+ },
123
+ {
124
+ name: "duration",
125
+ type: "number",
126
+ description: "How long to highlight in milliseconds. Defaults to 2000"
127
+ }
128
+ ]
129
+ }
130
+ ];
131
+
132
+ // src/api/poll.ts
133
+ var DEFAULT_BASE_URL2 = "https://api.dev.runwayml.com";
134
+ var DEFAULT_TIMEOUT_MS = 3e4;
135
+ var DEFAULT_INTERVAL_MS = 1e3;
136
+ var TERMINAL_STATUSES = ["COMPLETED", "FAILED", "CANCELLED"];
137
+ async function pollUntilReady(options) {
138
+ const {
139
+ sessionId,
140
+ apiKey,
141
+ baseUrl = DEFAULT_BASE_URL2,
142
+ timeoutMs = DEFAULT_TIMEOUT_MS,
143
+ intervalMs = DEFAULT_INTERVAL_MS
144
+ } = options;
145
+ const deadline = Date.now() + timeoutMs;
146
+ while (Date.now() < deadline) {
147
+ const url = `${baseUrl}/v1/realtime_sessions/${sessionId}`;
148
+ const response = await fetch(url, {
149
+ headers: {
150
+ Authorization: `Bearer ${apiKey}`,
151
+ "X-Runway-Version": "2024-11-06"
152
+ }
153
+ });
154
+ if (!response.ok) {
155
+ const text = await response.text();
156
+ throw new AvatarError(
157
+ "CONSUME_FAILED",
158
+ `Failed to retrieve session: ${response.status} ${text}`
159
+ );
160
+ }
161
+ const session = await response.json();
162
+ if (session.status === "READY") {
163
+ if (!session.sessionKey) {
164
+ throw new AvatarError(
165
+ "CONSUME_FAILED",
166
+ "Session is READY but sessionKey is missing"
167
+ );
168
+ }
169
+ return { sessionId, sessionKey: session.sessionKey };
170
+ }
171
+ if (TERMINAL_STATUSES.includes(session.status)) {
172
+ throw new AvatarError(
173
+ "CONNECTION_FAILED",
174
+ `Session ${session.status.toLowerCase()} before becoming ready`
175
+ );
176
+ }
177
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
178
+ }
179
+ throw new AvatarError("CONNECTION_FAILED", "Session creation timed out");
180
+ }
181
+
182
+ exports.clientTool = clientTool;
183
+ exports.consumeSession = consumeSession;
184
+ exports.getClientToolSchema = getClientToolSchema;
185
+ exports.pageActionTools = pageActionTools;
186
+ exports.pollUntilReady = pollUntilReady;
187
+ exports.validateClientToolArgs = validateClientToolArgs;
188
+ //# sourceMappingURL=api.cjs.map
189
+ //# sourceMappingURL=api.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools.ts","../src/error.ts","../src/api/config.ts","../src/api/consume.ts","../src/api/page-actions.ts","../src/api/poll.ts"],"names":["DEFAULT_BASE_URL"],"mappings":"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","file":"api.cjs","sourcesContent":["import type {\n InferSchemaOutput,\n StandardSchemaResult,\n StandardSchemaV1,\n} from './standard-schema';\nimport type { ClientEvent } from './types';\n\n/**\n * A standalone client tool definition. Composable — combine into arrays\n * and derive event types with `ClientEventsFrom`.\n *\n * At runtime this is just `{ type, name, description }` (exactly what the\n * Runway session create payload expects). The `Args` generic is phantom —\n * it only exists at the TypeScript level for type narrowing.\n *\n * When the tool is defined with a `schema` ([Standard Schema](https://standardschema.dev/)),\n * the schema is attached internally (not serialized) for runtime validation\n * and to infer `Args` from the schema output type.\n */\nexport interface ClientToolDef<Name extends string = string, Args = unknown> {\n readonly type: 'client_event';\n readonly name: Name;\n readonly description: string;\n /** @internal phantom field — always `undefined` at runtime */\n readonly _args?: Args;\n}\n\nexport type ClientToolArgs<Tool extends ClientToolDef> =\n Tool extends ClientToolDef<string, infer Args> ? Args : never;\n\nexport type ClientEventFromTool<Tool extends ClientToolDef> =\n Tool extends ClientToolDef<infer Name, infer Args>\n ? ClientEvent<Name, Args>\n : never;\n\n/**\n * Derive a discriminated union of ClientEvent types from an array of tools.\n *\n * @example\n * ```typescript\n * const tools = [showQuestion, playSound];\n * type MyEvent = ClientEventsFrom<typeof tools>;\n * ```\n */\nexport type ClientEventsFrom<T extends ReadonlyArray<ClientToolDef>> =\n T[number] extends infer U\n ? U extends ClientToolDef<infer Name, infer Args>\n ? ClientEvent<Name, Args>\n : never\n : never;\n\n// Schemas live in a WeakMap so the tool def itself stays a clean\n// `{ type, name, description }` object — safe to spread into the API\n// payload and JSON.stringify without leaking schema internals.\nconst toolSchemas = new WeakMap<ClientToolDef, StandardSchemaV1>();\n\n// Tracks tools that have already emitted the async-schema warning so a\n// misconfigured schema doesn't spam the console on every incoming event.\nconst asyncSchemaWarned = new WeakSet<ClientToolDef>();\n\n/**\n * Return the Standard Schema associated with a tool, if any.\n * Useful when composing validation outside of the SDK hooks.\n */\nexport function getClientToolSchema(\n tool: ClientToolDef,\n): StandardSchemaV1 | undefined {\n return toolSchemas.get(tool);\n}\n\n/**\n * Validate parsed client event args against a tool's schema.\n *\n * Returns the typed args on success, or `null` when the schema fails or\n * when the schema would need to resolve asynchronously (Standard Schema\n * allows async `validate`, but for fire-and-forget client events we only\n * accept sync results here).\n *\n * Tools defined without a schema always validate successfully — the\n * incoming args are returned as-is.\n */\nexport function validateClientToolArgs<Tool extends ClientToolDef>(\n tool: Tool,\n args: unknown,\n): ClientToolArgs<Tool> | null {\n const schema = toolSchemas.get(tool);\n if (!schema) {\n return args as ClientToolArgs<Tool>;\n }\n\n const result = schema['~standard'].validate(args);\n if (result instanceof Promise) {\n if (!asyncSchemaWarned.has(tool) && typeof console !== 'undefined') {\n asyncSchemaWarned.add(tool);\n console.warn(\n `[@runwayml/avatars-react] Async Standard Schema validation is not supported for client events (tool \"${tool.name}\"); subsequent events for this tool will be dropped silently.`,\n );\n }\n return null;\n }\n\n return isSuccess(result) ? (result.value as ClientToolArgs<Tool>) : null;\n}\n\nfunction isSuccess<Output>(\n result: StandardSchemaResult<Output>,\n): result is { readonly value: Output; readonly issues?: undefined } {\n return result.issues == null;\n}\n\n/**\n * Define a single client tool.\n *\n * Returns a standalone object that can be composed into arrays and passed\n * to `realtimeSessions.create({ tools })`.\n *\n * Two forms are supported:\n *\n * 1. **Schema-driven** (recommended) — pass a\n * [Standard Schema](https://standardschema.dev/) (Zod, Valibot, ArkType, …)\n * to infer `args` types and enable runtime validation of incoming events:\n *\n * ```typescript\n * import { z } from 'zod';\n * const showCaption = clientTool('show_caption', {\n * description: 'Display a caption',\n * schema: z.object({ text: z.string() }),\n * });\n * ```\n *\n * 2. **Type-only** — pass an `args` type via a cast when you don't need\n * runtime validation:\n *\n * ```typescript\n * const showCaption = clientTool('show_caption', {\n * description: 'Display a caption',\n * args: {} as { text: string },\n * });\n * ```\n *\n * Combine tools and derive event types with `ClientEventsFrom`:\n *\n * ```typescript\n * const tools = [showCaption, playSound];\n * type MyEvent = ClientEventsFrom<typeof tools>;\n * ```\n */\nexport function clientTool<\n Name extends string,\n Schema extends StandardSchemaV1,\n>(\n name: Name,\n config: { description: string; schema: Schema },\n): ClientToolDef<Name, InferSchemaOutput<Schema>>;\nexport function clientTool<Name extends string, Args>(\n name: Name,\n config: { description: string; args: Args },\n): ClientToolDef<Name, Args>;\nexport function clientTool<Name extends string>(\n name: Name,\n config:\n | { description: string; schema: StandardSchemaV1 }\n | { description: string; args: unknown },\n): ClientToolDef<Name> {\n const tool: ClientToolDef<Name> = {\n type: 'client_event',\n name,\n description: config.description,\n };\n\n if ('schema' in config) {\n toolSchemas.set(tool, config.schema);\n }\n\n return tool;\n}\n","export type AvatarErrorCode =\n | 'CONSUME_FAILED'\n | 'CONNECTION_FAILED'\n | 'MEDIA_PERMISSION_DENIED'\n | 'MEDIA_DEVICE_ERROR'\n | 'SCREEN_SHARE_FAILED'\n | 'PUBLISH_FAILED'\n | 'UNKNOWN';\n\nexport class AvatarError extends Error {\n readonly code: AvatarErrorCode;\n readonly cause?: Error;\n\n constructor(code: AvatarErrorCode, message: string, cause?: Error) {\n super(message);\n this.name = 'AvatarError';\n this.code = code;\n this.cause = cause;\n }\n}\n\nexport function toAvatarError(\n code: AvatarErrorCode,\n fallbackMessage: string,\n err: unknown,\n): AvatarError {\n if (err instanceof AvatarError) return err;\n const cause = err instanceof Error ? err : undefined;\n const message = cause?.message ?? fallbackMessage;\n return new AvatarError(code, message, cause);\n}\n","export const DEFAULT_BASE_URL = 'https://api.dev.runwayml.com';\n","import { AvatarError } from '../error';\nimport type { ConsumeSessionOptions, ConsumeSessionResponse } from '../types';\nimport { DEFAULT_BASE_URL } from './config';\n\nexport async function consumeSession(\n options: ConsumeSessionOptions,\n): Promise<ConsumeSessionResponse> {\n const { sessionId, sessionKey, baseUrl = DEFAULT_BASE_URL } = options;\n\n const url = `${baseUrl}/v1/realtime_sessions/${sessionId}/consume`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${sessionKey}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new AvatarError(\n 'CONSUME_FAILED',\n `Failed to consume session: ${response.status} ${errorText}`,\n );\n }\n\n return response.json();\n}\n","import { clientTool, type ClientEventsFrom } from '../tools';\n\nexport const clickTool = clientTool('click', {\n description: 'Clicks an interactive element on the page by its target ID',\n args: {} as { target: string },\n});\n\nexport const scrollToTool = clientTool('scroll_to', {\n description: 'Scrolls the page to an element by its target ID',\n args: {} as { target: string },\n});\n\nexport const highlightTool = clientTool('highlight', {\n description:\n 'Highlights an element on the page to draw attention to it by its target ID',\n args: {} as { target: string; duration?: number },\n});\n\nconst toolDefs = [clickTool, scrollToTool, highlightTool] as const;\n\nexport type PageActionEvent = ClientEventsFrom<typeof toolDefs>;\n\n/**\n * Pre-built tool definitions with `parameters` arrays, ready to spread\n * into `realtimeSessions.create({ tools })`.\n *\n * @example\n * ```ts\n * import { pageActionTools } from '@runwayml/avatars-react/api';\n *\n * await client.realtimeSessions.create({\n * model: 'gwm1_avatars',\n * avatar: { type: 'runway-preset', presetId: 'music-superstar' },\n * tools: [...pageActionTools, ...myCustomTools],\n * });\n * ```\n */\nexport const pageActionTools = [\n {\n ...clickTool,\n parameters: [\n {\n name: 'target',\n type: 'string',\n description:\n 'The ID or data-avatar-target value of the element to click',\n },\n ],\n },\n {\n ...scrollToTool,\n parameters: [\n {\n name: 'target',\n type: 'string',\n description:\n 'The ID or data-avatar-target value of the element to scroll to',\n },\n ],\n },\n {\n ...highlightTool,\n parameters: [\n {\n name: 'target',\n type: 'string',\n description:\n 'The ID or data-avatar-target value of the element to highlight',\n },\n {\n name: 'duration',\n type: 'number',\n description: 'How long to highlight in milliseconds. Defaults to 2000',\n },\n ],\n },\n];\n","import { AvatarError } from '../error';\n\nexport interface PollUntilReadyOptions {\n sessionId: string;\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n intervalMs?: number;\n}\n\ninterface SessionResponse {\n id: string;\n status: string;\n sessionKey?: string;\n [key: string]: unknown;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.dev.runwayml.com';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_INTERVAL_MS = 1_000;\nconst TERMINAL_STATUSES = ['COMPLETED', 'FAILED', 'CANCELLED'];\n\n/**\n * Poll a realtime session until it reaches READY status.\n *\n * Returns `{ sessionId, sessionKey }` — ready to pass to `streamTo` or `connect`.\n *\n * This is a server-side utility (requires your API key). Use it in\n * Next.js API routes, Express handlers, etc.\n *\n * @example\n * ```ts\n * import Runway from '@runwayml/sdk';\n * import { pollUntilReady } from '@runwayml/avatars/api';\n *\n * const runway = new Runway();\n * const { id: sessionId } = await runway.realtimeSessions.create({ ... });\n * const { sessionKey } = await pollUntilReady({ sessionId, apiKey: process.env.RUNWAYML_API_SECRET });\n *\n * return Response.json({ sessionId, sessionKey });\n * ```\n */\nexport async function pollUntilReady(\n options: PollUntilReadyOptions,\n): Promise<{ sessionId: string; sessionKey: string }> {\n const {\n sessionId,\n apiKey,\n baseUrl = DEFAULT_BASE_URL,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n intervalMs = DEFAULT_INTERVAL_MS,\n } = options;\n\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const url = `${baseUrl}/v1/realtime_sessions/${sessionId}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'X-Runway-Version': '2024-11-06',\n },\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new AvatarError(\n 'CONSUME_FAILED',\n `Failed to retrieve session: ${response.status} ${text}`,\n );\n }\n\n const session: SessionResponse = await response.json();\n\n if (session.status === 'READY') {\n if (!session.sessionKey) {\n throw new AvatarError(\n 'CONSUME_FAILED',\n 'Session is READY but sessionKey is missing',\n );\n }\n return { sessionId, sessionKey: session.sessionKey };\n }\n\n if (TERMINAL_STATUSES.includes(session.status)) {\n throw new AvatarError(\n 'CONNECTION_FAILED',\n `Session ${session.status.toLowerCase()} before becoming ready`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n\n throw new AvatarError('CONNECTION_FAILED', 'Session creation timed out');\n}\n"]}
package/dist/api.d.cts ADDED
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Standard Schema v1 — a tiny cross-library validation interface implemented
3
+ * by Zod, Valibot, ArkType, and others. Anything matching this shape plugs
4
+ * into `clientTool({ schema })`.
5
+ *
6
+ * @see https://standardschema.dev/
7
+ *
8
+ * We re-declare the spec here (instead of depending on `@standard-schema/spec`)
9
+ * so the SDK stays dependency-free and server-safe.
10
+ */
11
+ /** The schema itself — what a `z.object(...)` etc. resolves to. */
12
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
13
+ readonly '~standard': {
14
+ readonly version: 1;
15
+ readonly vendor: string;
16
+ readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
17
+ readonly types?: {
18
+ readonly input: Input;
19
+ readonly output: Output;
20
+ };
21
+ };
22
+ }
23
+ /** Result of calling `schema['~standard'].validate(value)`. */
24
+ type StandardSchemaResult<Output> = {
25
+ readonly value: Output;
26
+ readonly issues?: undefined;
27
+ } | {
28
+ readonly issues: ReadonlyArray<StandardSchemaIssue>;
29
+ };
30
+ /** A single validation failure — part of the error path. */
31
+ interface StandardSchemaIssue {
32
+ readonly message: string;
33
+ readonly path?: ReadonlyArray<PropertyKey | {
34
+ readonly key: PropertyKey;
35
+ }>;
36
+ }
37
+ type InferSchemaInput<Schema extends StandardSchemaV1> = Schema extends StandardSchemaV1<infer Input, unknown> ? Input : never;
38
+ type InferSchemaOutput<Schema extends StandardSchemaV1> = Schema extends StandardSchemaV1<unknown, infer Output> ? Output : never;
39
+
40
+ interface ConsumeSessionResponse {
41
+ url: string;
42
+ token: string;
43
+ roomName: string;
44
+ }
45
+ interface ConsumeSessionOptions {
46
+ sessionId: string;
47
+ sessionKey: string;
48
+ baseUrl?: string;
49
+ }
50
+ interface ClientEvent<T extends string = string, A = Record<string, unknown>> {
51
+ type: 'client_event';
52
+ tool: T;
53
+ args: A;
54
+ }
55
+
56
+ /**
57
+ * A standalone client tool definition. Composable — combine into arrays
58
+ * and derive event types with `ClientEventsFrom`.
59
+ *
60
+ * At runtime this is just `{ type, name, description }` (exactly what the
61
+ * Runway session create payload expects). The `Args` generic is phantom —
62
+ * it only exists at the TypeScript level for type narrowing.
63
+ *
64
+ * When the tool is defined with a `schema` ([Standard Schema](https://standardschema.dev/)),
65
+ * the schema is attached internally (not serialized) for runtime validation
66
+ * and to infer `Args` from the schema output type.
67
+ */
68
+ interface ClientToolDef<Name extends string = string, Args = unknown> {
69
+ readonly type: 'client_event';
70
+ readonly name: Name;
71
+ readonly description: string;
72
+ /** @internal phantom field — always `undefined` at runtime */
73
+ readonly _args?: Args;
74
+ }
75
+ type ClientToolArgs<Tool extends ClientToolDef> = Tool extends ClientToolDef<string, infer Args> ? Args : never;
76
+ type ClientEventFromTool<Tool extends ClientToolDef> = Tool extends ClientToolDef<infer Name, infer Args> ? ClientEvent<Name, Args> : never;
77
+ /**
78
+ * Derive a discriminated union of ClientEvent types from an array of tools.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const tools = [showQuestion, playSound];
83
+ * type MyEvent = ClientEventsFrom<typeof tools>;
84
+ * ```
85
+ */
86
+ type ClientEventsFrom<T extends ReadonlyArray<ClientToolDef>> = T[number] extends infer U ? U extends ClientToolDef<infer Name, infer Args> ? ClientEvent<Name, Args> : never : never;
87
+ /**
88
+ * Return the Standard Schema associated with a tool, if any.
89
+ * Useful when composing validation outside of the SDK hooks.
90
+ */
91
+ declare function getClientToolSchema(tool: ClientToolDef): StandardSchemaV1 | undefined;
92
+ /**
93
+ * Validate parsed client event args against a tool's schema.
94
+ *
95
+ * Returns the typed args on success, or `null` when the schema fails or
96
+ * when the schema would need to resolve asynchronously (Standard Schema
97
+ * allows async `validate`, but for fire-and-forget client events we only
98
+ * accept sync results here).
99
+ *
100
+ * Tools defined without a schema always validate successfully — the
101
+ * incoming args are returned as-is.
102
+ */
103
+ declare function validateClientToolArgs<Tool extends ClientToolDef>(tool: Tool, args: unknown): ClientToolArgs<Tool> | null;
104
+ /**
105
+ * Define a single client tool.
106
+ *
107
+ * Returns a standalone object that can be composed into arrays and passed
108
+ * to `realtimeSessions.create({ tools })`.
109
+ *
110
+ * Two forms are supported:
111
+ *
112
+ * 1. **Schema-driven** (recommended) — pass a
113
+ * [Standard Schema](https://standardschema.dev/) (Zod, Valibot, ArkType, …)
114
+ * to infer `args` types and enable runtime validation of incoming events:
115
+ *
116
+ * ```typescript
117
+ * import { z } from 'zod';
118
+ * const showCaption = clientTool('show_caption', {
119
+ * description: 'Display a caption',
120
+ * schema: z.object({ text: z.string() }),
121
+ * });
122
+ * ```
123
+ *
124
+ * 2. **Type-only** — pass an `args` type via a cast when you don't need
125
+ * runtime validation:
126
+ *
127
+ * ```typescript
128
+ * const showCaption = clientTool('show_caption', {
129
+ * description: 'Display a caption',
130
+ * args: {} as { text: string },
131
+ * });
132
+ * ```
133
+ *
134
+ * Combine tools and derive event types with `ClientEventsFrom`:
135
+ *
136
+ * ```typescript
137
+ * const tools = [showCaption, playSound];
138
+ * type MyEvent = ClientEventsFrom<typeof tools>;
139
+ * ```
140
+ */
141
+ declare function clientTool<Name extends string, Schema extends StandardSchemaV1>(name: Name, config: {
142
+ description: string;
143
+ schema: Schema;
144
+ }): ClientToolDef<Name, InferSchemaOutput<Schema>>;
145
+ declare function clientTool<Name extends string, Args>(name: Name, config: {
146
+ description: string;
147
+ args: Args;
148
+ }): ClientToolDef<Name, Args>;
149
+
150
+ declare function consumeSession(options: ConsumeSessionOptions): Promise<ConsumeSessionResponse>;
151
+
152
+ declare const toolDefs: readonly [ClientToolDef<"click", {
153
+ target: string;
154
+ }>, ClientToolDef<"scroll_to", {
155
+ target: string;
156
+ }>, ClientToolDef<"highlight", {
157
+ target: string;
158
+ duration?: number;
159
+ }>];
160
+ type PageActionEvent = ClientEventsFrom<typeof toolDefs>;
161
+ /**
162
+ * Pre-built tool definitions with `parameters` arrays, ready to spread
163
+ * into `realtimeSessions.create({ tools })`.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * import { pageActionTools } from '@runwayml/avatars-react/api';
168
+ *
169
+ * await client.realtimeSessions.create({
170
+ * model: 'gwm1_avatars',
171
+ * avatar: { type: 'runway-preset', presetId: 'music-superstar' },
172
+ * tools: [...pageActionTools, ...myCustomTools],
173
+ * });
174
+ * ```
175
+ */
176
+ declare const pageActionTools: ({
177
+ parameters: {
178
+ name: string;
179
+ type: string;
180
+ description: string;
181
+ }[];
182
+ type: "client_event";
183
+ name: "click";
184
+ description: string;
185
+ _args?: {
186
+ target: string;
187
+ } | undefined;
188
+ } | {
189
+ parameters: {
190
+ name: string;
191
+ type: string;
192
+ description: string;
193
+ }[];
194
+ type: "client_event";
195
+ name: "scroll_to";
196
+ description: string;
197
+ _args?: {
198
+ target: string;
199
+ } | undefined;
200
+ } | {
201
+ parameters: {
202
+ name: string;
203
+ type: string;
204
+ description: string;
205
+ }[];
206
+ type: "client_event";
207
+ name: "highlight";
208
+ description: string;
209
+ _args?: {
210
+ target: string;
211
+ duration?: number;
212
+ } | undefined;
213
+ })[];
214
+
215
+ interface PollUntilReadyOptions {
216
+ sessionId: string;
217
+ apiKey: string;
218
+ baseUrl?: string;
219
+ timeoutMs?: number;
220
+ intervalMs?: number;
221
+ }
222
+ /**
223
+ * Poll a realtime session until it reaches READY status.
224
+ *
225
+ * Returns `{ sessionId, sessionKey }` — ready to pass to `streamTo` or `connect`.
226
+ *
227
+ * This is a server-side utility (requires your API key). Use it in
228
+ * Next.js API routes, Express handlers, etc.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * import Runway from '@runwayml/sdk';
233
+ * import { pollUntilReady } from '@runwayml/avatars/api';
234
+ *
235
+ * const runway = new Runway();
236
+ * const { id: sessionId } = await runway.realtimeSessions.create({ ... });
237
+ * const { sessionKey } = await pollUntilReady({ sessionId, apiKey: process.env.RUNWAYML_API_SECRET });
238
+ *
239
+ * return Response.json({ sessionId, sessionKey });
240
+ * ```
241
+ */
242
+ declare function pollUntilReady(options: PollUntilReadyOptions): Promise<{
243
+ sessionId: string;
244
+ sessionKey: string;
245
+ }>;
246
+
247
+ export { type ClientEvent, type ClientEventFromTool, type ClientEventsFrom, type ClientToolArgs, type ClientToolDef, type ConsumeSessionOptions, type ConsumeSessionResponse, type InferSchemaInput, type InferSchemaOutput, type PageActionEvent, type PollUntilReadyOptions, type StandardSchemaIssue, type StandardSchemaResult, type StandardSchemaV1, clientTool, consumeSession, getClientToolSchema, pageActionTools, pollUntilReady, validateClientToolArgs };