realtime-avatar 0.7.2 → 0.7.3

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.
@@ -43,7 +43,7 @@ function isTransient(cause) {
43
43
 
44
44
  // ../http-client/src/client.ts
45
45
  var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
46
- var SDK_VERSION = "0.7.2";
46
+ var SDK_VERSION = "0.7.3";
47
47
  var RealtimeAvatar = class {
48
48
  #apiKey;
49
49
  #baseUrl;
@@ -1,4 +1,4 @@
1
- import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-ARILC2U3.js';
1
+ import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-2V333KRZ.js';
2
2
 
3
3
  // ../proxy/src/config.ts
4
4
  var json = (body, status = 200) => new Response(JSON.stringify(body), {
package/dist/express.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-MQQKAUOB.js';
2
- import './chunk-ARILC2U3.js';
1
+ import { createProxyHandler } from './chunk-OMOEUVO5.js';
2
+ import './chunk-2V333KRZ.js';
3
3
 
4
4
  // ../proxy/src/express.ts
5
5
  function realtimeAvatarExpress(config) {
package/dist/hono.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-MQQKAUOB.js';
2
- import './chunk-ARILC2U3.js';
1
+ import { createProxyHandler } from './chunk-OMOEUVO5.js';
2
+ import './chunk-2V333KRZ.js';
3
3
 
4
4
  // ../proxy/src/hono.ts
5
5
  function realtimeAvatarHono(config) {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-ARILC2U3.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-2V333KRZ.js';
package/dist/nextjs.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-MQQKAUOB.js';
2
- import './chunk-ARILC2U3.js';
1
+ import { createProxyHandler } from './chunk-OMOEUVO5.js';
2
+ import './chunk-2V333KRZ.js';
3
3
 
4
4
  // ../proxy/src/nextjs.ts
5
5
  function createRealtimeAvatarRoute(config) {
@@ -131,6 +131,43 @@ function timeoutSetter(fn, ms) {
131
131
  }
132
132
 
133
133
  // ../client/src/errors.ts
134
+ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
135
+ constructor(message, status, body, response, metadata = {}) {
136
+ super(message);
137
+ this.status = status;
138
+ this.body = body;
139
+ this.response = response;
140
+ this.name = "RealtimeAvatarApiError";
141
+ this.code = metadata.code;
142
+ this.retryable = metadata.retryable;
143
+ this.billingUrl = metadata.billingUrl;
144
+ this.rawMessage = metadata.rawMessage ?? null;
145
+ }
146
+ status;
147
+ body;
148
+ response;
149
+ code;
150
+ retryable;
151
+ billingUrl;
152
+ /** Debug detail returned by the API. Do not render directly in user-facing UI. */
153
+ rawMessage;
154
+ get isBillingRequired() {
155
+ return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
156
+ }
157
+ static async fromResponse(response) {
158
+ const contentType = response.headers.get("content-type") ?? "";
159
+ const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
160
+ const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
161
+ const metadata = extractErrorMetadata(body, response.status, rawMessage);
162
+ return new _RealtimeAvatarApiError(
163
+ metadata.message,
164
+ response.status,
165
+ body,
166
+ response,
167
+ metadata
168
+ );
169
+ }
170
+ };
134
171
  var RealtimeAvatarCapacityError = class extends Error {
135
172
  constructor(message, busy) {
136
173
  super(message);
@@ -151,6 +188,107 @@ var RealtimeAvatarCapacityError = class extends Error {
151
188
  return this.busy.recommended_retry_ms;
152
189
  }
153
190
  };
191
+ function extractErrorMessage(body) {
192
+ if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
193
+ const record = body;
194
+ const detail = readMessageValue(record.detail);
195
+ if (detail) return detail;
196
+ const error = readMessageValue(record.error);
197
+ if (error) return error;
198
+ const message = readMessageValue(record.message);
199
+ if (message) return message;
200
+ return null;
201
+ }
202
+ function normalizeRealtimeAvatarError(input) {
203
+ const status = input.status ?? 0;
204
+ const rawCode = input.code?.trim() || null;
205
+ const rawMessage = input.message?.trim() || "";
206
+ const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
207
+ if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
208
+ return {
209
+ code: rawCode ?? "billing_required",
210
+ message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
211
+ retryable: false
212
+ };
213
+ }
214
+ if (status === 401) {
215
+ return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
216
+ }
217
+ if (status === 403) {
218
+ return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
219
+ }
220
+ if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
221
+ return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
222
+ }
223
+ if (status === 409) {
224
+ return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
225
+ }
226
+ if (status === 429) {
227
+ return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
228
+ }
229
+ if (isWarmingMessage(blob)) {
230
+ return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
231
+ }
232
+ if (status === 400 || status === 422) {
233
+ return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
234
+ }
235
+ if (status >= 500) {
236
+ return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
237
+ }
238
+ return {
239
+ code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
240
+ message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
241
+ retryable: status === 0 || status >= 500
242
+ };
243
+ }
244
+ function extractErrorMetadata(body, status, rawMessage) {
245
+ if (!body || typeof body !== "object") {
246
+ const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
247
+ return { ...normalized2, rawMessage };
248
+ }
249
+ const record = body;
250
+ const code = typeof record.code === "string" ? record.code : null;
251
+ const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
252
+ return {
253
+ code: normalized.code,
254
+ retryable: typeof record.retryable === "boolean" ? record.retryable : normalized.retryable,
255
+ ...typeof record.billingUrl === "string" ? { billingUrl: record.billingUrl } : {},
256
+ rawMessage,
257
+ message: normalized.message
258
+ };
259
+ }
260
+ var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
261
+ "billing_required",
262
+ "invalid_request",
263
+ "unauthorized",
264
+ "forbidden",
265
+ "not_found",
266
+ "conflict",
267
+ "rate_limited",
268
+ "service_unavailable",
269
+ "request_failed",
270
+ "network_error"
271
+ ]);
272
+ function isPublicErrorCode(code) {
273
+ return Boolean(code && PUBLIC_ERROR_CODES.has(code));
274
+ }
275
+ function readMessageValue(value) {
276
+ if (typeof value === "string") return value.trim() || null;
277
+ if (!value || typeof value !== "object") return null;
278
+ const record = value;
279
+ return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
280
+ }
281
+ function userSafeMessage(message) {
282
+ if (!message || hasInternalDetail(message)) return null;
283
+ return message.length > 180 ? `${message.slice(0, 177)}...` : message;
284
+ }
285
+ function isWarmingMessage(blob) {
286
+ return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
287
+ }
288
+ var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
289
+ function hasInternalDetail(message) {
290
+ return MACHINE_SHAPED.test(message);
291
+ }
154
292
  function splitCallTranscript(all, localIdentity) {
155
293
  if (!localIdentity) return { user: [], agent: all };
156
294
  const user = [];
@@ -2390,8 +2528,7 @@ function createProxyClient(options) {
2390
2528
  return { status: "busy", busy };
2391
2529
  }
2392
2530
  if (!response.ok) {
2393
- const detail = await response.text().catch(() => "");
2394
- throw new Error(`realtime-avatar: proxy refused the call (${response.status}) ${detail}`.trim());
2531
+ throw await RealtimeAvatarApiError.fromResponse(response);
2395
2532
  }
2396
2533
  return { status: "ready", grant: await response.json() };
2397
2534
  },
package/dist/react.js CHANGED
@@ -90,6 +90,43 @@ function timeoutSetter(fn, ms) {
90
90
  }
91
91
 
92
92
  // ../client/src/errors.ts
93
+ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
94
+ constructor(message, status, body, response, metadata = {}) {
95
+ super(message);
96
+ this.status = status;
97
+ this.body = body;
98
+ this.response = response;
99
+ this.name = "RealtimeAvatarApiError";
100
+ this.code = metadata.code;
101
+ this.retryable = metadata.retryable;
102
+ this.billingUrl = metadata.billingUrl;
103
+ this.rawMessage = metadata.rawMessage ?? null;
104
+ }
105
+ status;
106
+ body;
107
+ response;
108
+ code;
109
+ retryable;
110
+ billingUrl;
111
+ /** Debug detail returned by the API. Do not render directly in user-facing UI. */
112
+ rawMessage;
113
+ get isBillingRequired() {
114
+ return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
115
+ }
116
+ static async fromResponse(response) {
117
+ const contentType = response.headers.get("content-type") ?? "";
118
+ const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
119
+ const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
120
+ const metadata = extractErrorMetadata(body, response.status, rawMessage);
121
+ return new _RealtimeAvatarApiError(
122
+ metadata.message,
123
+ response.status,
124
+ body,
125
+ response,
126
+ metadata
127
+ );
128
+ }
129
+ };
93
130
  var RealtimeAvatarCapacityError = class extends Error {
94
131
  constructor(message, busy) {
95
132
  super(message);
@@ -110,6 +147,107 @@ var RealtimeAvatarCapacityError = class extends Error {
110
147
  return this.busy.recommended_retry_ms;
111
148
  }
112
149
  };
150
+ function extractErrorMessage(body) {
151
+ if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
152
+ const record = body;
153
+ const detail = readMessageValue(record.detail);
154
+ if (detail) return detail;
155
+ const error = readMessageValue(record.error);
156
+ if (error) return error;
157
+ const message = readMessageValue(record.message);
158
+ if (message) return message;
159
+ return null;
160
+ }
161
+ function normalizeRealtimeAvatarError(input) {
162
+ const status = input.status ?? 0;
163
+ const rawCode = input.code?.trim() || null;
164
+ const rawMessage = input.message?.trim() || "";
165
+ const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
166
+ if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
167
+ return {
168
+ code: rawCode ?? "billing_required",
169
+ message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
170
+ retryable: false
171
+ };
172
+ }
173
+ if (status === 401) {
174
+ return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
175
+ }
176
+ if (status === 403) {
177
+ return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
178
+ }
179
+ if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
180
+ return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
181
+ }
182
+ if (status === 409) {
183
+ return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
184
+ }
185
+ if (status === 429) {
186
+ return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
187
+ }
188
+ if (isWarmingMessage(blob)) {
189
+ return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
190
+ }
191
+ if (status === 400 || status === 422) {
192
+ return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
193
+ }
194
+ if (status >= 500) {
195
+ return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
196
+ }
197
+ return {
198
+ code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
199
+ message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
200
+ retryable: status === 0 || status >= 500
201
+ };
202
+ }
203
+ function extractErrorMetadata(body, status, rawMessage) {
204
+ if (!body || typeof body !== "object") {
205
+ const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
206
+ return { ...normalized2, rawMessage };
207
+ }
208
+ const record = body;
209
+ const code = typeof record.code === "string" ? record.code : null;
210
+ const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
211
+ return {
212
+ code: normalized.code,
213
+ retryable: typeof record.retryable === "boolean" ? record.retryable : normalized.retryable,
214
+ ...typeof record.billingUrl === "string" ? { billingUrl: record.billingUrl } : {},
215
+ rawMessage,
216
+ message: normalized.message
217
+ };
218
+ }
219
+ var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
220
+ "billing_required",
221
+ "invalid_request",
222
+ "unauthorized",
223
+ "forbidden",
224
+ "not_found",
225
+ "conflict",
226
+ "rate_limited",
227
+ "service_unavailable",
228
+ "request_failed",
229
+ "network_error"
230
+ ]);
231
+ function isPublicErrorCode(code) {
232
+ return Boolean(code && PUBLIC_ERROR_CODES.has(code));
233
+ }
234
+ function readMessageValue(value) {
235
+ if (typeof value === "string") return value.trim() || null;
236
+ if (!value || typeof value !== "object") return null;
237
+ const record = value;
238
+ return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
239
+ }
240
+ function userSafeMessage(message) {
241
+ if (!message || hasInternalDetail(message)) return null;
242
+ return message.length > 180 ? `${message.slice(0, 177)}...` : message;
243
+ }
244
+ function isWarmingMessage(blob) {
245
+ return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
246
+ }
247
+ var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
248
+ function hasInternalDetail(message) {
249
+ return MACHINE_SHAPED.test(message);
250
+ }
113
251
  function splitCallTranscript(all, localIdentity) {
114
252
  if (!localIdentity) return { user: [], agent: all };
115
253
  const user = [];
@@ -2695,8 +2833,7 @@ function createProxyClient(options) {
2695
2833
  return { status: "busy", busy };
2696
2834
  }
2697
2835
  if (!response.ok) {
2698
- const detail = await response.text().catch(() => "");
2699
- throw new Error(`realtime-avatar: proxy refused the call (${response.status}) ${detail}`.trim());
2836
+ throw await RealtimeAvatarApiError.fromResponse(response);
2700
2837
  }
2701
2838
  return { status: "ready", grant: await response.json() };
2702
2839
  },
package/dist/server.js CHANGED
@@ -1 +1 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-ARILC2U3.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-2V333KRZ.js';
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-MQQKAUOB.js';
2
- import './chunk-ARILC2U3.js';
1
+ import { createProxyHandler } from './chunk-OMOEUVO5.js';
2
+ import './chunk-2V333KRZ.js';
3
3
 
4
4
  // src/tanstack-start.ts
5
5
  function realtimeAvatarServerRoute(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtime-avatar",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Realtime Avatar SDK. `realtime-avatar` and the route adapters hold your API key and are server-only; `realtime-avatar/react`, `/browser` and `/tools` never can — importing a server entry into a browser build throws at bundle time.",
5
5
  "license": "MIT",
6
6
  "author": "The Influence Company",