fix(agent): validate UI envelopes

This commit is contained in:
2026-07-14 11:01:07 +08:00
parent 1c85d938a6
commit ed60a13f12
6 changed files with 276 additions and 31 deletions
+38 -7
View File
@@ -13,6 +13,19 @@ const registry: UIRegistry = {
};
describe("UIEnvelope validation", () => {
it("fails closed when the backend registry is unavailable", () => {
const envelope = parseUiEnvelope({
kind: "chart",
schemaVersion: "agent-ui@1",
grammar: "echarts-safe-subset",
surface: "chat_inline",
spec: { chart_type: "line" },
data: { x_data: ["a"], series: [{ name: "s", data: [1] }] }
});
expect(envelope).not.toBeNull();
expect(envelope ? isUiEnvelopeAllowed(envelope, null) : true).toBe(false);
});
it("rejects registered components outside the frontend allowlist", () => {
const envelope = parseUiEnvelope({
kind: "registered_component",
@@ -22,8 +35,7 @@ describe("UIEnvelope validation", () => {
props: {}
});
expect(envelope).not.toBeNull();
expect(envelope ? isUiEnvelopeAllowed(envelope, registry) : false).toBe(false);
expect(envelope).toBeNull();
});
it("rejects registered components on unsupported surfaces", () => {
@@ -32,7 +44,7 @@ describe("UIEnvelope validation", () => {
schemaVersion: "agent-ui@1",
component: "HistoryPanel",
surface: "chat_inline",
props: {}
props: { feature_infos: [["J1", "junction"]], data_type: "realtime" }
});
expect(envelope).not.toBeNull();
@@ -46,8 +58,8 @@ describe("UIEnvelope validation", () => {
schemaVersion: "agent-ui@1",
grammar: "vega-lite",
surface: "chat_inline",
spec: {},
data: {}
spec: { chart_type: "line" },
data: { x_data: ["a"], series: [{ name: "s", data: [1] }] }
})
).toBeNull();
@@ -56,14 +68,33 @@ describe("UIEnvelope validation", () => {
schemaVersion: "agent-ui@1",
grammar: "echarts-safe-subset",
surface: "chat_inline",
spec: {},
data: {}
spec: { chart_type: "line" },
data: { x_data: ["a"], series: [{ name: "s", data: [1] }] }
});
expect(envelope).not.toBeNull();
expect(envelope ? isUiEnvelopeAllowed(envelope, registry) : false).toBe(true);
});
it("rejects unsupported chart types and mismatched data", () => {
expect(parseUiEnvelope({
kind: "chart",
schemaVersion: "agent-ui@1",
grammar: "echarts-safe-subset",
surface: "chat_inline",
spec: { chart_type: "pie" },
data: { x_data: ["a"], series: [{ name: "s", data: [1] }] }
})).toBeNull();
expect(parseUiEnvelope({
kind: "chart",
schemaVersion: "agent-ui@1",
grammar: "echarts-safe-subset",
surface: "chat_inline",
spec: { chart_type: "line" },
data: { x_data: ["a", "b"], series: [{ name: "s", data: [1] }] }
})).toBeNull();
});
it("filters invalid registry surfaces before allowlist checks", () => {
const parsed = parseUiRegistry({
schema_version: "agent-ui-registry@1",