feat(agent): render local interactive answers and fold completed process

This commit is contained in:
2026-10-12 18:54:18 +08:00
parent e0eb6a721e
commit 2c5bf15cce
30 changed files with 1610 additions and 75 deletions
@@ -0,0 +1,13 @@
import { describe, expect, it } from "vitest";
import { parseFrontendChart } from "./chart";
describe("frontend chart boundary", () => {
const chart = { title: "压力", chart_type: "line", x_data: ["t1", "t2"], series: [{ name: "D1", data: [20, 21] }] };
it("accepts finite aligned series and rejects code, unaligned data and excessive points", () => {
expect(parseFrontendChart(chart)).not.toBeNull();
expect(parseFrontendChart({ ...chart, formatter: "function(){}" })).toBeNull();
expect(parseFrontendChart({ ...chart, series: [{ name: "D1", data: [20] }] })).toBeNull();
expect(parseFrontendChart({ ...chart, series: [{ name: "D1", data: [20, Infinity] }] })).toBeNull();
expect(parseFrontendChart({ ...chart, x_data: Array(101).fill("t") })).toBeNull();
});
});
@@ -0,0 +1,11 @@
import { z } from "zod";
const chartSchema = z.object({
title: z.string().max(200), chart_type: z.enum(["line", "bar"]),
x_data: z.array(z.string().max(128)).min(1).max(100),
series: z.array(z.object({ name: z.string().max(128), data: z.array(z.number().finite()).min(1).max(100) }).strict()).min(1).max(8),
}).strict().refine((value) => value.series.every((series) => series.data.length === value.x_data.length));
export const parseFrontendChart = (value: unknown) => {
const parsed = chartSchema.safeParse(value);
return parsed.success ? parsed.data : null;
};