@agent-native/toolkit 0.206.0-nightly-20261009124314 → 0.206.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/dist/app/auth/entry.d.ts +2 -2
- package/dist/app/auth/entry.d.ts.map +1 -1
- package/dist/app/auth/entry.js +9 -2
- package/dist/app/auth/entry.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +7 -5
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewer.js +16 -6
- package/dist/app/extensions/ExtensionViewer.js.map +1 -1
- package/dist/app/extensions/InlineExtensionFrame.d.ts.map +1 -1
- package/dist/app/extensions/InlineExtensionFrame.js +9 -5
- package/dist/app/extensions/InlineExtensionFrame.js.map +1 -1
- package/dist/app/extensions/iframe-display-sources.d.ts +9 -0
- package/dist/app/extensions/iframe-display-sources.d.ts.map +1 -0
- package/dist/app/extensions/iframe-display-sources.js +56 -0
- package/dist/app/extensions/iframe-display-sources.js.map +1 -0
- package/dist/app/progress/RunsTray.js +1 -1
- package/dist/app/progress/RunsTray.js.map +1 -1
- package/dist/app/styles/agent-native.css +22 -4
- package/package.json +4 -4
- package/src/app/auth/entry.spec.tsx +31 -1
- package/src/app/auth/entry.tsx +13 -1
- package/src/app/chat/chat/run-recovery.tsx +7 -5
- package/src/app/extensions/ExtensionViewer.spec.tsx +68 -2
- package/src/app/extensions/ExtensionViewer.tsx +15 -4
- package/src/app/extensions/InlineExtensionFrame.spec.tsx +17 -2
- package/src/app/extensions/InlineExtensionFrame.tsx +13 -3
- package/src/app/extensions/iframe-display-sources.spec.ts +74 -0
- package/src/app/extensions/iframe-display-sources.ts +61 -0
- package/src/app/progress/RunsTray.tsx +1 -1
- package/src/app/styles/agent-native.css +22 -4
|
@@ -11,8 +11,20 @@ vi.mock("@agent-native/core/client/agent-chat", () => ({
|
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
13
|
vi.mock("@agent-native/core/extensions/html-shell", () => ({
|
|
14
|
-
buildExtensionHtml: (
|
|
15
|
-
|
|
14
|
+
buildExtensionHtml: (
|
|
15
|
+
content: string,
|
|
16
|
+
_themeVars: string,
|
|
17
|
+
_isDark: boolean,
|
|
18
|
+
_extensionId?: string,
|
|
19
|
+
_binding?: unknown,
|
|
20
|
+
metaCsp?: string,
|
|
21
|
+
) =>
|
|
22
|
+
`<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="${metaCsp ?? ""}" /></head><body>${content}</body></html>`,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
vi.mock("./iframe-display-sources.js", () => ({
|
|
26
|
+
useExtensionIframeMetaCsp: (enabled = true) =>
|
|
27
|
+
enabled ? "img-src 'self' https://cdn.example.com;" : undefined,
|
|
16
28
|
}));
|
|
17
29
|
|
|
18
30
|
describe("InlineExtensionFrame", () => {
|
|
@@ -54,6 +66,9 @@ describe("InlineExtensionFrame", () => {
|
|
|
54
66
|
"allow-scripts allow-forms allow-popups allow-downloads",
|
|
55
67
|
);
|
|
56
68
|
expect(iframe?.getAttribute("srcdoc")).toContain("Send choice");
|
|
69
|
+
expect(iframe?.getAttribute("srcdoc")).toContain(
|
|
70
|
+
`content="img-src 'self' https://cdn.example.com;"`,
|
|
71
|
+
);
|
|
57
72
|
|
|
58
73
|
await act(async () => {
|
|
59
74
|
window.dispatchEvent(
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type BridgePolicyContext,
|
|
14
14
|
type ExtensionBridgeRole,
|
|
15
15
|
} from "./iframe-bridge.js";
|
|
16
|
+
import { useExtensionIframeMetaCsp } from "./iframe-display-sources.js";
|
|
16
17
|
import { normalizeAgentNativeExtensionSandbox } from "./portable-extension.js";
|
|
17
18
|
|
|
18
19
|
const THEME_CSS_VARS = [
|
|
@@ -300,6 +301,7 @@ async function handleTransientExtensionData(
|
|
|
300
301
|
function buildTransientSrcDoc(
|
|
301
302
|
extension: InlineExtensionDefinition,
|
|
302
303
|
isDark: boolean,
|
|
304
|
+
metaCsp: string,
|
|
303
305
|
): string {
|
|
304
306
|
return buildExtensionHtml(
|
|
305
307
|
extension.content ?? "",
|
|
@@ -312,6 +314,7 @@ function buildTransientSrcDoc(
|
|
|
312
314
|
isAuthor: true,
|
|
313
315
|
role: "owner",
|
|
314
316
|
},
|
|
317
|
+
metaCsp,
|
|
315
318
|
);
|
|
316
319
|
}
|
|
317
320
|
|
|
@@ -397,10 +400,17 @@ export function InlineExtensionFrame({
|
|
|
397
400
|
);
|
|
398
401
|
}, [extension?.updatedAt, isTransient, resolvedId, slotId]);
|
|
399
402
|
|
|
403
|
+
// Transient frames are built here, so they fetch the deployment's configured
|
|
404
|
+
// img-src / media-src from the server and wait for it before rendering.
|
|
405
|
+
const iframeMetaCsp = useExtensionIframeMetaCsp(!!extension && isTransient);
|
|
400
406
|
const srcDoc = useMemo(() => {
|
|
401
|
-
if (!extension || !isTransient) return undefined;
|
|
402
|
-
return buildTransientSrcDoc(
|
|
403
|
-
|
|
407
|
+
if (!extension || !isTransient || !iframeMetaCsp) return undefined;
|
|
408
|
+
return buildTransientSrcDoc(
|
|
409
|
+
extension,
|
|
410
|
+
initialDarkRef.current,
|
|
411
|
+
iframeMetaCsp,
|
|
412
|
+
);
|
|
413
|
+
}, [extension, iframeMetaCsp, isTransient]);
|
|
404
414
|
|
|
405
415
|
useEffect(() => {
|
|
406
416
|
bridgeContextRef.current = isTransient
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { EXTENSION_IFRAME_META_CSP } from "@agent-native/core/extensions/html-shell";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
loadExtensionIframeMetaCsp,
|
|
6
|
+
resetExtensionIframeMetaCspForTests,
|
|
7
|
+
} from "./iframe-display-sources.js";
|
|
8
|
+
|
|
9
|
+
describe("loadExtensionIframeMetaCsp", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
resetExtensionIframeMetaCspForTests();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.unstubAllGlobals();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("builds the meta CSP from the server's configured sources once", async () => {
|
|
19
|
+
const fetchMock = vi.fn(async () =>
|
|
20
|
+
Response.json({
|
|
21
|
+
imageSources: ["'self'", "https:"],
|
|
22
|
+
mediaSources: ["'self'", "blob:"],
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
26
|
+
|
|
27
|
+
const csp = await loadExtensionIframeMetaCsp();
|
|
28
|
+
expect(csp).toContain("img-src 'self' https:;");
|
|
29
|
+
expect(csp).toContain("media-src 'self' blob:;");
|
|
30
|
+
expect(csp).toContain("connect-src 'self';");
|
|
31
|
+
await expect(loadExtensionIframeMetaCsp()).resolves.toBe(csp);
|
|
32
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).toContain(
|
|
34
|
+
"/_agent-native/extensions/iframe/display-sources",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("falls back to the default policy for an invalid source list", async () => {
|
|
39
|
+
vi.stubGlobal(
|
|
40
|
+
"fetch",
|
|
41
|
+
vi.fn(async () =>
|
|
42
|
+
Response.json({
|
|
43
|
+
imageSources: ["'self'; connect-src *"],
|
|
44
|
+
mediaSources: ["'self'"],
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
await expect(loadExtensionIframeMetaCsp()).resolves.toBe(
|
|
50
|
+
EXTENSION_IFRAME_META_CSP,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("falls back to the default policy on a failed request and retries later", async () => {
|
|
55
|
+
const fetchMock = vi
|
|
56
|
+
.fn()
|
|
57
|
+
.mockResolvedValueOnce(new Response("nope", { status: 500 }))
|
|
58
|
+
.mockResolvedValueOnce(
|
|
59
|
+
Response.json({
|
|
60
|
+
imageSources: ["'self'", "https:"],
|
|
61
|
+
mediaSources: ["'self'"],
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
65
|
+
|
|
66
|
+
await expect(loadExtensionIframeMetaCsp()).resolves.toBe(
|
|
67
|
+
EXTENSION_IFRAME_META_CSP,
|
|
68
|
+
);
|
|
69
|
+
await expect(loadExtensionIframeMetaCsp()).resolves.toContain(
|
|
70
|
+
"img-src 'self' https:;",
|
|
71
|
+
);
|
|
72
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
2
|
+
import {
|
|
3
|
+
EXTENSION_IFRAME_DISPLAY_SOURCES_PATH,
|
|
4
|
+
EXTENSION_IFRAME_META_CSP,
|
|
5
|
+
extensionIframeMetaCspFromDisplaySources,
|
|
6
|
+
} from "@agent-native/core/extensions/html-shell";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Client-rendered extension frames (`ExtensionViewer` srcDoc,
|
|
11
|
+
* `InlineExtensionFrame` transient frames) build their HTML in the browser, so
|
|
12
|
+
* they read the deployment's `extensions.iframeImageSources` /
|
|
13
|
+
* `iframeMediaSources` from the server instead of from app config. The lists
|
|
14
|
+
* are validated again by `extensionIframeMetaCspFromDisplaySources`, and any
|
|
15
|
+
* failure (network, non-OK status, malformed body, invalid source) falls back
|
|
16
|
+
* to the default policy, which blocks remote images and media.
|
|
17
|
+
*/
|
|
18
|
+
let pending: Promise<string> | null = null;
|
|
19
|
+
|
|
20
|
+
export function loadExtensionIframeMetaCsp(): Promise<string> {
|
|
21
|
+
if (!pending) {
|
|
22
|
+
const request = fetch(
|
|
23
|
+
agentNativePath(EXTENSION_IFRAME_DISPLAY_SOURCES_PATH),
|
|
24
|
+
)
|
|
25
|
+
.then(async (res) => {
|
|
26
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
27
|
+
return extensionIframeMetaCspFromDisplaySources(await res.json());
|
|
28
|
+
})
|
|
29
|
+
.catch(() => {
|
|
30
|
+
// Do not cache a failure: the next frame retries.
|
|
31
|
+
if (pending === request) pending = null;
|
|
32
|
+
return EXTENSION_IFRAME_META_CSP;
|
|
33
|
+
});
|
|
34
|
+
pending = request;
|
|
35
|
+
}
|
|
36
|
+
return pending;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The meta CSP for a client-rendered frame, or `undefined` while it loads.
|
|
41
|
+
* Callers wait for it rather than rendering with the default policy first, so
|
|
42
|
+
* a frame is built once with the policy it keeps.
|
|
43
|
+
*/
|
|
44
|
+
export function useExtensionIframeMetaCsp(enabled = true): string | undefined {
|
|
45
|
+
const [metaCsp, setMetaCsp] = useState<string | undefined>(undefined);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!enabled) return;
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
void loadExtensionIframeMetaCsp().then((csp) => {
|
|
50
|
+
if (!cancelled) setMetaCsp(csp);
|
|
51
|
+
});
|
|
52
|
+
return () => {
|
|
53
|
+
cancelled = true;
|
|
54
|
+
};
|
|
55
|
+
}, [enabled]);
|
|
56
|
+
return enabled ? metaCsp : undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resetExtensionIframeMetaCspForTests(): void {
|
|
60
|
+
pending = null;
|
|
61
|
+
}
|
|
@@ -1061,10 +1061,18 @@
|
|
|
1061
1061
|
}
|
|
1062
1062
|
|
|
1063
1063
|
.agent-builder-setup-card--attached {
|
|
1064
|
+
inline-size: min(
|
|
1065
|
+
calc(100% - var(--agent-kit-composer-adjacent-inline-inset) - 0.5rem),
|
|
1066
|
+
var(--agent-kit-conversation-max-width)
|
|
1067
|
+
);
|
|
1068
|
+
min-inline-size: 0;
|
|
1069
|
+
max-inline-size: min(100%, var(--agent-kit-conversation-max-width));
|
|
1064
1070
|
position: relative;
|
|
1065
1071
|
z-index: 1;
|
|
1072
|
+
margin-inline: auto;
|
|
1066
1073
|
margin-bottom: -1.875rem;
|
|
1067
1074
|
padding-bottom: 0 !important;
|
|
1075
|
+
padding-inline: 0 !important;
|
|
1068
1076
|
animation: agent-builder-setup-card-enter 220ms var(--ease-out-strong) both;
|
|
1069
1077
|
}
|
|
1070
1078
|
|
|
@@ -1098,10 +1106,20 @@
|
|
|
1098
1106
|
padding-bottom: calc(0.625rem + 1.875rem);
|
|
1099
1107
|
}
|
|
1100
1108
|
|
|
1101
|
-
.agent-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1109
|
+
.agent-sidebar-panel .agent-builder-setup-card--attached {
|
|
1110
|
+
inline-size: min(
|
|
1111
|
+
calc(100% - 0.5rem),
|
|
1112
|
+
var(--agent-kit-conversation-max-width)
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
@media (max-width: 40rem) {
|
|
1117
|
+
.agentkit-composer-stack .agent-builder-setup-card--attached {
|
|
1118
|
+
inline-size: min(
|
|
1119
|
+
calc(100% - 0.5rem),
|
|
1120
|
+
var(--agent-kit-conversation-max-width)
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1105
1123
|
}
|
|
1106
1124
|
|
|
1107
1125
|
.agent-builder-setup-inline--attached {
|