@runtypelabs/persona 4.32.1 → 4.32.2
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/dist/{chunk-C9uOhfk5.js → chunk-B9Zu-wrk.js} +1 -1
- package/dist/chunk-BNE40cEG.js +1 -0
- package/dist/{chunk-DM-OJt5q.js → chunk-CjkechxR.js} +1 -1
- package/dist/{chunk-CumoXK3u.js → chunk-D0tcIZJM.js} +1 -1
- package/dist/client-dispatch.js +1 -1
- package/dist/client-history.js +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/launcher.global.js +1 -1
- package/dist/launcher.global.js.map +1 -1
- package/dist/theme-editor-preview.cjs +2 -2
- package/dist/theme-editor-preview.js +2 -2
- package/dist/voice-runtime.cjs +1 -1
- package/dist/voice-runtime.js +1 -1
- package/package.json +1 -1
- package/src/index-global.ts +2 -1
- package/src/install.test.ts +6 -5
- package/src/install.ts +2 -2
- package/src/launcher-global.ts +2 -1
- package/src/session.test.ts +41 -0
- package/src/session.ts +14 -7
- package/src/utils/cdn-release.test.ts +20 -0
- package/src/utils/cdn-release.ts +14 -0
- package/dist/chunk-BkOYJ11B.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "4.32.
|
|
3
|
+
"version": "4.32.2",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
package/src/index-global.ts
CHANGED
|
@@ -37,12 +37,13 @@ export type { StreamAnimationPlugin, StreamAnimationContext } from "./types";
|
|
|
37
37
|
// ---------------------------------------------------------------------------
|
|
38
38
|
|
|
39
39
|
import { setWebMcpPolyfillLoader } from "./webmcp-bridge";
|
|
40
|
+
import { pinCdnRelease } from "./utils/cdn-release";
|
|
40
41
|
|
|
41
42
|
// Capture at module-evaluation time: `document.currentScript` is null once
|
|
42
43
|
// execution leaves the script's initial synchronous run.
|
|
43
44
|
const widgetScriptSrc: string | null =
|
|
44
45
|
typeof document !== "undefined"
|
|
45
|
-
? ((document.currentScript as HTMLScriptElement | null)?.src ?? null
|
|
46
|
+
? pinCdnRelease((document.currentScript as HTMLScriptElement | null)?.src ?? "") || null
|
|
46
47
|
: null;
|
|
47
48
|
|
|
48
49
|
/**
|
package/src/install.test.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// `vi.runAllTimersAsync()` to drive it forward deterministically.
|
|
11
11
|
|
|
12
12
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
13
|
+
import { version } from "../package.json";
|
|
13
14
|
|
|
14
15
|
const PERSONA_EVENTS = [
|
|
15
16
|
"persona:script-load",
|
|
@@ -464,11 +465,11 @@ describe("install.ts: CDN base derivation", () => {
|
|
|
464
465
|
return { cssHref: css?.href ?? null, scripts };
|
|
465
466
|
}
|
|
466
467
|
|
|
467
|
-
it("
|
|
468
|
+
it("pins first-party latest installer assets to the embedded release", async () => {
|
|
468
469
|
setCurrentScript("https://cdn.runtype.com/persona/latest/install.global.js");
|
|
469
470
|
const { cssHref, scripts } = await installedUrls({});
|
|
470
|
-
expect(cssHref).toBe(
|
|
471
|
-
expect(scripts).toContain(
|
|
471
|
+
expect(cssHref).toBe(`https://cdn.runtype.com/persona/${version}/widget.css`);
|
|
472
|
+
expect(scripts).toContain(`https://cdn.runtype.com/persona/${version}/launcher.global.js`);
|
|
472
473
|
});
|
|
473
474
|
|
|
474
475
|
it("keeps npm-CDN siblings on the installer's own pinned range (no @latest skew)", async () => {
|
|
@@ -478,13 +479,13 @@ describe("install.ts: CDN base derivation", () => {
|
|
|
478
479
|
expect(scripts).toContain("https://cdn.jsdelivr.net/npm/@runtypelabs/persona@4/dist/launcher.global.js");
|
|
479
480
|
});
|
|
480
481
|
|
|
481
|
-
it("the full bundle
|
|
482
|
+
it("pins the full bundle on the eager first-party latest path", async () => {
|
|
482
483
|
setCurrentScript("https://cdn.runtype.com/persona/latest/install.global.js");
|
|
483
484
|
markCssLoaded();
|
|
484
485
|
await install({ config: { apiUrl: "/api", launcher: { enabled: false } } });
|
|
485
486
|
await flush();
|
|
486
487
|
const scripts = Array.from(document.head.querySelectorAll("script")).map((s) => s.src);
|
|
487
|
-
expect(scripts).toContain(
|
|
488
|
+
expect(scripts).toContain(`https://cdn.runtype.com/persona/${version}/index.global.js`);
|
|
488
489
|
});
|
|
489
490
|
|
|
490
491
|
it("falls back to jsDelivr@latest when currentScript is unavailable", async () => {
|
package/src/install.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* if configuration is provided via window.siteAgentConfig
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { pinCdnRelease } from "./utils/cdn-release";
|
|
8
8
|
|
|
9
9
|
import type { BrandConfig } from "./types/theme";
|
|
10
10
|
import { delayInstallLoading } from "./utils/install-loading";
|
|
@@ -201,7 +201,7 @@ declare global {
|
|
|
201
201
|
const getInstallerBase = (): string | null => {
|
|
202
202
|
if (!installerSrc) return null;
|
|
203
203
|
try {
|
|
204
|
-
const url = new URL(installerSrc, document.baseURI);
|
|
204
|
+
const url = new URL(pinCdnRelease(installerSrc), document.baseURI);
|
|
205
205
|
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
206
206
|
return url.origin + url.pathname.slice(0, url.pathname.lastIndexOf("/"));
|
|
207
207
|
} catch {
|
package/src/launcher-global.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { createLauncherSurface } from "./components/launcher";
|
|
|
24
24
|
import { applyLauncherThemeVariables } from "./utils/theme";
|
|
25
25
|
import { DEFAULT_LAUNCHER_CONFIG, DEFAULT_LAUNCHER_V5 } from "./defaults";
|
|
26
26
|
import { setIconsExtraLoader } from "./icons-extra-loader";
|
|
27
|
+
import { pinCdnRelease } from "./utils/cdn-release";
|
|
27
28
|
import type { AgentWidgetConfig } from "./types";
|
|
28
29
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
@@ -33,7 +34,7 @@ import type { AgentWidgetConfig } from "./types";
|
|
|
33
34
|
// ---------------------------------------------------------------------------
|
|
34
35
|
const launcherScriptSrc: string | null =
|
|
35
36
|
typeof document !== "undefined"
|
|
36
|
-
? ((document.currentScript as HTMLScriptElement | null)?.src ?? null
|
|
37
|
+
? pinCdnRelease((document.currentScript as HTMLScriptElement | null)?.src ?? "") || null
|
|
37
38
|
: null;
|
|
38
39
|
|
|
39
40
|
setIconsExtraLoader(() => {
|
package/src/session.test.ts
CHANGED
|
@@ -1070,6 +1070,47 @@ describe('AgentWidgetSession - dispatch error fallback', () => {
|
|
|
1070
1070
|
expect(session.getStatus()).toBe('idle');
|
|
1071
1071
|
});
|
|
1072
1072
|
|
|
1073
|
+
it('does not blame the backend for an internal stream TypeError after a successful response', async () => {
|
|
1074
|
+
const response = new Response('data: {}\n\n', { headers: { 'Content-Type': 'text/event-stream' } });
|
|
1075
|
+
const fault = new TypeError('t.streamGeneration is not a function');
|
|
1076
|
+
vi.spyOn(response.body!, 'getReader').mockImplementation(() => { throw fault; });
|
|
1077
|
+
global.fetch = vi.fn().mockResolvedValue(response);
|
|
1078
|
+
const errors: Error[] = [];
|
|
1079
|
+
const session = new AgentWidgetSession({ apiUrl: 'http://example.invalid/chat' }, {
|
|
1080
|
+
onMessagesChanged: () => {}, onStatusChanged: () => {}, onStreamingChanged: () => {},
|
|
1081
|
+
onError: error => errors.push(error),
|
|
1082
|
+
});
|
|
1083
|
+
await session.sendMessage('Hello');
|
|
1084
|
+
const content = lastAssistantMessage(session)?.content;
|
|
1085
|
+
expect(content).toContain('the chat encountered an unexpected error');
|
|
1086
|
+
expect(content).toContain('t.streamGeneration is not a function');
|
|
1087
|
+
expect(content).not.toContain('backend');
|
|
1088
|
+
expect(content).not.toContain("couldn't reach");
|
|
1089
|
+
expect(errors).toContain(fault);
|
|
1090
|
+
expect(session.isStreaming()).toBe(false);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
it.each(['Failed to fetch', 'Load failed', 'NetworkError when attempting to fetch resource.', 'fetch failed'])(
|
|
1094
|
+
'recognizes the browser fetch TypeError: %s', async (message) => {
|
|
1095
|
+
failFetchWith(new TypeError(message));
|
|
1096
|
+
const session = new AgentWidgetSession({ apiUrl: 'http://example.invalid/chat' }, {
|
|
1097
|
+
onMessagesChanged: () => {}, onStatusChanged: () => {}, onStreamingChanged: () => {},
|
|
1098
|
+
});
|
|
1099
|
+
await session.sendMessage('Hello');
|
|
1100
|
+
expect(lastAssistantMessage(session)?.content).toContain("I couldn't reach the assistant");
|
|
1101
|
+
},
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
it('describes a server rejection without claiming the server was unreachable', async () => {
|
|
1105
|
+
global.fetch = vi.fn().mockResolvedValue(new Response('unavailable', { status: 503 }));
|
|
1106
|
+
const session = new AgentWidgetSession({ apiUrl: 'http://example.invalid/chat' }, {
|
|
1107
|
+
onMessagesChanged: () => {}, onStatusChanged: () => {}, onStreamingChanged: () => {},
|
|
1108
|
+
});
|
|
1109
|
+
await session.sendMessage('Hello');
|
|
1110
|
+
expect(lastAssistantMessage(session)?.content).toContain("chat service couldn't complete the request");
|
|
1111
|
+
expect(lastAssistantMessage(session)?.content).toContain('_Details: Chat backend request failed: 503_');
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1073
1114
|
it('uses a static errorMessage override verbatim', async () => {
|
|
1074
1115
|
failFetchWith(new Error('boom'));
|
|
1075
1116
|
const session = new AgentWidgetSession(
|
package/src/session.ts
CHANGED
|
@@ -265,10 +265,9 @@ export class SessionHistoryError extends Error {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* underlying reason to help with debugging.
|
|
268
|
+
* Dispatch can fail in transport, stream parsing, lazy modules or application
|
|
269
|
+
* callbacks. Only recognized fetch failures should suggest connectivity;
|
|
270
|
+
* unknown/internal failures must not send users to debug a healthy backend.
|
|
272
271
|
*
|
|
273
272
|
* Callers can override the copy via `config.errorMessage` (a static string or
|
|
274
273
|
* a function of the error). An override that returns an empty string yields ""
|
|
@@ -283,9 +282,17 @@ function buildDispatchErrorContent(
|
|
|
283
282
|
if (typeof override === "string") return override;
|
|
284
283
|
if (typeof override === "function") return override(err);
|
|
285
284
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
285
|
+
// Fetch rejects with TypeError too, so the error class cannot distinguish
|
|
286
|
+
// an offline browser from an internal fault such as a missing function.
|
|
287
|
+
const detail = err.message.trim();
|
|
288
|
+
const networkFailure = /^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.?)$/i.test(detail);
|
|
289
|
+
const backendFailure = /^(Chat backend|Agent execution) request failed: \d{3}\b/.test(detail);
|
|
290
|
+
const base = networkFailure
|
|
291
|
+
? "Sorry: I couldn't reach the assistant. Please check your connection and try again."
|
|
292
|
+
: backendFailure
|
|
293
|
+
? "Sorry: the chat service couldn't complete the request. Please try again."
|
|
294
|
+
: "Sorry: the chat encountered an unexpected error. Please reload the page and try again.";
|
|
295
|
+
return detail ? `${base}\n\n_Details: ${detail}_` : base;
|
|
289
296
|
}
|
|
290
297
|
|
|
291
298
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { version } from "../../package.json";
|
|
3
|
+
import { pinCdnRelease } from "./cdn-release";
|
|
4
|
+
|
|
5
|
+
describe("pinCdnRelease", () => {
|
|
6
|
+
it.each(["install", "index", "launcher"])("pins the %s entry's dependencies to its embedded release", (entry) => {
|
|
7
|
+
expect(pinCdnRelease(`https://cdn.runtype.com/persona/latest/${entry}.global.js?embed=1`))
|
|
8
|
+
.toBe(`https://cdn.runtype.com/persona/${version}/${entry}.global.js?embed=1`);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it.each([
|
|
12
|
+
"https://cdn.runtype.com/persona/4.0.0/index.global.js",
|
|
13
|
+
"https://cdn.example.com/persona/latest/index.global.js",
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@runtypelabs/persona@4/dist/index.global.js",
|
|
15
|
+
"https://cdn.runtype.com/other/latest/index.global.js",
|
|
16
|
+
"",
|
|
17
|
+
])("preserves explicit versions and other hosting layouts: %s", (src) => {
|
|
18
|
+
expect(pinCdnRelease(src)).toBe(src);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { version } from "../../package.json";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A cached /latest/ entry must keep using its own release, even after another
|
|
5
|
+
* release replaces latest. Only rewrite our published CDN layout; custom hosts
|
|
6
|
+
* and explicitly versioned URLs retain their existing sibling resolution.
|
|
7
|
+
* The bundler embeds this package version in each script-tag entry.
|
|
8
|
+
*/
|
|
9
|
+
export function pinCdnRelease(src: string): string {
|
|
10
|
+
return src.replace(
|
|
11
|
+
/^https:\/\/cdn\.runtype\.com\/persona\/latest\//,
|
|
12
|
+
`https://cdn.runtype.com/persona/${version}/`,
|
|
13
|
+
);
|
|
14
|
+
}
|
package/dist/chunk-BkOYJ11B.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=`4.32.1`;export{e as t};
|