From cbe1ee7943cafdb722c2a6e5681176d7a7dbbb25 Mon Sep 17 00:00:00 2001 From: jp-agenta <174311389+jp-agenta@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:46:38 +0000 Subject: [PATCH 01/13] v0.96.7 --- api/pyproject.toml | 2 +- sdk/pyproject.toml | 2 +- services/pyproject.toml | 2 +- web/ee/package.json | 2 +- web/oss/package.json | 2 +- web/package.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index a787f25a30..62bd57321e 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "api" -version = "0.96.6" +version = "0.96.7" description = "Agenta API" authors = [ { name = "Mahmoud Mabrouk", email = "mahmoud@agenta.ai" }, diff --git a/sdk/pyproject.toml b/sdk/pyproject.toml index 00bd184d33..747b1f8218 100644 --- a/sdk/pyproject.toml +++ b/sdk/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.96.6" +version = "0.96.7" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = [ diff --git a/services/pyproject.toml b/services/pyproject.toml index c8db7e6f1e..5e7097218e 100644 --- a/services/pyproject.toml +++ b/services/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "services" -version = "0.96.6" +version = "0.96.7" description = "Agenta Services (Chat & Completion)" authors = [ "Mahmoud Mabrouk ", diff --git a/web/ee/package.json b/web/ee/package.json index 8f084f6df8..1bea001d3a 100644 --- a/web/ee/package.json +++ b/web/ee/package.json @@ -1,6 +1,6 @@ { "name": "@agenta/ee", - "version": "0.96.6", + "version": "0.96.7", "private": true, "engines": { "node": ">=18" diff --git a/web/oss/package.json b/web/oss/package.json index 366483c530..f1322bbec0 100644 --- a/web/oss/package.json +++ b/web/oss/package.json @@ -1,6 +1,6 @@ { "name": "@agenta/oss", - "version": "0.96.6", + "version": "0.96.7", "private": true, "engines": { "node": ">=18" diff --git a/web/package.json b/web/package.json index 2c6e614594..eff633802e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "agenta-web", - "version": "0.96.6", + "version": "0.96.7", "workspaces": [ "ee", "oss", From 8589228c88067e09c838755469344a839f6d7a12 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Fri, 17 Apr 2026 16:51:01 +0200 Subject: [PATCH 02/13] [feat] Persist evaluator app selection across sessions and commits - Add localStorage-backed atom to remember selected app per project - Restore app connection when reopening evaluator drawer - Update drawer entity ID after commit to display new revision - Fix output port detection for evaluators with draft field edits - Detect JSON-formatted testcase values after async load - Key EditorProvider on JSON detection to trigger clean remount --- .../components/ConfigureEvaluator/atoms.ts | 39 +- .../WorkflowRevisionDrawerWrapper/index.tsx | 27 +- .../src/testcase/state/molecule.ts | 9 +- .../src/workflow/state/molecule.ts | 32 +- .../adapters/VariableControlAdapter.tsx | 26 +- web/pnpm-lock.yaml | 698 +++++++++--------- 6 files changed, 467 insertions(+), 364 deletions(-) diff --git a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts index 038ac97122..f36ec5ba13 100644 --- a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts +++ b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts @@ -12,7 +12,43 @@ import {playgroundController} from "@agenta/playground" import {playgroundNodesAtom} from "@agenta/playground/state" +import {projectIdAtom} from "@agenta/shared/state" import {atom} from "jotai" +import {atomWithStorage} from "jotai/utils" + +// ============================================================================ +// PERSISTED APP SELECTION +// ============================================================================ + +interface PersistedAppSelection { + appRevisionId: string + appLabel: string +} + +/** Stores the last selected app per project in localStorage. */ +const persistedAppSelectionByProjectAtom = atomWithStorage>( + "agenta:evaluator:selected-app", + {}, +) + +/** Read/write the persisted app selection for the current project. */ +export const persistedAppSelectionAtom = atom( + (get) => { + const projectId = get(projectIdAtom) || "__global__" + const all = get(persistedAppSelectionByProjectAtom) + return all[projectId] ?? null + }, + (get, set, next: PersistedAppSelection | null) => { + const projectId = get(projectIdAtom) || "__global__" + const all = get(persistedAppSelectionByProjectAtom) + if (next) { + set(persistedAppSelectionByProjectAtom, {...all, [projectId]: next}) + } else { + const {[projectId]: _, ...rest} = all + set(persistedAppSelectionByProjectAtom, rest) + } + }, +) // ============================================================================ // DERIVED SELECTORS @@ -74,8 +110,9 @@ export const connectAppToEvaluatorAtom = atom( ) => { const {appRevisionId, appLabel, evaluatorRevisionId, evaluatorLabel} = params - // Track selected app label for display + // Track selected app label for display + persist across sessions set(selectedAppLabelAtom, appLabel) + set(persistedAppSelectionAtom, {appRevisionId, appLabel}) // Replace primary node with app const nodeId = set(playgroundController.actions.changePrimaryNode, { diff --git a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx index 2a54170a03..853a2d5c3e 100644 --- a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx +++ b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx @@ -56,6 +56,7 @@ import OSSdrillInUIProvider from "@/oss/components/DrillInView/OSSdrillInUIProvi import SimpleSharedEditor from "@/oss/components/EditorViews/SimpleSharedEditor" import { connectAppToEvaluatorAtom, + persistedAppSelectionAtom, selectedAppLabelAtom, } from "@/oss/components/Evaluators/components/ConfigureEvaluator/atoms" import EvaluatorPlaygroundHeader from "@/oss/components/Evaluators/components/ConfigureEvaluator/EvaluatorPlaygroundHeader" @@ -182,10 +183,24 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { const setInitialized = useSetAtom(playgroundInitializedAtom) const setSelectedAppLabel = useSetAtom(selectedAppLabelAtom) const setConnectedTestset = useSetAtom(connectedTestsetAtom) + const connectApp = useSetAtom(connectAppToEvaluatorAtom) useEffect(() => { if (entityId) { addPrimaryNode({type: "workflow", id: entityId, label: "Evaluator"}) setInitialized(true) + + // Restore persisted app selection (survives drawer close/reopen and commits) + const store = getDefaultStore() + const persisted = store.get(persistedAppSelectionAtom) + if (persisted) { + setSelectedAppLabel(persisted.appLabel) + connectApp({ + appRevisionId: persisted.appRevisionId, + appLabel: persisted.appLabel, + evaluatorRevisionId: entityId, + evaluatorLabel: "Evaluator", + }) + } } return () => { const store = getDefaultStore() @@ -232,11 +247,9 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { setInitialized, setSelectedAppLabel, setConnectedTestset, + connectApp, ]) - // Evaluator state — derive directly from nodes instead of external atoms - // to avoid stale reads across atom boundaries. - const connectApp = useSetAtom(connectAppToEvaluatorAtom) const selectedAppLabel = useAtomValue(selectedAppLabelAtom) const nodes = useAtomValue(useMemo(() => playgroundController.selectors.nodes(), [])) @@ -370,6 +383,14 @@ const useEvaluatorCommitCallback = () => { if (isEvaluatorCreate) { drawerCallbackRef.current?.(result.newRevisionId) closeDrawerRef.current() + } else { + // In evaluator-view mode, the selection change callback + // skips updating the drawer entity ID (because the drawer + // entity intentionally differs from the primary playground + // node after app connection). Update it explicitly here + // so the drawer displays the newly committed revision. + const store = getDefaultStore() + store.set(workflowRevisionDrawerEntityIdAtom, result.newRevisionId) } message.success( diff --git a/web/packages/agenta-entities/src/testcase/state/molecule.ts b/web/packages/agenta-entities/src/testcase/state/molecule.ts index 1949c85f39..f77163510b 100644 --- a/web/packages/agenta-entities/src/testcase/state/molecule.ts +++ b/web/packages/agenta-entities/src/testcase/state/molecule.ts @@ -958,8 +958,13 @@ export const testcaseMolecule = { }, }, - // Imperative API - get: extendedMolecule.get, + // Imperative API — override get.data to use testcaseEntityAtomFamily + // (handles draft-only/new testcases that the base molecule's merge returns null for) + get: { + ...extendedMolecule.get, + data: (id: string, options?: StoreOptions) => + getStore(options).get(testcaseEntityAtomFamily(id)), + }, set: extendedMolecule.set, // Lifecycle and cleanup from base molecule diff --git a/web/packages/agenta-entities/src/workflow/state/molecule.ts b/web/packages/agenta-entities/src/workflow/state/molecule.ts index fa80e2529a..97bda0862a 100644 --- a/web/packages/agenta-entities/src/workflow/state/molecule.ts +++ b/web/packages/agenta-entities/src/workflow/state/molecule.ts @@ -714,10 +714,36 @@ const outputPortsAtomFamily = atomFamily((workflowId: string) => const schemaOutputs = extractOutputPortsFromSchema(entity?.data?.schemas?.outputs) - // For evaluators, the backend output schema may be incomplete (e.g., only "score" - // when the json_schema config also defines "reasoning"). Prefer the richer source. + // For evaluators, the backend output schema (`schemas.outputs`) only reflects + // the last committed revision. The draft-merged parameters are more authoritative + // for the current (possibly uncommitted) field configuration. + // + // Priority order: + // 1. `fields` array in parameters — directly reflects draft edits from + // FieldsTagsEditorControl (e.g., JSON Multi-Field Match). Each field + // becomes a number port, plus an "aggregate_score" port. + // 2. feedback_config.json_schema.schema.properties — generated by the + // backend on commit, may be stale if the user hasn't committed yet. + // 3. schemas.outputs — committed output schema from the backend. + // 4. Default: single "score" port. if (entity?.flags?.is_evaluator) { const config = resolveParameters(entity.data) as Record | undefined + + // Check for a `fields` array (e.g., from FieldsTagsEditorControl). + // This is the most current source when the user adds/removes fields. + const fieldsArray = config?.fields as string[] | undefined + if (Array.isArray(fieldsArray) && fieldsArray.length > 0) { + const ports: RunnablePort[] = [ + {key: "aggregate_score", name: "Aggregate score", type: "number"}, + ...fieldsArray.map((field) => ({ + key: field, + name: formatKeyAsName(field), + type: "number" as const, + })), + ] + return ports + } + // Nested form: feedback_config.json_schema.schema.properties const feedbackConfig = config?.feedback_config as Record | undefined const jsonSchema = feedbackConfig?.json_schema as @@ -727,7 +753,7 @@ const outputPortsAtomFamily = atomFamily((workflowId: string) => const flatJsonSchema = config?.json_schema as typeof jsonSchema | undefined const fbProperties = jsonSchema?.schema?.properties ?? flatJsonSchema?.schema?.properties - if (fbProperties && Object.keys(fbProperties).length > schemaOutputs.length) { + if (fbProperties && Object.keys(fbProperties).length > 0) { return Object.entries(fbProperties).map(([key, prop]) => ({ key, name: formatKeyAsName(key), diff --git a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx index 62c32597c2..803e58427a 100644 --- a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx +++ b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useMemo, useRef} from "react" +import React, {useCallback, useEffect, useMemo, useRef, useState} from "react" import {executionItemController, playgroundController} from "@agenta/playground" import {isJsonString} from "@agenta/shared/utils" @@ -144,15 +144,22 @@ const VariableControlAdapter: React.FC = ({ const isJsonType = portType === "object" || portType === "array" const jsonDefault = portType === "array" ? "[]" : "{}" - // Capture whether the initial value looks like JSON at mount time. - // This is safe because codeOnly is set once before Lexical initialises. - // Switching codeOnly dynamically at runtime crashes Lexical - // (MarkdownShortcuts: missing dependency code), so the flag is immutable. - const initialValueLooksLikeJson = useRef( - typeof value === "string" && !!value && isJsonString(value), - ).current + // Detect whether the value looks like JSON. Uses state instead of ref so that + // async-loaded testcase data (initially "") is detected once it arrives. + // Only upgrades false→true (never downgrades) because switching Lexical's + // codeOnly from true→false at runtime crashes (MarkdownShortcuts dependency). + // The EditorProvider is keyed on this flag so the upgrade triggers a clean remount. + const [detectedAsJson, setDetectedAsJson] = useState( + () => typeof value === "string" && !!value && isJsonString(value), + ) + + useEffect(() => { + if (!detectedAsJson && typeof value === "string" && !!value && isJsonString(value)) { + setDetectedAsJson(true) + } + }, [value, detectedAsJson]) - const isJsonEditor = isJsonType || initialValueLooksLikeJson + const isJsonEditor = isJsonType || detectedAsJson const effectiveValue = isJsonEditor && isJsonType && (!value || value === "") ? jsonDefault : value @@ -256,6 +263,7 @@ const VariableControlAdapter: React.FC = ({ return (
=4.17.23' version: 4.18.1 postcss: specifier: ^8.5.6 - version: 8.5.9 + version: 8.5.10 posthog-js: specifier: ^1.223.3 - version: 1.367.0 + version: 1.369.2 rc-virtual-list: specifier: ^3.19.2 version: 3.19.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -294,7 +294,7 @@ importers: version: 0.51.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(supertokens-web-js@0.16.0) swc-loader: specifier: ^0.2.7 - version: 0.2.7(@swc/core@1.15.24(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21))) + version: 0.2.7(@swc/core@1.15.26(@swc/helpers@0.5.21))(webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21))) swr: specifier: ^2.4.0 version: 2.4.1(react@19.2.5) @@ -315,7 +315,7 @@ importers: version: 11.1.0 webpack: specifier: ^5.104.1 - version: 5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21)) + version: 5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21)) devDependencies: '@agenta/web-tests': specifier: workspace:../tests @@ -382,7 +382,7 @@ importers: version: 6.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@ant-design/x': specifier: ^2.5.0 - version: 2.5.0(antd@6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 2.5.0(antd@6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@cloudflare/stream-react': specifier: ^1.9.3 version: 1.9.3(react@19.2.5) @@ -514,10 +514,10 @@ importers: version: 8.18.0 antd: specifier: ^6.1.3 - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) autoprefixer: specifier: 10.4.20 - version: 10.4.20(postcss@8.5.9) + version: 10.4.20(postcss@8.5.10) axios: specifier: 1.13.5 version: 1.13.5 @@ -565,7 +565,7 @@ importers: version: 4.1.1 jsonrepair: specifier: ^3.13.2 - version: 3.13.3 + version: 3.14.0 lexical: specifier: ^0.40.0 version: 0.40.0 @@ -586,10 +586,10 @@ importers: version: 5.5.3 postcss: specifier: ^8.5.6 - version: 8.5.9 + version: 8.5.10 posthog-js: specifier: ^1.223.3 - version: 1.367.0 + version: 1.369.2 prismjs: specifier: ^1.30.0 version: 1.30.0 @@ -637,7 +637,7 @@ importers: version: 0.16.0 swc-loader: specifier: ^0.2.7 - version: 0.2.7(@swc/core@1.15.24(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21))) + version: 0.2.7(@swc/core@1.15.26(@swc/helpers@0.5.21))(webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21))) swr: specifier: ^2.4.0 version: 2.4.1(react@19.2.5) @@ -741,7 +741,7 @@ importers: version: 2.1.10(react-dom@19.2.5(react@19.2.5))(react@19.2.5) antd: specifier: '>=5.0.0' - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) dayjs: specifier: ^1.11.20 version: 1.11.20 @@ -787,7 +787,7 @@ importers: version: 5.99.0(react@19.2.5) antd: specifier: '>=5.0.0' - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) axios: specifier: 1.13.5 version: 1.13.5 @@ -869,7 +869,7 @@ importers: version: 0.40.0 '@tanstack/react-virtual': specifier: ^3.13.2 - version: 3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -902,7 +902,7 @@ importers: version: 4.1.1 jsonrepair: specifier: ^3.13.2 - version: 3.13.3 + version: 3.14.0 lexical: specifier: ^0.40.0 version: 0.40.0 @@ -938,7 +938,7 @@ importers: version: 5.99.0(react@19.2.5) antd: specifier: '>=5.0.0' - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) axios: specifier: 1.13.5 version: 1.13.5 @@ -1014,7 +1014,7 @@ importers: version: 0.40.0 '@tanstack/react-virtual': specifier: ^3.13.2 - version: 3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -1044,7 +1044,7 @@ importers: version: 3.1.3 jsonrepair: specifier: ^3.13.2 - version: 3.13.3 + version: 3.14.0 lexical: specifier: ^0.40.0 version: 0.40.0 @@ -1126,7 +1126,7 @@ importers: version: 2.1.10(react-dom@19.2.5(react@19.2.5))(react@19.2.5) antd: specifier: '>=5.0.0' - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1187,7 +1187,7 @@ importers: version: 2.2.3 jsonrepair: specifier: ^3.13.3 - version: 3.13.3 + version: 3.14.0 react: specifier: '>=18.0.0' version: 19.2.5 @@ -1266,16 +1266,16 @@ importers: version: 5.99.0(react@19.2.5) '@tanstack/react-virtual': specifier: ^3.12.3 - version: 3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5) antd: specifier: '>=5.0.0' - version: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) clsx: specifier: ^2.1.1 version: 2.1.1 dompurify: specifier: ^3.3.3 - version: 3.3.3 + version: 3.4.0 fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -1302,7 +1302,7 @@ importers: version: 2.2.3 jsonrepair: specifier: ^3.13.2 - version: 3.13.3 + version: 3.14.0 lexical: specifier: ^0.40.0 version: 0.40.0 @@ -1593,11 +1593,11 @@ packages: peerDependencies: react: '>=16.8.0' - '@emnapi/core@1.9.2': - resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/runtime@1.9.2': - resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -2046,6 +2046,15 @@ packages: cpu: [x64] os: [win32] + '@internationalized/date@3.12.1': + resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} + + '@internationalized/number@3.6.6': + resolution: {integrity: sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==} + + '@internationalized/string@3.2.8': + resolution: {integrity: sha512-NdbMQUSfXLYIQol5VyMtinm9pZDciiMfN7RtmSuSB78io1hqwJ0naYfxyW6vgxWBkzWymQa/3uLDlbfmshtCaA==} + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2264,8 +2273,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.6.1': - resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} + '@opentelemetry/core@2.7.0': + resolution: {integrity: sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -2294,8 +2303,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.6.1': - resolution: {integrity: sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==} + '@opentelemetry/resources@2.7.0': + resolution: {integrity: sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -2344,8 +2353,8 @@ packages: '@posthog/core@1.25.2': resolution: {integrity: sha512-h2FO7ut/BbfwpAXWpwdDHTzQgUo9ibDFEs6ZO+3cI3KPWQt5XwczK1OLAuPprcjm8T/jl0SH8jSFo5XdU4RbTg==} - '@posthog/types@1.367.0': - resolution: {integrity: sha512-FUcTEAeKhuHKyCcTQPx/sTN3s8S+PusPsiP8T/LrG/T7pDkwMfNZG0/P630JX6fT6qiW0moVvVSsaXgZDJF7wg==} + '@posthog/types@1.369.2': + resolution: {integrity: sha512-PJqkqPCFnnbCZslH2jHSvXlasRqvke6YAsYPhPALy4zy2hldor8A0O2wIlpAefEJ7fVz6wR5ZbRJzQP6nwujyw==} '@preact/signals-core@1.14.1': resolution: {integrity: sha512-vxPpfXqrwUe9lpjqfYNjAF/0RF/eFGeLgdJzdmIIZjpOnTmGmAB4BjWone562mJGMRP4frU6iZ6ei3PDsu52Ng==} @@ -2439,8 +2448,8 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@rc-component/image@1.8.1': - resolution: {integrity: sha512-JfPCijmMl+EaMvbftsEs/4VHmTyJKsZBh5ujFowSA45i9NTVYS1vuHtgpVV/QrGa27kXwbVOZriffCe/PNKuMw==} + '@rc-component/image@1.9.0': + resolution: {integrity: sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ==} peerDependencies: react: '>=16.9.0' react-dom: '>=16.9.0' @@ -2493,8 +2502,8 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@rc-component/overflow@1.0.0': - resolution: {integrity: sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==} + '@rc-component/overflow@1.0.1': + resolution: {integrity: sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==} peerDependencies: react: '>=16.9.0' react-dom: '>=16.9.0' @@ -2663,40 +2672,20 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@react-aria/focus@3.21.5': - resolution: {integrity: sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==} + '@react-aria/focus@3.22.0': + resolution: {integrity: sha512-ZfDOVuVhqDsM9mkNji3QUZ/d40JhlVgXrDkrfXylM1035QCrcTHN7m2DpbE95sU2A8EQb4wikvt5jM6K/73BPg==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/interactions@3.27.1': - resolution: {integrity: sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/ssr@3.9.10': - resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} - engines: {node: '>= 12'} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/utils@3.33.1': - resolution: {integrity: sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==} + '@react-aria/interactions@3.28.0': + resolution: {integrity: sha512-OXwdU1EWFdMxmr/K1CXNGJzmNlCClByb+PuCaqUyzBymHPCGVhawirLIon/CrIN5psh3AiWpHSh4H0WeJdVpng==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-stately/flags@3.1.2': - resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} - - '@react-stately/utils@3.11.0': - resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-types/shared@3.33.1': - resolution: {integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==} + '@react-types/shared@3.34.0': + resolution: {integrity: sha512-gp6xo/s2lX54AlTjOiqwDnxA7UW79BNvI9dB9pr3LZTzRKCd1ZA+ZbgKw/ReIiWuvvVw/8QFJpnqeeFyLocMcQ==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -2784,86 +2773,86 @@ packages: '@swc-jotai/react-refresh@0.5.0': resolution: {integrity: sha512-TXCQJ/eZ8mZXGnO0W1nK73VKGDt5dVsrPd3wSYypLOth5tSdBRtbU2opYeUJEXY4iUI5sVu1zUAmRJSJ7V+h8A==} - '@swc/core-darwin-arm64@1.15.24': - resolution: {integrity: sha512-uM5ZGfFXjtvtJ+fe448PVBEbn/CSxS3UAyLj3O9xOqKIWy3S6hPTXSPbszxkSsGDYKi+YFhzAsR4r/eXLxEQ0g==} + '@swc/core-darwin-arm64@1.15.26': + resolution: {integrity: sha512-OmcP96CFsNOwa65tamQayRcfqhNlcQ3YCWOq+0Wb+CAM4uB7kOMrXY41Gj4atthxrGhLQ9pg7Vk26iApb88idA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.24': - resolution: {integrity: sha512-fMIb/Zfn929pw25VMBhV7Ji2Dl+lCWtUPNdYJQYOke+00E5fcQ9ynxtP8+qhUo/HZc+mYQb1gJxwHM9vty+lXg==} + '@swc/core-darwin-x64@1.15.26': + resolution: {integrity: sha512-liTTTpKSv89ivIxcZ+iU1cRige9Y7JkOjVnJ2Ystzl+DsWNHqt7wLTTgm/u7gEqmmAS2JKryODLQn3q1UtFNPQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.24': - resolution: {integrity: sha512-vOkjsyjjxnoYx3hMEWcGxQrMgnNrRm6WAegBXrN8foHtDAR+zpdhpGF5a4lj1bNPgXAvmysjui8cM1ov/Clkaw==} + '@swc/core-linux-arm-gnueabihf@1.15.26': + resolution: {integrity: sha512-Y/g+m3I8CeBof5A3kWWOS6QA2HOIUytF5EeTgfwcAK+GKT/tGe7Xqo5svBtaqflU5od2zzbMTWqkinPXgRWGgA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.24': - resolution: {integrity: sha512-h/oNu+upkXJ6Cicnq7YGVj9PkdfarLCdQa8l/FlHYvfv8CEiMaeeTnpLU7gSBH/rGxosM6Qkfa/J9mThGF9CLA==} + '@swc/core-linux-arm64-gnu@1.15.26': + resolution: {integrity: sha512-19IvwyPfBN/rz9s7qXhOTQmW0922+pjpRUUvIebu+CMM75nX6YuDzHsGx8hSmn5dS89SNaMCh1lgUuXqm++6jg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.24': - resolution: {integrity: sha512-ZpF/pRe1guk6sKzQI9D1jAORtjTdNlyeXn9GDz8ophof/w2WhojRblvSDJaGe7rJjcPN8AaOkhwdRUh7q8oYIg==} + '@swc/core-linux-arm64-musl@1.15.26': + resolution: {integrity: sha512-iNlbvTIo425rkKzDLLWFJGnFXr3myETUdIDHcjuiPNZE8b0ogmcAuilC4yEJX7FSHGbnlsoJcCT2xf4b3VJmmQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-ppc64-gnu@1.15.24': - resolution: {integrity: sha512-QZEsZfisHTSJlmyChgDFNmKPb3W6Lhbfo/O76HhIngfEdnQNmukS38/VSe1feho+xkV5A5hETyCbx3sALBZKAQ==} + '@swc/core-linux-ppc64-gnu@1.15.26': + resolution: {integrity: sha512-AuuEOtG+YXKIjIUup4RsxYNklx6XVB3WKWfhxG6hnfPrn7vp89RNOLbbyyprgj6Sk7k9ulwGVTJElEvmBNPSCA==} engines: {node: '>=10'} cpu: [ppc64] os: [linux] libc: [glibc] - '@swc/core-linux-s390x-gnu@1.15.24': - resolution: {integrity: sha512-DLdJKVsJgglqQrJBuoUYNmzm3leI7kUZhLbZGHv42onfKsGf6JDS3+bzCUQfte/XOqDjh/tmmn1DR/CF/tCJFw==} + '@swc/core-linux-s390x-gnu@1.15.26': + resolution: {integrity: sha512-JcMDWQvW1BchUyRg8E0jHiTx7CQYpUr5uDEL1dnPDECrEjBEGG2ynmJ3XX70sWXql0JagqR1t3VpANYFWdUnqA==} engines: {node: '>=10'} cpu: [s390x] os: [linux] libc: [glibc] - '@swc/core-linux-x64-gnu@1.15.24': - resolution: {integrity: sha512-IpLYfposPA/XLxYOKpRfeccl1p5dDa3+okZDHHTchBkXEaVCnq5MADPmIWwIYj1tudt7hORsEHccG5no6IUQRw==} + '@swc/core-linux-x64-gnu@1.15.26': + resolution: {integrity: sha512-FW7V7Mbpq4+PA7BiAq76LJs8MdNuUSylyuRVfQRkhIyeWadFroZ+KOPgjku8Z/fXzngxBRvsk+PGGB0t8mGcjA==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-x64-musl@1.15.24': - resolution: {integrity: sha512-JHy3fMSc0t/EPWgo74+OK5TGr51aElnzqfUPaiRf2qJ/BfX5CUCfMiWVBuhI7qmVMBnk1jTRnL/xZnOSHDPLYg==} + '@swc/core-linux-x64-musl@1.15.26': + resolution: {integrity: sha512-w8erqMHsVcdGwUfJxF6LaiTuPoKnyLOcUbhLcxiXrlLt5MLjtlgcIeUY/NWK/oPoyqkgH+/i8pOJnMTxvl83ZQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.24': - resolution: {integrity: sha512-Txj+qUH1z2bUd1P3JvwByfjKFti3cptlAxhWgmunBUUxy/IW3CXLZ6l6Gk4liANadKkU71nIU1X30Z5vpMT3BA==} + '@swc/core-win32-arm64-msvc@1.15.26': + resolution: {integrity: sha512-uDCWCNpUiqkbvPmsuPUTn/P7ag9SqNXD2JT/W3dUu7yZ2krzN+nmmoQ2xRX63/J6RYiHI7aT4jo7Z++lsljlPA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.24': - resolution: {integrity: sha512-15D/nl3XwrhFpMv+MADFOiVwv3FvH9j8c6Rf8EXBT3Q5LoMh8YnDnSgPYqw1JzPnksvsBX6QPXLiPqmcR/Z4qQ==} + '@swc/core-win32-ia32-msvc@1.15.26': + resolution: {integrity: sha512-2k1ax1QmmqLEnpC0uRCw7OXhBfyvdPqERBXupDasjYbChT6ZSO/uha28Bp38cw0viKIG79L27aTDkbkABsMW3w==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.24': - resolution: {integrity: sha512-PR0PlTlPra2JbaDphrOAzm6s0v9rA0F17YzB+XbWD95B4g2cWcZY9LAeTa4xll70VLw9Jr7xBrlohqlQmelMFQ==} + '@swc/core-win32-x64-msvc@1.15.26': + resolution: {integrity: sha512-aUuYecSEGa4SUSdyCWaI/vk8jdseifYnsF1GZQx2+piL8GIuT/5QrVcFfmes4Iwy7FIVXxtzD063z/FfpZ7K7w==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.24': - resolution: {integrity: sha512-5Hj8aNasue7yusUt8LGCUe/AjM7RMAce8ZoyDyiFwx7Al+GbYKL+yE7g4sJk8vEr1dKIkTRARkNIJENc4CjkBQ==} + '@swc/core@1.15.26': + resolution: {integrity: sha512-tglZGyx8N5PC+x1Nd/JrZxqpqlcZoSuG9gTDKO6AuFToFiVB3uS8HvbKFuO7g3lJzvFf9riAb94xs9HU2UhAHQ==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -2901,14 +2890,14 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-virtual@3.13.23': - resolution: {integrity: sha512-XnMRnHQ23piOVj2bzJqHrRrLg4r+F86fuBcwteKfbIjJrtGxb4z7tIvPVAe4B+4UVwo9G4Giuz5fmapcrnZ0OQ==} + '@tanstack/react-virtual@3.13.24': + resolution: {integrity: sha512-aIJvz5OSkhNIhZIpYivrxrPTKYsjW9Uzy+sP/mx0S3sev2HyvPb7xmjbYvokzEpfgYHy/HjzJ2zFAETuUfgCpg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.13.23': - resolution: {integrity: sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==} + '@tanstack/virtual-core@3.14.0': + resolution: {integrity: sha512-JLANqGy/D6k4Ujmh8Tr25lGimuOXNiaVyXaCAZS0W+1390sADdGnyUdSWNIfd49gebtIxGMij4IktRVzrdr12Q==} '@tremor/react@3.18.7': resolution: {integrity: sha512-nmqvf/1m0GB4LXc7v2ftdfSLoZhy5WLrhV6HNf0SOriE6/l8WkYeWuhQq8QsBjRi94mUIKLJ/VC3/Y/pj6VubQ==} @@ -3145,11 +3134,11 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@typescript-eslint/eslint-plugin@8.58.1': - resolution: {integrity: sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==} + '@typescript-eslint/eslint-plugin@8.58.2': + resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.1 + '@typescript-eslint/parser': ^8.58.2 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -3163,15 +3152,15 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.58.1': - resolution: {integrity: sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==} + '@typescript-eslint/parser@8.58.2': + resolution: {integrity: sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.58.1': - resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} + '@typescript-eslint/project-service@8.58.2': + resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -3180,18 +3169,18 @@ packages: resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/scope-manager@8.58.1': - resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} + '@typescript-eslint/scope-manager@8.58.2': + resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.58.1': - resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} + '@typescript-eslint/tsconfig-utils@8.58.2': + resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.58.1': - resolution: {integrity: sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==} + '@typescript-eslint/type-utils@8.58.2': + resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3201,8 +3190,8 @@ packages: resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/types@8.58.1': - resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} + '@typescript-eslint/types@8.58.2': + resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@6.21.0': @@ -3214,14 +3203,14 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.58.1': - resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} + '@typescript-eslint/typescript-estree@8.58.2': + resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.58.1': - resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} + '@typescript-eslint/utils@8.58.2': + resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3231,8 +3220,8 @@ packages: resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/visitor-keys@8.58.1': - resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} + '@typescript-eslint/visitor-keys@8.58.2': + resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -3478,8 +3467,8 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - antd@6.3.5: - resolution: {integrity: sha512-8BPz9lpZWQm42PTx7yL4KxWAotVuqINiKcoYRcLtdd5BFmAcAZicVyFTnBJyRDlzGZFZeRW3foGu6jXYFnej6Q==} + antd@6.3.6: + resolution: {integrity: sha512-zdCYjusrTUn4gNxEg4PH8MWlfuXYbKfuGOkjgZ0Rg6DpWbIVmG/MwvsZ5yvG6z3Y6UI/gzYpaQ82iTt4KdbeaA==} peerDependencies: react: '>=18.0.0' react-dom: '>=18.0.0' @@ -3565,8 +3554,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.2: - resolution: {integrity: sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==} + axe-core@4.11.3: + resolution: {integrity: sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg==} engines: {node: '>=4'} axios@1.13.5: @@ -3579,8 +3568,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.10.18: - resolution: {integrity: sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==} + baseline-browser-mapping@2.10.19: + resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==} engines: {node: '>=6.0.0'} hasBin: true @@ -3626,8 +3615,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001787: - resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} + caniuse-lite@1.0.30001788: + resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3748,8 +3737,8 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crisp-sdk-web@1.0.28: - resolution: {integrity: sha512-UIT3gY8JXkqzSqCJoNgIbv3yCfZ8MxRpcOPJJLFGYWqYQ/oYXz4wfrLAZbxPiZ+BvmCgq2pOCJeSsdCopRcbDw==} + crisp-sdk-web@1.1.0: + resolution: {integrity: sha512-CsNGa+ZoYQYti2RD5oNhRlNrZF+iXyZEL3nMAOBJisdKqseNA7GSuH7zLb9uQcQ63SajbYhqzTD3oHx/PoJgPw==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -4037,8 +4026,8 @@ packages: dompurify@3.2.7: resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} - dompurify@3.3.3: - resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dompurify@3.4.0: + resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==} dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} @@ -4056,8 +4045,8 @@ packages: engines: {node: '>=14'} hasBin: true - electron-to-chromium@1.5.335: - resolution: {integrity: sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==} + electron-to-chromium@1.5.340: + resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -4209,11 +4198,11 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-hooks@7.0.1: - resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + eslint-plugin-react-hooks@7.1.0: + resolution: {integrity: sha512-LDicyhrRFrIaheDYryeM2W8gWyZXnAs4zIr2WVPiOSeTmIu2RjR4x/9N0xLaRWZ+9hssBDGo3AadcohuzAvSvg==} engines: {node: '>=18'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} @@ -4464,8 +4453,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.13.7: - resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -4897,9 +4886,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -4925,8 +4911,8 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jsonrepair@3.13.3: - resolution: {integrity: sha512-BTznj0owIt2CBAH/LTo7+1I5pMvl1e1033LRl/HUowlZmJOIhzC0zbX5bxMngLkfT4WnzPP26QnW5wMr2g9tsQ==} + jsonrepair@3.14.0: + resolution: {integrity: sha512-tWPGKMZf/8UPim+fcW2EfcQ/d/7aKUrP6IECz9G3Tu6Q5dX0orSleqJ9z6sSw7qrQkjF8/Edo4DvsWBZ8H+HNg==} hasBin: true jss-plugin-camel-case@10.10.0: @@ -5158,6 +5144,10 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -5500,12 +5490,12 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.9: - resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.367.0: - resolution: {integrity: sha512-jWNwB8XjlVUC9PbGaIlmsyohUDMBrwf7cvLuOY3lIOmWVO3L6VxTE3GZShjxpFKQtmWcPxFbf1hcbct1YCb6xg==} + posthog-js@1.369.2: + resolution: {integrity: sha512-pY+SvNvRp3C2XW80h/jwVLTgoruK15C6klo9bYYoO6DCK9EbcwS6YzjgxBHx1dIN0XBZM3KWJPmuaSimU65HQQ==} preact@10.29.1: resolution: {integrity: sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==} @@ -5558,8 +5548,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + protobufjs@7.5.5: + resolution: {integrity: sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==} engines: {node: '>=12.0.0'} proxy-from-env@1.1.0: @@ -5601,6 +5591,12 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' + react-aria@3.48.0: + resolution: {integrity: sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-day-picker@8.10.1: resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} peerDependencies: @@ -5676,6 +5672,11 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-stately@3.46.0: + resolution: {integrity: sha512-OdxhWvHgs2L4OJGIs7hnuTr5WjjMM6enhNEAMRqiekhF8+ITvA2LRwNftOZwcogaoCslGYq5S2VQTQwnm0GbCA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-syntax-highlighter@16.1.1: resolution: {integrity: sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==} engines: {node: '>= 16.20.2'} @@ -6222,8 +6223,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.58.1: - resolution: {integrity: sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==} + typescript-eslint@8.58.2: + resolution: {integrity: sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -6359,8 +6360,8 @@ packages: resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} - webpack@5.106.1: - resolution: {integrity: sha512-EW8af29ak8Oaf4T8k8YsajjrDBDYgnKZ5er6ljWFJsXABfTNowQfvHLftwcepVgdz+IoLSdEAbBiM9DFXoll9w==} + webpack@5.106.2: + resolution: {integrity: sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -6507,7 +6508,7 @@ snapshots: react-dom: 19.2.5(react@19.2.5) throttle-debounce: 5.0.2 - '@ant-design/x@2.5.0(antd@6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@ant-design/x@2.5.0(antd@6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@ant-design/colors': 8.0.1 '@ant-design/cssinjs': 2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -6518,7 +6519,7 @@ snapshots: '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - antd: 6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + antd: 6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) clsx: 2.1.1 lodash.throttle: 4.1.1 mermaid: 11.14.0 @@ -6692,13 +6693,13 @@ snapshots: react: 19.2.5 tslib: 2.8.1 - '@emnapi/core@1.9.2': + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.2': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true @@ -6913,9 +6914,9 @@ snapshots: '@headlessui/react@2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.26.28(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@react-aria/focus': 3.21.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@react-aria/interactions': 3.27.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@tanstack/react-virtual': 3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@react-aria/focus': 3.22.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@react-aria/interactions': 3.28.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tanstack/react-virtual': 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) @@ -7033,7 +7034,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.9.2 + '@emnapi/runtime': 1.10.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -7045,6 +7046,18 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@internationalized/date@3.12.1': + dependencies: + '@swc/helpers': 0.5.21 + + '@internationalized/number@3.6.6': + dependencies: + '@swc/helpers': 0.5.21 + + '@internationalized/string@3.2.8': + dependencies: + '@swc/helpers': 0.5.21 + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.10 @@ -7269,8 +7282,8 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.1 optional: true @@ -7338,7 +7351,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 @@ -7367,7 +7380,7 @@ snapshots: '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.1) - protobufjs: 7.5.4 + protobufjs: 7.5.5 '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.1)': dependencies: @@ -7375,10 +7388,10 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.7.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.1)': @@ -7418,7 +7431,7 @@ snapshots: '@posthog/core@1.25.2': {} - '@posthog/types@1.367.0': {} + '@posthog/types@1.369.2': {} '@preact/signals-core@1.14.1': {} @@ -7522,7 +7535,7 @@ snapshots: react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - '@rc-component/image@1.8.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@rc-component/image@1.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -7560,7 +7573,7 @@ snapshots: '@rc-component/menu@1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/overflow': 1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) clsx: 2.1.1 @@ -7592,7 +7605,7 @@ snapshots: react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - '@rc-component/overflow@1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@rc-component/overflow@1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -7610,7 +7623,7 @@ snapshots: '@rc-component/picker@1.9.1(date-fns@3.6.0)(dayjs@1.11.20)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/overflow': 1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -7665,7 +7678,7 @@ snapshots: '@rc-component/select@1.6.15(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/overflow': 1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/virtual-list': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -7792,52 +7805,22 @@ snapshots: react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - '@react-aria/focus@3.21.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@react-aria/focus@3.22.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@react-aria/interactions': 3.27.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@react-aria/utils': 3.33.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@react-types/shared': 3.33.1(react@19.2.5) '@swc/helpers': 0.5.21 - clsx: 2.1.1 react: 19.2.5 + react-aria: 3.48.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react-dom: 19.2.5(react@19.2.5) - '@react-aria/interactions@3.27.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@react-aria/interactions@3.28.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@react-aria/ssr': 3.9.10(react@19.2.5) - '@react-aria/utils': 3.33.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@react-stately/flags': 3.1.2 - '@react-types/shared': 3.33.1(react@19.2.5) + '@react-types/shared': 3.34.0(react@19.2.5) '@swc/helpers': 0.5.21 react: 19.2.5 + react-aria: 3.48.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react-dom: 19.2.5(react@19.2.5) - '@react-aria/ssr@3.9.10(react@19.2.5)': - dependencies: - '@swc/helpers': 0.5.21 - react: 19.2.5 - - '@react-aria/utils@3.33.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@react-aria/ssr': 3.9.10(react@19.2.5) - '@react-stately/flags': 3.1.2 - '@react-stately/utils': 3.11.0(react@19.2.5) - '@react-types/shared': 3.33.1(react@19.2.5) - '@swc/helpers': 0.5.21 - clsx: 2.1.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - - '@react-stately/flags@3.1.2': - dependencies: - '@swc/helpers': 0.5.21 - - '@react-stately/utils@3.11.0(react@19.2.5)': - dependencies: - '@swc/helpers': 0.5.21 - react: 19.2.5 - - '@react-types/shared@3.33.1(react@19.2.5)': + '@react-types/shared@3.34.0(react@19.2.5)': dependencies: react: 19.2.5 @@ -7935,59 +7918,59 @@ snapshots: '@swc-jotai/react-refresh@0.5.0': {} - '@swc/core-darwin-arm64@1.15.24': + '@swc/core-darwin-arm64@1.15.26': optional: true - '@swc/core-darwin-x64@1.15.24': + '@swc/core-darwin-x64@1.15.26': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.24': + '@swc/core-linux-arm-gnueabihf@1.15.26': optional: true - '@swc/core-linux-arm64-gnu@1.15.24': + '@swc/core-linux-arm64-gnu@1.15.26': optional: true - '@swc/core-linux-arm64-musl@1.15.24': + '@swc/core-linux-arm64-musl@1.15.26': optional: true - '@swc/core-linux-ppc64-gnu@1.15.24': + '@swc/core-linux-ppc64-gnu@1.15.26': optional: true - '@swc/core-linux-s390x-gnu@1.15.24': + '@swc/core-linux-s390x-gnu@1.15.26': optional: true - '@swc/core-linux-x64-gnu@1.15.24': + '@swc/core-linux-x64-gnu@1.15.26': optional: true - '@swc/core-linux-x64-musl@1.15.24': + '@swc/core-linux-x64-musl@1.15.26': optional: true - '@swc/core-win32-arm64-msvc@1.15.24': + '@swc/core-win32-arm64-msvc@1.15.26': optional: true - '@swc/core-win32-ia32-msvc@1.15.24': + '@swc/core-win32-ia32-msvc@1.15.26': optional: true - '@swc/core-win32-x64-msvc@1.15.24': + '@swc/core-win32-x64-msvc@1.15.26': optional: true - '@swc/core@1.15.24(@swc/helpers@0.5.21)': + '@swc/core@1.15.26(@swc/helpers@0.5.21)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.26 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.24 - '@swc/core-darwin-x64': 1.15.24 - '@swc/core-linux-arm-gnueabihf': 1.15.24 - '@swc/core-linux-arm64-gnu': 1.15.24 - '@swc/core-linux-arm64-musl': 1.15.24 - '@swc/core-linux-ppc64-gnu': 1.15.24 - '@swc/core-linux-s390x-gnu': 1.15.24 - '@swc/core-linux-x64-gnu': 1.15.24 - '@swc/core-linux-x64-musl': 1.15.24 - '@swc/core-win32-arm64-msvc': 1.15.24 - '@swc/core-win32-ia32-msvc': 1.15.24 - '@swc/core-win32-x64-msvc': 1.15.24 + '@swc/core-darwin-arm64': 1.15.26 + '@swc/core-darwin-x64': 1.15.26 + '@swc/core-linux-arm-gnueabihf': 1.15.26 + '@swc/core-linux-arm64-gnu': 1.15.26 + '@swc/core-linux-arm64-musl': 1.15.26 + '@swc/core-linux-ppc64-gnu': 1.15.26 + '@swc/core-linux-s390x-gnu': 1.15.26 + '@swc/core-linux-x64-gnu': 1.15.26 + '@swc/core-linux-x64-musl': 1.15.26 + '@swc/core-win32-arm64-msvc': 1.15.26 + '@swc/core-win32-ia32-msvc': 1.15.26 + '@swc/core-win32-x64-msvc': 1.15.26 '@swc/helpers': 0.5.21 '@swc/counter@0.1.3': {} @@ -8020,13 +8003,13 @@ snapshots: '@tanstack/query-core': 5.99.0 react: 19.2.5 - '@tanstack/react-virtual@3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@tanstack/react-virtual@3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@tanstack/virtual-core': 3.13.23 + '@tanstack/virtual-core': 3.14.0 react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - '@tanstack/virtual-core@3.13.23': {} + '@tanstack/virtual-core@3.14.0': {} '@tremor/react@3.18.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: @@ -8281,14 +8264,14 @@ snapshots: '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/type-utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/type-utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.58.2 eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -8310,22 +8293,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': + '@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.58.2 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.1(typescript@5.8.3)': + '@typescript-eslint/project-service@8.58.2(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.8.3) - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.8.3) + '@typescript-eslint/types': 8.58.2 debug: 4.4.3 typescript: 5.8.3 transitivePeerDependencies: @@ -8336,20 +8319,20 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - '@typescript-eslint/scope-manager@8.58.1': + '@typescript-eslint/scope-manager@8.58.2': dependencies: - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 - '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.58.2(typescript@5.8.3)': dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.8.3) + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.8.3) @@ -8359,7 +8342,7 @@ snapshots: '@typescript-eslint/types@6.21.0': {} - '@typescript-eslint/types@8.58.1': {} + '@typescript-eslint/types@8.58.2': {} '@typescript-eslint/typescript-estree@6.21.0(typescript@5.8.3)': dependencies: @@ -8376,12 +8359,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.58.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.58.2(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.58.1(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.8.3) - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/project-service': 8.58.2(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.8.3) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 debug: 4.4.3 minimatch: 5.1.8 semver: 7.7.4 @@ -8391,12 +8374,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': + '@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: @@ -8407,9 +8390,9 @@ snapshots: '@typescript-eslint/types': 6.21.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.58.1': + '@typescript-eslint/visitor-keys@8.58.2': dependencies: - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/types': 8.58.2 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} @@ -8622,7 +8605,7 @@ snapshots: ansi-styles@5.2.0: {} - antd@6.3.5(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + antd@6.3.6(date-fns@3.6.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: '@ant-design/colors': 8.0.1 '@ant-design/cssinjs': 2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -8639,7 +8622,7 @@ snapshots: '@rc-component/drawer': 1.4.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/dropdown': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/form': 1.8.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@rc-component/image': 1.8.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/image': 1.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/input': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/input-number': 1.6.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@rc-component/mentions': 1.6.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -8773,21 +8756,21 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.20(postcss@8.5.9): + autoprefixer@10.4.20(postcss@8.5.10): dependencies: browserslist: 4.28.2 - caniuse-lite: 1.0.30001787 + caniuse-lite: 1.0.30001788 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.9 + postcss: 8.5.10 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.2: {} + axe-core@4.11.3: {} axios@1.13.5: dependencies: @@ -8801,7 +8784,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.10.18: {} + baseline-browser-mapping@2.10.19: {} binary-extensions@2.3.0: {} @@ -8819,9 +8802,9 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.18 - caniuse-lite: 1.0.30001787 - electron-to-chromium: 1.5.335 + baseline-browser-mapping: 2.10.19 + caniuse-lite: 1.0.30001788 + electron-to-chromium: 1.5.340 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -8848,7 +8831,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001787: {} + caniuse-lite@1.0.30001788: {} ccount@2.0.1: {} @@ -8961,7 +8944,7 @@ snapshots: create-require@1.1.1: {} - crisp-sdk-web@1.0.28: {} + crisp-sdk-web@1.1.0: {} cross-spawn@7.0.6: dependencies: @@ -9268,7 +9251,7 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 - dompurify@3.3.3: + dompurify@3.4.0: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -9289,7 +9272,7 @@ snapshots: minimatch: 5.1.8 semver: 7.7.4 - electron-to-chromium@1.5.335: {} + electron-to-chromium@1.5.340: {} emoji-regex@9.2.2: {} @@ -9442,12 +9425,12 @@ snapshots: dependencies: '@next/eslint-plugin-next': 15.5.14 '@rushstack/eslint-patch': 1.16.1 - '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4(jiti@2.6.1)) @@ -9475,28 +9458,28 @@ snapshots: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) - get-tsconfig: 4.13.7 + get-tsconfig: 4.14.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -9507,7 +9490,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9519,7 +9502,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -9531,7 +9514,7 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.11.2 + axe-core: 4.11.3 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -9558,7 +9541,7 @@ snapshots: dependencies: eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-react-hooks@7.1.0(eslint@9.39.4(jiti@2.6.1)): dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.2 @@ -9888,7 +9871,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.13.7: + get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -10299,8 +10282,6 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -10319,7 +10300,7 @@ snapshots: jsonpointer@5.0.1: {} - jsonrepair@3.13.3: {} + jsonrepair@3.14.0: {} jss-plugin-camel-case@10.10.0: dependencies: @@ -10562,7 +10543,7 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.20 - dompurify: 3.3.3 + dompurify: 3.4.0 katex: 0.16.45 khroma: 2.1.0 lodash-es: 4.18.1 @@ -10598,6 +10579,8 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -10665,7 +10648,7 @@ snapshots: dependencies: '@next/env': 15.5.14 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001787 + caniuse-lite: 1.0.30001788 postcss: 8.4.31 react: 19.2.5 react-dom: 19.2.5(react@19.2.5) @@ -10879,30 +10862,30 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.9): + postcss-import@15.1.0(postcss@8.5.10): dependencies: - postcss: 8.5.9 + postcss: 8.5.10 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-js@4.1.0(postcss@8.5.9): + postcss-js@4.1.0(postcss@8.5.10): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.9 + postcss: 8.5.10 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.9)(tsx@4.21.0)(yaml@2.8.3): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0)(yaml@2.8.3): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.5.9 + postcss: 8.5.10 tsx: 4.21.0 yaml: 2.8.3 - postcss-nested@6.2.0(postcss@8.5.9): + postcss-nested@6.2.0(postcss@8.5.10): dependencies: - postcss: 8.5.9 + postcss: 8.5.10 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.1.2: @@ -10918,23 +10901,23 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.9: + postcss@8.5.10: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.367.0: + posthog-js@1.369.2: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) '@posthog/core': 1.25.2 - '@posthog/types': 1.367.0 + '@posthog/types': 1.369.2 core-js: 3.49.0 - dompurify: 3.3.3 + dompurify: 3.4.0 fflate: 0.4.8 preact: 10.29.1 query-selector-shadow-dom: 1.0.1 @@ -10990,7 +10973,7 @@ snapshots: proto-list@1.2.4: {} - protobufjs@7.5.4: + protobufjs@7.5.5: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -11042,6 +11025,20 @@ snapshots: react: 19.2.5 react-dom: 19.2.5(react@19.2.5) + react-aria@3.48.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + '@internationalized/date': 3.12.1 + '@internationalized/number': 3.6.6 + '@internationalized/string': 3.2.8 + '@react-types/shared': 3.34.0(react@19.2.5) + '@swc/helpers': 0.5.21 + aria-hidden: 1.2.6 + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + react-stately: 3.46.0(react@19.2.5) + use-sync-external-store: 1.6.0(react@19.2.5) + react-day-picker@8.10.1(date-fns@3.6.0)(react@19.2.5): dependencies: date-fns: 3.6.0 @@ -11123,6 +11120,16 @@ snapshots: react-dom: 19.2.5(react@19.2.5) react-transition-group: 4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react-stately@3.46.0(react@19.2.5): + dependencies: + '@internationalized/date': 3.12.1 + '@internationalized/number': 3.6.6 + '@internationalized/string': 3.2.8 + '@react-types/shared': 3.34.0(react@19.2.5) + '@swc/helpers': 0.5.21 + react: 19.2.5 + use-sync-external-store: 1.6.0(react@19.2.5) + react-syntax-highlighter@16.1.1(react@19.2.5): dependencies: '@babel/runtime': 7.29.2 @@ -11592,11 +11599,11 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swc-loader@0.2.7(@swc/core@1.15.24(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21))): + swc-loader@0.2.7(@swc/core@1.15.26(@swc/helpers@0.5.21))(webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21))): dependencies: - '@swc/core': 1.15.24(@swc/helpers@0.5.21) + '@swc/core': 1.15.26(@swc/helpers@0.5.21) '@swc/counter': 0.1.3 - webpack: 5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21)) + webpack: 5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21)) swr-devtools@1.3.2(react@19.2.5)(swr@2.4.1(react@19.2.5)): dependencies: @@ -11640,11 +11647,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.9 - postcss-import: 15.1.0(postcss@8.5.9) - postcss-js: 4.1.0(postcss@8.5.9) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.9)(tsx@4.21.0)(yaml@2.8.3) - postcss-nested: 6.2.0(postcss@8.5.9) + postcss: 8.5.10 + postcss-import: 15.1.0(postcss@8.5.10) + postcss-js: 4.1.0(postcss@8.5.10) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0)(yaml@2.8.3) + postcss-nested: 6.2.0(postcss@8.5.10) postcss-selector-parser: 6.1.2 resolve: 1.22.12 sucrase: 3.35.1 @@ -11654,15 +11661,15 @@ snapshots: tapable@2.3.2: {} - terser-webpack-plugin@5.4.0(@swc/core@1.15.24(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21))): + terser-webpack-plugin@5.4.0(@swc/core@1.15.26(@swc/helpers@0.5.21))(webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21))): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.46.1 - webpack: 5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21)) + webpack: 5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21)) optionalDependencies: - '@swc/core': 1.15.24(@swc/helpers@0.5.21) + '@swc/core': 1.15.26(@swc/helpers@0.5.21) terser@5.46.1: dependencies: @@ -11722,7 +11729,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.15.24(@swc/helpers@0.5.21))(@types/node@20.19.39)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.15.26(@swc/helpers@0.5.21))(@types/node@20.19.39)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 @@ -11740,7 +11747,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.15.24(@swc/helpers@0.5.21) + '@swc/core': 1.15.26(@swc/helpers@0.5.21) tsconfig-paths@3.15.0: dependencies: @@ -11760,7 +11767,7 @@ snapshots: tsx@4.21.0: dependencies: esbuild: 0.27.7 - get-tsconfig: 4.13.7 + get-tsconfig: 4.14.0 optionalDependencies: fsevents: 2.3.3 @@ -11817,12 +11824,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3): + typescript-eslint@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.8.3) + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: @@ -12023,7 +12030,7 @@ snapshots: webpack-sources@3.3.4: {} - webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21)): + webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21)): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -12041,13 +12048,12 @@ snapshots: events: 3.3.0 glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 loader-runner: 4.3.1 - mime-types: 2.1.35 + mime-db: 1.54.0 neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.2 - terser-webpack-plugin: 5.4.0(@swc/core@1.15.24(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.24(@swc/helpers@0.5.21))) + terser-webpack-plugin: 5.4.0(@swc/core@1.15.26(@swc/helpers@0.5.21))(webpack@5.106.2(@swc/core@1.15.26(@swc/helpers@0.5.21))) watchpack: 2.5.1 webpack-sources: 3.3.4 transitivePeerDependencies: From 3aeac881a136a570dafa1df864cb654b65e04f33 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 20 Apr 2026 16:45:38 +0600 Subject: [PATCH 03/13] expand evaluator drawers by default when opened from evaluation contexts --- .../NewEvaluation/Components/CreateEvaluatorDrawer/state.ts | 2 +- .../src/components/WorkflowRevisionDrawer/store.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/web/oss/src/components/pages/evaluations/NewEvaluation/Components/CreateEvaluatorDrawer/state.ts b/web/oss/src/components/pages/evaluations/NewEvaluation/Components/CreateEvaluatorDrawer/state.ts index 20049aa832..75f5fc9d25 100644 --- a/web/oss/src/components/pages/evaluations/NewEvaluation/Components/CreateEvaluatorDrawer/state.ts +++ b/web/oss/src/components/pages/evaluations/NewEvaluation/Components/CreateEvaluatorDrawer/state.ts @@ -30,7 +30,7 @@ export const drawerExpandedAtom = atomWithReset(false) export const openDrawerWithEntityAtom = atom(null, (_get, set, entityId: string) => { set(drawerEntityIdAtom, entityId) set(drawerOpenAtom, true) - set(drawerExpandedAtom, false) + set(drawerExpandedAtom, true) set(playgroundController.actions.setEntityIds, [entityId]) }) diff --git a/web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts b/web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts index 167f8acd05..337b7374f4 100644 --- a/web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts +++ b/web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts @@ -77,9 +77,12 @@ export const workflowRevisionDrawerAtom = atom((get) => ({ /** Open the drawer */ export const openWorkflowRevisionDrawerAtom = atom(null, (get, set, params: OpenDrawerParams) => { + const opensExpanded = + params.context === "evaluator-view" || params.context === "evaluator-create" + set(workflowRevisionDrawerEntityIdAtom, params.entityId) set(workflowRevisionDrawerOpenAtom, true) - set(workflowRevisionDrawerExpandedAtom, false) + set(workflowRevisionDrawerExpandedAtom, opensExpanded) set(workflowRevisionDrawerContextAtom, params.context) if (params.navigationIds !== undefined) { set(workflowRevisionDrawerNavigationIdsAtom, params.navigationIds) From e20ae4f5ee3e82117ac633688997646e6bcbb118 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 20 Apr 2026 13:46:49 +0200 Subject: [PATCH 04/13] [feat] Add beautified JSON view mode with structured rendering for trace spans - Replace `rendered-json` mode with `beautified-json` and `decoded-json` modes - Extract JSON parsing/decoding helpers to `decodedJsonHelpers.ts` - Move beautified rendering logic to `BeautifiedJsonView.tsx` component - Add `VIEW_MODES.md` documentation for all display modes - Update mode selection logic to support `viewModePreset="message"` preference - Normalize escaped line breaks and unwrap nested stringified JSON in --- .../DrillInView/BeautifiedJsonView.tsx | 589 ++++++++++++++ .../DrillInView/TraceSpanDrillInView.tsx | 721 ++---------------- .../src/components/DrillInView/VIEW_MODES.md | 133 ++++ .../DrillInView/decodedJsonHelpers.ts | 197 +++++ .../components/AccordionTreePanel.tsx | 237 ++---- 5 files changed, 1077 insertions(+), 800 deletions(-) create mode 100644 web/oss/src/components/DrillInView/BeautifiedJsonView.tsx create mode 100644 web/oss/src/components/DrillInView/VIEW_MODES.md create mode 100644 web/oss/src/components/DrillInView/decodedJsonHelpers.ts diff --git a/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx b/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx new file mode 100644 index 0000000000..4ada672cfb --- /dev/null +++ b/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx @@ -0,0 +1,589 @@ +import {memo, useEffect, useLayoutEffect, useMemo} from "react" + +import { + Editor as EditorWrapper, + EditorProvider, + useLexicalComposerContext, + SET_MARKDOWN_VIEW, +} from "@agenta/ui" +import { + extractChatMessages, + normalizeChatMessages, + ROLE_COLOR_CLASSES, + DEFAULT_ROLE_COLOR_CLASS, +} from "@agenta/ui/cell-renderers" + +/** + * "Beautified JSON" view. + * + * ## What this mode does + * + * Reshapes data for readability and renders it OUTSIDE the JSON editor as a + * component tree: + * + * - chat-like arrays and single messages → chat bubbles (role label + content + * editor with markdown support) + * - plain objects → per-key labeled variable fields (recursive; short leaves + * inline as `key: value`) + * - known envelope patterns (AI SDK `{type: "text"|"tool-call"|"tool-result"}`) + * are unwrapped into their payload + * - noisy provider-metadata keys (`providerOptions`, `rawHeaders`, `rawCall`, + * `rawResponse`, `logprobs`, etc.) are stripped from objects + * + * ## What this mode is NOT + * + * "Beautified JSON" is not JSON — it hides fields, restructures values, and + * renders via custom React components. When the exact shape of the wire data + * matters, use "JSON" (faithful) or "Decoded JSON" (faithful shape with + * string decoding, see `decodedJsonHelpers.ts`). + * + * This mode is the default when `viewModePreset="message"`, because that + * preset is used specifically for chat-style data where the reshape is + * desirable. Everywhere else, "Decoded JSON" is the default. + * + * ## Authoritative reference + * + * `VIEW_MODES.md` in this folder documents every view mode and the rules + * for choosing a default. Keep it in sync when you change behavior here. + */ + +const METADATA_NOISE_KEYS = new Set([ + "providerOptions", + "experimental_providerMetadata", + "rawHeaders", + "caller", + "messageId", + "toolCallId", + "rawCall", + "rawResponse", + "logprobs", +]) + +const EDITOR_RESET_CLASSES = + "!min-h-0 [&_.editor-inner]:!border-0 [&_.editor-inner]:!rounded-none [&_.editor-inner]:!min-h-0 [&_.editor-container]:!bg-transparent [&_.editor-container]:!min-h-0 [&_.editor-input]:!min-h-0 [&_.editor-input]:!px-0 [&_.editor-input]:!py-0 [&_.editor-paragraph]:!mb-1 [&_.editor-paragraph:last-child]:!mb-0 [&_.agenta-editor-wrapper]:!min-h-0" + +const DEFAULT_MAX_RENDER_DEPTH = 5 + +/** + * Simplify a value by unwrapping known envelope patterns. + * Returns the simplified value, or the original if no simplification applies. + */ +const simplifyValue = (value: unknown): unknown => { + if (!value || typeof value !== "object") return value + + const rec = value as Record + + // AI SDK text part: {type: "text", text: "hello"} → "hello" + if (rec.type === "text" && typeof rec.text === "string") { + return rec.text + } + + // AI SDK tool-call: {type: "tool-call", toolName: "fn", input: {...}} → "fn({...})" + if (rec.type === "tool-call" && typeof rec.toolName === "string") { + const args = rec.input ?? rec.args + if (!args || (typeof args === "object" && Object.keys(args as object).length === 0)) { + return `${rec.toolName}()` + } + try { + return `${rec.toolName}(${JSON.stringify(args, null, 2)})` + } catch { + return `${rec.toolName}(...)` + } + } + + // AI SDK tool-result envelope: {type: "tool-result", output: {type: "json", value: X}} → X + if (rec.type === "tool-result" && rec.output !== undefined) { + const output = rec.output as Record | undefined + if (output && typeof output === "object" && output.value !== undefined) { + return output.value + } + return rec.output + } + + // Single-element array of a simplifiable item + if (Array.isArray(value) && value.length === 1) { + const simplified = simplifyValue(value[0]) + if (simplified !== value[0]) return simplified + } + + // Multi-element array: simplify each element + if (Array.isArray(value) && value.length > 1) { + const simplified = value.map(simplifyValue) + const changed = simplified.some((s, i) => s !== value[i]) + if (changed) { + if (simplified.every((s) => typeof s === "string")) { + return (simplified as string[]).join("\n") + } + return simplified + } + } + + // Strip metadata noise keys from objects + if (!Array.isArray(value)) { + const keys = Object.keys(rec) + const noiseKeys = keys.filter((k) => METADATA_NOISE_KEYS.has(k)) + if (noiseKeys.length > 0 && noiseKeys.length < keys.length) { + const cleaned: Record = {} + for (const k of keys) { + if (!METADATA_NOISE_KEYS.has(k)) { + cleaned[k] = rec[k] + } + } + const cleanedKeys = Object.keys(cleaned) + if (cleanedKeys.length === 1) { + return cleaned[cleanedKeys[0]] + } + return cleaned + } + } + + return value +} + +const formatLabel = (key: string): string => + key + .replace(/_/g, " ") + .replace(/([a-z])([A-Z])/g, "$1 $2") + .replace(/\b\w/g, (c) => c.toUpperCase()) + +const isShortLeaf = (value: unknown): boolean => { + if (value === null || value === undefined) return true + if (typeof value === "boolean" || typeof value === "number") return true + if (typeof value === "string" && value.length <= 120 && !value.includes("\n")) return true + return false +} + +const InlineKeyValue = memo(function InlineKeyValue({ + label, + value, +}: { + label: string + value: string +}) { + return ( +
+ {label} + {value || "—"} +
+ ) +}) + +const MarkdownModeSync = ({isMarkdownView}: {isMarkdownView: boolean}) => { + const [editor] = useLexicalComposerContext() + + useLayoutEffect(() => { + editor.dispatchCommand(SET_MARKDOWN_VIEW, isMarkdownView) + }, [editor, isMarkdownView]) + + useEffect(() => { + const frameId = requestAnimationFrame(() => { + editor.dispatchCommand(SET_MARKDOWN_VIEW, isMarkdownView) + }) + return () => cancelAnimationFrame(frameId) + }, [editor, isMarkdownView]) + + return null +} + +const getMessageText = (content: unknown): string => { + if (content === null || content === undefined) return "" + if (typeof content === "string") return content + + if (content && typeof content === "object" && !Array.isArray(content)) { + const rec = content as Record + if (rec.type === "text" && typeof rec.text === "string") return rec.text + if (rec.type === "tool-call" && typeof rec.toolName === "string") { + const args = rec.args ?? rec.input + return args ? `${rec.toolName}(${JSON.stringify(args, null, 2)})` : String(rec.toolName) + } + if (rec.type === "tool-result") { + const output = rec.output as Record | undefined + const value = output?.value ?? output ?? rec.result + return typeof value === "string" ? value : JSON.stringify(value, null, 2) + } + } + + if (Array.isArray(content)) { + const parts: string[] = [] + for (const c of content) { + const rec = c as Record | null + if (!rec || typeof rec !== "object") { + parts.push(String(c)) + continue + } + if (rec.type === "text" && typeof rec.text === "string") { + parts.push(rec.text) + } else if (rec.type === "tool-call" && typeof rec.toolName === "string") { + parts.push(`[tool: ${rec.toolName}]`) + } else if (rec.type === "tool-result") { + const output = rec.output as Record | undefined + const value = output?.value ?? output ?? rec.result + parts.push(typeof value === "string" ? value : JSON.stringify(value, null, 2)) + } else if (typeof rec.text === "string") { + parts.push(rec.text) + } + } + if (parts.length > 0) return parts.join("\n") + } + + try { + return JSON.stringify(content, null, 2) + } catch { + return String(content) + } +} + +const RenderedChatMessages = memo(function RenderedChatMessages({ + messages, + keyPrefix, +}: { + messages: unknown[] + keyPrefix: string +}) { + const normalized = useMemo(() => normalizeChatMessages(messages), [messages]) + + return ( +
+ {normalized.map((msg, i) => { + const roleColor = + ROLE_COLOR_CLASSES[msg.role.toLowerCase()] ?? DEFAULT_ROLE_COLOR_CLASS + const text = getMessageText(msg.content) + const editorId = `${keyPrefix}-msg-${i}` + + return ( +
+ + {msg.role} + + + + + +
+ ) + })} +
+ ) +}) + +const valueToString = (value: unknown): string => { + if (value === null || value === undefined) return "" + if (typeof value === "string") return value + if (typeof value === "number" || typeof value === "boolean") return String(value) + try { + return JSON.stringify(value, null, 2) + } catch { + return String(value) + } +} + +const ReadOnlyVariableField = memo(function ReadOnlyVariableField({ + label, + value, + editorId, +}: { + label: string + value: string + editorId: string +}) { + return ( + + +
+ + {label} + + +
+
+ ) +}) + +const RenderedValueBlock = memo(function RenderedValueBlock({ + value: rawValue, + keyPrefix, + depth = 0, + maxDepth = DEFAULT_MAX_RENDER_DEPTH, +}: { + value: unknown + keyPrefix: string + depth?: number + maxDepth?: number +}) { + const value = useMemo(() => simplifyValue(rawValue), [rawValue]) + + const chatMessages = useMemo(() => extractChatMessages(value), [value]) + + if (chatMessages && chatMessages.length > 0) { + return + } + + if (value === null || value === undefined) { + return + } + + if (typeof value === "string") { + return ( + + + + + ) + } + + if (Array.isArray(value) && value.length === 0) { + return + } + + if ( + depth < maxDepth && + Array.isArray(value) && + value.length > 0 && + value.some((item) => item && typeof item === "object") + ) { + return ( +
+ {value.map((item, i) => { + const simplified = simplifyValue(item) + if ( + typeof simplified === "string" || + typeof simplified === "number" || + typeof simplified === "boolean" + ) { + return ( + + ) + } + if (simplified && typeof simplified === "object") { + return ( +
+
+ +
+
+ ) + } + return ( + + ) + })} +
+ ) + } + + if (value && typeof value === "object" && !Array.isArray(value)) { + const entries = Object.entries(value as Record) + return ( +
+ {entries.map(([k, v]) => { + const simplified = simplifyValue(v) + const nestedChat = extractChatMessages(simplified) + if (nestedChat && nestedChat.length > 0) { + return ( +
+ + {formatLabel(k)} + + +
+ ) + } + + if (isShortLeaf(simplified)) { + return ( + + ) + } + + if (depth < maxDepth && simplified && typeof simplified === "object") { + return ( +
+ + {formatLabel(k)} + +
+ +
+
+ ) + } + + return ( + + ) + })} +
+ ) + } + + return ( + + + + + ) +}) + +/** + * Beautified JSON view: renders a value as per-key fields or chat messages + * (opt-in display mode; do not use as the default when faithful JSON is + * required — use the JSON code viewer for that). + */ +export const BeautifiedJsonView = memo(function BeautifiedJsonView({ + data: rawData, + keyPrefix, +}: { + data: unknown + keyPrefix: string +}) { + const data = useMemo(() => simplifyValue(rawData), [rawData]) + + const isDirectChat = useMemo(() => { + if (typeof data === "string") return false + if (Array.isArray(data)) return !!extractChatMessages(data) + if (data && typeof data === "object" && "role" in (data as Record)) { + return !!extractChatMessages(data) + } + return false + }, [data]) + const directChatMessages = useMemo( + () => (isDirectChat ? extractChatMessages(data) : null), + [isDirectChat, data], + ) + + const entries = useMemo(() => { + if (typeof data === "string") return null + if (isDirectChat) return null + if (!data || typeof data !== "object" || Array.isArray(data)) return null + return Object.entries(data as Record) + }, [data, isDirectChat]) + + if (typeof data === "string") { + return ( +
+ +
+ ) + } + + if (isDirectChat && directChatMessages && directChatMessages.length > 0) { + return ( +
+ +
+ ) + } + + if (entries) { + return ( +
+ {entries.map(([key, value]) => ( +
+ {key} + +
+ ))} +
+ ) + } + + return ( +
+ +
+ ) +}) + +BeautifiedJsonView.displayName = "BeautifiedJsonView" diff --git a/web/oss/src/components/DrillInView/TraceSpanDrillInView.tsx b/web/oss/src/components/DrillInView/TraceSpanDrillInView.tsx index e98b91c8df..6e0102e503 100644 --- a/web/oss/src/components/DrillInView/TraceSpanDrillInView.tsx +++ b/web/oss/src/components/DrillInView/TraceSpanDrillInView.tsx @@ -20,12 +20,6 @@ import { SET_MARKDOWN_VIEW, SearchPlugin, } from "@agenta/ui" -import { - extractChatMessages, - normalizeChatMessages, - ROLE_COLOR_CLASSES, - DEFAULT_ROLE_COLOR_CLASS, -} from "@agenta/ui/cell-renderers" import { ArrowDownIcon, ArrowUpIcon, @@ -40,12 +34,17 @@ import { import {Button, Input, Select} from "antd" import {useAtomValue} from "jotai" import yaml from "js-yaml" -import JSON5 from "json5" import dynamic from "next/dynamic" import {copyToClipboard} from "@/oss/lib/helpers/copyToClipboard" import {getStringOrJson, sanitizeDataWithBlobUrls} from "@/oss/lib/helpers/utils" +import {BeautifiedJsonView} from "./BeautifiedJsonView" +import { + buildDecodedJsonOutput, + normalizeEscapedLineBreaks, + parseStructuredJson, +} from "./decodedJsonHelpers" import type {DrillInContentProps} from "./DrillInContent" import {EntityDrillInView} from "./EntityDrillInView" const ImagePreview = dynamic(() => import("@agenta/ui").then((mod) => mod.ImagePreview), { @@ -104,629 +103,42 @@ export interface TraceSpanDrillInViewProps extends Omit< type RawSpanViewMode = "json" | "yaml" -type RawSpanDisplayMode = RawSpanViewMode | "rendered-json" | "text" | "markdown" +/** + * View modes for a trace span. + * + * See `VIEW_MODES.md` in this folder for the full definition of each mode, + * including which display target it uses, what cleanup it applies, and when + * it is the default. + * + * Summary: + * - `json` / `yaml`: faithful — data as stored, no cleanup. + * - `decoded-json`: JSON editor, cleaned (unwrap nested stringified JSON, + * decode escaped newlines). Default for non-message data. + * - `beautified-json`: custom component tree (chat bubbles, per-key fields, + * envelope unwrap, noise stripping). Default for `viewModePreset="message"`. + * - `text` / `markdown`: prose editor. + */ +type RawSpanDisplayMode = RawSpanViewMode | "decoded-json" | "beautified-json" | "text" | "markdown" const RAW_SPAN_VIEW_MODE_LABELS: Record = { json: "JSON", yaml: "YAML", - "rendered-json": "Rendered JSON", + "decoded-json": "Decoded JSON", + "beautified-json": "Beautified JSON", text: "Text", markdown: "Markdown", } -const getDefaultRawSpanViewMode = (availableModes: RawSpanDisplayMode[]): RawSpanDisplayMode => { - if (availableModes.includes("rendered-json")) return "rendered-json" +const getDefaultRawSpanViewMode = ( + availableModes: RawSpanDisplayMode[], + {preferBeautified = false}: {preferBeautified?: boolean} = {}, +): RawSpanDisplayMode => { + if (preferBeautified && availableModes.includes("beautified-json")) return "beautified-json" + if (availableModes.includes("decoded-json")) return "decoded-json" return availableModes[0] ?? "json" } -const normalizeEscapedLineBreaks = (value: string): string => - value.replaceAll("\\r\\n", "\n").replaceAll("\\n", "\n") - -const parseStructuredJson = (value: string): unknown | null => { - const tryParseJson = (input: string): unknown | null => { - try { - return JSON.parse(input) - } catch { - return null - } - } - - const toStructured = (parsed: unknown): unknown | null => { - if (parsed && typeof parsed === "object") return parsed - if (typeof parsed !== "string") return null - - const nested = tryParseJson(parsed.trim()) - if (nested && typeof nested === "object") return nested - return null - } - - let candidate = value.trim() - if (!candidate) return null - - const fencedMatch = candidate.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i) - if (fencedMatch?.[1]) { - candidate = fencedMatch[1].trim() - } - - const strictParsed = toStructured(tryParseJson(candidate)) - if (strictParsed !== null) return strictParsed - - try { - return toStructured(JSON5.parse(candidate)) - } catch { - return null - } -} - -// ============================================================================ -// VALUE SIMPLIFICATION -// ============================================================================ - -/** Keys that are metadata noise — filtered out in rendered JSON mode */ -const METADATA_NOISE_KEYS = new Set([ - "providerOptions", - "experimental_providerMetadata", - "rawHeaders", - "caller", - "messageId", - "toolCallId", - "rawCall", - "rawResponse", - "logprobs", - "experimental_providerMetadata", -]) - -/** - * Simplify a value by unwrapping known envelope patterns. - * Returns the simplified value, or the original if no simplification applies. - */ -const simplifyValue = (value: unknown): unknown => { - if (!value || typeof value !== "object") return value - - const rec = value as Record - - // AI SDK text part: {type: "text", text: "hello"} → "hello" - if (rec.type === "text" && typeof rec.text === "string") { - return rec.text - } - - // AI SDK tool-call: {type: "tool-call", toolName: "fn", input: {...}} → "fn({...})" - if (rec.type === "tool-call" && typeof rec.toolName === "string") { - const args = rec.input ?? rec.args - if (!args || (typeof args === "object" && Object.keys(args as object).length === 0)) { - return `${rec.toolName}()` - } - try { - return `${rec.toolName}(${JSON.stringify(args, null, 2)})` - } catch { - return `${rec.toolName}(...)` - } - } - - // AI SDK tool-result envelope: {type: "tool-result", output: {type: "json", value: X}} → X - if (rec.type === "tool-result" && rec.output !== undefined) { - const output = rec.output as Record | undefined - if (output && typeof output === "object" && output.value !== undefined) { - return output.value - } - return rec.output - } - - // Single-element array of a simplifiable item: [{type: "text", text: "hello"}] → "hello" - if (Array.isArray(value) && value.length === 1) { - const simplified = simplifyValue(value[0]) - if (simplified !== value[0]) return simplified - } - - // Multi-element array: simplify each element - if (Array.isArray(value) && value.length > 1) { - const simplified = value.map(simplifyValue) - const changed = simplified.some((s, i) => s !== value[i]) - if (changed) { - // If all simplified to strings, join them - if (simplified.every((s) => typeof s === "string")) { - return (simplified as string[]).join("\n") - } - return simplified - } - } - - // Strip metadata noise keys from objects - if (!Array.isArray(value)) { - const keys = Object.keys(rec) - const noiseKeys = keys.filter((k) => METADATA_NOISE_KEYS.has(k)) - if (noiseKeys.length > 0 && noiseKeys.length < keys.length) { - const cleaned: Record = {} - for (const k of keys) { - if (!METADATA_NOISE_KEYS.has(k)) { - cleaned[k] = rec[k] - } - } - // If only one meaningful key remains, unwrap it - const cleanedKeys = Object.keys(cleaned) - if (cleanedKeys.length === 1) { - return cleaned[cleanedKeys[0]] - } - return cleaned - } - } - - return value -} - -/** Format a key label: snake_case → Title Case */ -const formatLabel = (key: string): string => - key - .replace(/_/g, " ") - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/\b\w/g, (c) => c.toUpperCase()) - -/** Check if a value is a short primitive suitable for inline display */ -const isShortLeaf = (value: unknown): boolean => { - if (value === null || value === undefined) return true - if (typeof value === "boolean" || typeof value === "number") return true - if (typeof value === "string" && value.length <= 120 && !value.includes("\n")) return true - return false -} - -/** Inline key-value pair for compact leaf rendering */ -const InlineKeyValue = memo(function InlineKeyValue({ - label, - value, -}: { - label: string - value: string -}) { - return ( -
- {label} - {value || "—"} -
- ) -}) - -const EDITOR_RESET_CLASSES = - "!min-h-0 [&_.editor-inner]:!border-0 [&_.editor-inner]:!rounded-none [&_.editor-inner]:!min-h-0 [&_.editor-container]:!bg-transparent [&_.editor-container]:!min-h-0 [&_.editor-input]:!min-h-0 [&_.editor-input]:!px-0 [&_.editor-input]:!py-0 [&_.editor-paragraph]:!mb-1 [&_.editor-paragraph:last-child]:!mb-0 [&_.agenta-editor-wrapper]:!min-h-0" - -/** Get text content from a chat message, with AI SDK part awareness */ -const getMessageText = (content: unknown): string => { - if (content === null || content === undefined) return "" - if (typeof content === "string") return content - - // Single AI SDK part object - if (content && typeof content === "object" && !Array.isArray(content)) { - const rec = content as Record - if (rec.type === "text" && typeof rec.text === "string") return rec.text - if (rec.type === "tool-call" && typeof rec.toolName === "string") { - const args = rec.args ?? rec.input - return args ? `${rec.toolName}(${JSON.stringify(args, null, 2)})` : String(rec.toolName) - } - if (rec.type === "tool-result") { - const output = rec.output as Record | undefined - const value = output?.value ?? output ?? rec.result - return typeof value === "string" ? value : JSON.stringify(value, null, 2) - } - } - - if (Array.isArray(content)) { - // Collect text from all parts - const parts: string[] = [] - for (const c of content) { - const rec = c as Record | null - if (!rec || typeof rec !== "object") { - parts.push(String(c)) - continue - } - if (rec.type === "text" && typeof rec.text === "string") { - parts.push(rec.text) - } else if (rec.type === "tool-call" && typeof rec.toolName === "string") { - parts.push(`[tool: ${rec.toolName}]`) - } else if (rec.type === "tool-result") { - const output = rec.output as Record | undefined - const value = output?.value ?? output ?? rec.result - parts.push(typeof value === "string" ? value : JSON.stringify(value, null, 2)) - } else if (typeof rec.text === "string") { - parts.push(rec.text) - } - } - if (parts.length > 0) return parts.join("\n") - } - - try { - return JSON.stringify(content, null, 2) - } catch { - return String(content) - } -} - -/** - * Renders chat messages with editor-backed content for markdown support. - * Each message gets a role label + EditorProvider for its content. - */ -const RenderedChatMessages = memo(function RenderedChatMessages({ - messages, - keyPrefix, -}: { - messages: unknown[] - keyPrefix: string -}) { - const normalized = useMemo(() => normalizeChatMessages(messages), [messages]) - - return ( -
- {normalized.map((msg, i) => { - const roleColor = - ROLE_COLOR_CLASSES[msg.role.toLowerCase()] ?? DEFAULT_ROLE_COLOR_CLASS - const text = getMessageText(msg.content) - const editorId = `${keyPrefix}-msg-${i}` - - return ( -
- - {msg.role} - - - - - -
- ) - })} -
- ) -}) - -/** Convert a value to a string for display in the editor */ -const valueToString = (value: unknown): string => { - if (value === null || value === undefined) return "" - if (typeof value === "string") return value - if (typeof value === "number" || typeof value === "boolean") return String(value) - try { - return JSON.stringify(value, null, 2) - } catch { - return String(value) - } -} - -/** - * Read-only variable field: renders a labeled value using EditorProvider, - * matching the playground's variable display with markdown support. - */ -const ReadOnlyVariableField = memo(function ReadOnlyVariableField({ - label, - value, - editorId, -}: { - label: string - value: string - editorId: string -}) { - return ( - - -
- - {label} - - -
-
- ) -}) - -/** - * Renders a value with smart type detection: - * - Chat messages → RenderedChatMessages (editor-backed) - * - Plain objects → labeled variable fields (recursive) - * - Primitives → read-only editor field - * - Arrays → per-item rendering - */ -const DEFAULT_MAX_RENDER_DEPTH = 5 - -const RenderedValueBlock = memo(function RenderedValueBlock({ - value: rawValue, - keyPrefix, - depth = 0, - maxDepth = DEFAULT_MAX_RENDER_DEPTH, -}: { - value: unknown - keyPrefix: string - depth?: number - maxDepth?: number -}) { - // Simplify envelopes and strip metadata before rendering - const value = useMemo(() => simplifyValue(rawValue), [rawValue]) - - const chatMessages = useMemo(() => extractChatMessages(value), [value]) - - if (chatMessages && chatMessages.length > 0) { - return - } - - if (value === null || value === undefined) { - return - } - - // After simplification, primitives render as text - if (typeof value === "string") { - return ( - - - - - ) - } - - if (Array.isArray(value) && value.length === 0) { - return - } - - // Non-empty array of objects → render each item recursively - if ( - depth < maxDepth && - Array.isArray(value) && - value.length > 0 && - value.some((item) => item && typeof item === "object") - ) { - return ( -
- {value.map((item, i) => { - const simplified = simplifyValue(item) - if ( - typeof simplified === "string" || - typeof simplified === "number" || - typeof simplified === "boolean" - ) { - return ( - - ) - } - if (simplified && typeof simplified === "object") { - return ( -
-
- -
-
- ) - } - return ( - - ) - })} -
- ) - } - - // Plain object → render each key as a variable field, simplifying nested values - if (value && typeof value === "object" && !Array.isArray(value)) { - const entries = Object.entries(value as Record) - return ( -
- {entries.map(([k, v]) => { - const simplified = simplifyValue(v) - const nestedChat = extractChatMessages(simplified) - if (nestedChat && nestedChat.length > 0) { - return ( -
- - {formatLabel(k)} - - -
- ) - } - - // Short leaf values → inline key: value on one line - if (isShortLeaf(simplified)) { - return ( - - ) - } - - // Recurse for nested objects/arrays (up to depth limit) - if (depth < maxDepth && simplified && typeof simplified === "object") { - return ( -
- - {formatLabel(k)} - -
- -
-
- ) - } - - // Long strings or depth-limited objects → editor field - return ( - - ) - })} -
- ) - } - - // Primitives, arrays, anything else → single editor - return ( - - - - - ) -}) - -/** - * Rendered JSON view for a span field. - * - If the entire value is chat-like (single message or array), render as chat. - * - If it's an object, render each top-level key separately, - * detecting chat vs non-chat per key. - * - Otherwise render as formatted text. - */ -const RenderedJsonView = memo(function RenderedJsonView({ - data: rawData, - keyPrefix, -}: { - data: unknown - keyPrefix: string -}) { - // Simplify envelopes (tool-call, tool-result, text parts) before rendering - const data = useMemo(() => simplifyValue(rawData), [rawData]) - - // Determine if this is a direct chat value: - // - Array of messages → render as chat - // - Single message object (has "role" key) → render as chat - // - Object with multiple keys (some may contain chat) → render per-key - const isDirectChat = useMemo(() => { - if (typeof data === "string") return false - if (Array.isArray(data)) return !!extractChatMessages(data) - if (data && typeof data === "object" && "role" in (data as Record)) { - return !!extractChatMessages(data) - } - return false - }, [data]) - const directChatMessages = useMemo( - () => (isDirectChat ? extractChatMessages(data) : null), - [isDirectChat, data], - ) - - // For non-chat objects, render each key separately - const entries = useMemo(() => { - if (typeof data === "string") return null - if (isDirectChat) return null - if (!data || typeof data !== "object" || Array.isArray(data)) return null - return Object.entries(data as Record) - }, [data, isDirectChat]) - - // If simplification produced a string, render as text directly - if (typeof data === "string") { - return ( -
- -
- ) - } - - if (isDirectChat && directChatMessages && directChatMessages.length > 0) { - return ( -
- -
- ) - } - - if (entries) { - return ( -
- {entries.map(([key, value]) => ( -
- {key} - -
- ))} -
- ) - } - - // Primitive or array fallback - return ( -
- -
- ) -}) +// Value-simplification and beautified rendering live in ./BeautifiedJsonView. const LanguageAwareViewer = ({ initialValue, @@ -942,39 +354,64 @@ export const TraceSpanDrillInView = memo( return getStringOrJson(sanitizedSpanData) }, [parsedStructuredString, sanitizedSpanData]) + const decodedJsonOutput = useMemo( + () => buildDecodedJsonOutput(sanitizedSpanData, parsedStructuredString), + [sanitizedSpanData, parsedStructuredString], + ) + + const beautifiedJsonSource = useMemo(() => { + if (isStringValue) return parsedStructuredString ?? sanitizedSpanData + return sanitizedSpanData + }, [isStringValue, parsedStructuredString, sanitizedSpanData]) + + const hasStructuredValue = + (isStringValue && parsedStructuredString !== null) || + (!isStringValue && isObjectOrArrayValue) + const availableViewModes = useMemo(() => { if (viewModePreset === "message") { const modes: RawSpanDisplayMode[] = ["text", "markdown"] - if ( - (isStringValue && parsedStructuredString !== null) || - (!isStringValue && isObjectOrArrayValue) - ) { - modes.push("rendered-json") + if (hasStructuredValue) { + modes.push("decoded-json", "beautified-json") } return modes } if (isStringValue) { if (parsedStructuredString !== null) { - const modes: RawSpanDisplayMode[] = ["json", "yaml", "rendered-json"] - modes.push("text", "markdown") - return modes + return [ + "json", + "yaml", + "decoded-json", + "beautified-json", + "text", + "markdown", + ] as RawSpanDisplayMode[] } return ["text", "markdown"] as RawSpanDisplayMode[] } - const modes: RawSpanDisplayMode[] = ["json", "yaml", "rendered-json"] - return modes - }, [viewModePreset, isStringValue, isObjectOrArrayValue, parsedStructuredString]) + return ["json", "yaml", "decoded-json", "beautified-json"] as RawSpanDisplayMode[] + }, [viewModePreset, isStringValue, hasStructuredValue, parsedStructuredString]) const [viewMode, setViewMode] = useState(() => - getDefaultRawSpanViewMode(availableViewModes), + getDefaultRawSpanViewMode(availableViewModes, { + preferBeautified: viewModePreset === "message", + }), ) - const isCodeMode = viewMode === "json" || viewMode === "yaml" - const isRenderedJson = viewMode === "rendered-json" + const isCodeMode = viewMode === "json" || viewMode === "yaml" || viewMode === "decoded-json" + const isBeautifiedJson = viewMode === "beautified-json" const activeOutput = - viewMode === "yaml" ? yamlOutput : viewMode === "json" ? jsonOutput : textOutput + viewMode === "yaml" + ? yamlOutput + : viewMode === "json" + ? jsonOutput + : viewMode === "decoded-json" + ? decodedJsonOutput + : viewMode === "beautified-json" + ? JSON.stringify(beautifiedJsonSource, null, 2) + : textOutput const closeSearch = useCallback(() => { setIsSearchOpen(false) @@ -1004,9 +441,13 @@ export const TraceSpanDrillInView = memo( useEffect(() => { if (!availableViewModes.includes(viewMode)) { - setViewMode(getDefaultRawSpanViewMode(availableViewModes)) + setViewMode( + getDefaultRawSpanViewMode(availableViewModes, { + preferBeautified: viewModePreset === "message", + }), + ) } - }, [availableViewModes, viewMode]) + }, [availableViewModes, viewMode, viewModePreset]) useEffect(() => { closeSearch() @@ -1114,7 +555,7 @@ export const TraceSpanDrillInView = memo( - ) : isRenderedJson ? ( + ) : isBeautifiedJson ? (
-
diff --git a/web/oss/src/components/DrillInView/VIEW_MODES.md b/web/oss/src/components/DrillInView/VIEW_MODES.md new file mode 100644 index 0000000000..8ad4bad5e5 --- /dev/null +++ b/web/oss/src/components/DrillInView/VIEW_MODES.md @@ -0,0 +1,133 @@ +# Trace/Panel View Modes + +Authoritative definitions for the view-mode selector used in the trace drill-in +viewer ([TraceSpanDrillInView](./TraceSpanDrillInView.tsx)) and the legacy trace +drawer ([AccordionTreePanel](../SharedDrawers/TraceDrawer/components/AccordionTreePanel.tsx)). + +If you are about to add, rename, or change the behavior of a view mode, update +this file first and keep the two panels aligned. + +## Display targets + +There are three display targets shared across modes: + +1. **JSON code editor** — read-only, syntax-highlighted, grey background. + Used by `json`, `yaml`, and `decoded-json`. +2. **Editor for prose** — the shared text/markdown editor. + Used by `text` and `markdown`. +3. **Beautified component tree** — custom React layout. + Used by `beautified-json` only. + +## Modes + +### `json` — Faithful + +What the instrumentation stored, verbatim. No decoding, no reshaping. + +- `isStringValue` that parses as JSON → the raw string verbatim (escaped `\n`, + embedded quotes in stringified fields, ` ```json … ``` ` wrappers, all kept). +- `isStringValue` that does not parse → `JSON.stringify(value)` (wrapped in + quotes so it is valid JSON in the code view). +- object / array → `getStringOrJson(value)` — a plain `JSON.stringify(_, null, 2)`. + +Use `json` when you need to see exactly what is on the wire. + +### `yaml` — Faithful, YAML + +Same source data as `json`, emitted via `js-yaml` at 120-char line width. +No decoding is applied — if a string field contains stringified JSON, it comes +out as a YAML string containing stringified JSON. Intended for readability only. + +### `decoded-json` — Decoded + +**This is the default for non-message data.** Same JSON code editor as `json`, +but the source is passed through the pipeline in +[decodedJsonHelpers.ts](./decodedJsonHelpers.ts) before pretty-printing: + +1. If the raw value is a string, use its structure-parsed form — + tolerates whitespace, fenced ` ```json … ``` ` blocks, one level of + string-wrapping, and JSON5 syntax (single quotes, trailing commas). +2. `unwrapStringifiedJson` walks the structure and, for every string leaf that + parses as JSON, replaces the string with the parsed value. So + `{"result": "{\"ok\":true}"}` becomes `{"result": {"ok": true}}`. +3. `formatJsonStringsForDisplay` decodes escaped `\n` / `\r\n` in every + remaining string leaf into real newlines (swapped to U+2028 so + `JSON.stringify` keeps the multiline look without breaking the output). +4. Pretty-print with `JSON.stringify(_, null, 2)`. + +Mental model: this is the inverse of serialization. If the wire data is a +JSON value wrapped in one or more layers of string-encoding and escape +sequences, this mode peels those layers away so you see the actual structure. + +Use `decoded-json` when you want the real shape of LLM and tracing output +without wading through escape artifacts. This is what users mean when they +say "show the output as JSON" in the bug reports. + +Historical note: this mode was previously labeled "Rendered JSON". The name +invited the misreading that it meant "rendered into another UI" — leading a +previous change to silently turn it into a chat-bubble view. The enum value +is now `decoded-json`. Do not reintroduce the old name. + +### `beautified-json` — Reshaped + +**This is the default for `viewModePreset="message"`.** Not JSON at all — +renders via [BeautifiedJsonView](./BeautifiedJsonView.tsx), which uses a +custom React layout: + +- Chat-like arrays and single messages → chat bubbles (role label + content + editor with markdown support). +- Plain objects → labeled variable fields, recursively, with short leaves + inlined as `key: value`. +- Known envelope patterns (AI SDK `{type: "text", …}`, `{type: "tool-call", …}`, + `{type: "tool-result", …}`) are unwrapped. +- Noisy provider-metadata keys (`providerOptions`, `rawHeaders`, `rawCall`, + `rawResponse`, `logprobs`, etc.) are stripped. + +Use `beautified-json` when you want a readable presentation of chat messages +or when the structural rewriting is desirable. **Never** use it when the shape +needs to match the wire data — it hides fields. + +### `text` — String + +Rendered in the shared editor in plain-text mode. For a string value that +parses as JSON, escaped line breaks are normalized into real newlines; for +anything else the value is coerced via `getStringOrJson`. Useful for reading +an LLM response whose content is just prose. + +### `markdown` — Markdown + +Same as `text` but the editor is put into markdown-preview mode. Use when the +string is markdown-formatted (headings, lists, code fences). + +## Default behavior + +Both panels use the same default-selection logic: + +- If `viewModePreset === "message"` and `beautified-json` is available, + default to `beautified-json`. +- Otherwise, if `decoded-json` is available, default to `decoded-json`. +- Otherwise the first available mode wins (in practice `text` for plain + strings that do not parse as JSON). + +## Availability matrix + +| Source | Available modes | +| --------------------------------------------------- | ------------------------------------------------------------------------- | +| object or array | `json`, `yaml`, `decoded-json`, `beautified-json` | +| string that parses as structured JSON | `json`, `yaml`, `decoded-json`, `beautified-json`, `text`, `markdown` | +| string that does not parse as JSON | `text`, `markdown` | +| `viewModePreset="message"` + structured value | `text`, `markdown`, `decoded-json`, `beautified-json` | +| `viewModePreset="message"` + unstructured string | `text`, `markdown` | + +## Change-safety rules + +- **Do not reshape data inside `decoded-json`** beyond the string decoding + described above. Any rewriting that changes the set of keys belongs in + `beautified-json`. +- **Do not duplicate the decoding helpers.** Both panels import from + [decodedJsonHelpers.ts](./decodedJsonHelpers.ts) and + [BeautifiedJsonView.tsx](./BeautifiedJsonView.tsx) — a prior duplication + is what caused the original "Rendered JSON" regression. If you need a new + helper, add it to the shared module. +- If you change the `PanelViewMode` or `RawSpanDisplayMode` union, update the + other and update this README. diff --git a/web/oss/src/components/DrillInView/decodedJsonHelpers.ts b/web/oss/src/components/DrillInView/decodedJsonHelpers.ts new file mode 100644 index 0000000000..62402856a0 --- /dev/null +++ b/web/oss/src/components/DrillInView/decodedJsonHelpers.ts @@ -0,0 +1,197 @@ +import JSON5 from "json5" + +/** + * Helpers for the "Decoded JSON" view mode. + * + * ## What "Decoded JSON" means + * + * "Decoded JSON" shows data in the JSON code editor (grey-background, + * syntax-highlighted) — the same display target as the plain "JSON" mode. + * The ONLY difference vs plain JSON is that the source is decoded first to + * strip common quasi-JSON encodings produced by LLMs and instrumentation + * layers: + * + * - fenced code blocks around a string value (```json ... ```) + * - JSON strings nested inside other JSON strings (stringified JSON in fields) + * - escaped line breaks (\\n, \\r\\n) which are decoded into real newlines + * - JSON5 syntactic relaxations (single quotes, trailing commas) + * + * Think of it as the inverse of serialization: if the wire data is a JSON + * value wrapped in one or more layers of string-encoding and escape sequences, + * this mode peels those layers away so you see the actual structure. + * + * "Decoded JSON" does NOT reshape the data, drop keys, or render it outside + * the JSON editor. Any operation that changes the set of keys or renders the + * data as chat bubbles / labeled fields belongs in "Beautified JSON" — + * see `BeautifiedJsonView.tsx`. + * + * Historical note: this mode was previously called "Rendered JSON". The name + * invited confusion — "rendered" sounded like "rendered into another UI", + * which led a previous change to silently turn it into a chat-bubble view. + * If you rename again, keep the semantics described above. + * + * ## Why this file exists + * + * These helpers are shared between `TraceSpanDrillInView` and + * `AccordionTreePanel` so their `decoded-json` output cannot drift. If you + * need a new decoding step, add it here and use it from both panels. + * + * ## Authoritative reference + * + * `VIEW_MODES.md` in this folder documents every view mode and the rules + * for choosing a default. Keep it in sync when you change behavior here. + */ + +/** Decode escaped \n/\r\n sequences at both encoding depths into real newlines. */ +export const normalizeEscapedLineBreaks = (value: string): string => + value.replaceAll("\\r\\n", "\n").replaceAll("\\n", "\n") + +/** + * Try to parse a string as structured JSON, tolerating: + * - whitespace + * - markdown fenced code blocks (```json ... ```) + * - one level of string-wrapping (e.g. a JSON-stringified JSON) + * - JSON5 syntax (single quotes, trailing commas, etc.) + * + * Returns the parsed object/array, or null if not structured. + */ +export const parseStructuredJson = (value: string): unknown | null => { + const tryParseJson = (input: string): unknown | null => { + try { + return JSON.parse(input) + } catch { + return null + } + } + + const toStructured = (parsed: unknown): unknown | null => { + if (parsed && typeof parsed === "object") return parsed + if (typeof parsed !== "string") return null + + const nested = tryParseJson(parsed.trim()) + if (nested && typeof nested === "object") return nested + return null + } + + let candidate = value.trim() + if (!candidate) return null + + const fencedMatch = candidate.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i) + if (fencedMatch?.[1]) { + candidate = fencedMatch[1].trim() + } + + const strictParsed = toStructured(tryParseJson(candidate)) + if (strictParsed !== null) return strictParsed + + try { + return toStructured(JSON5.parse(candidate)) + } catch { + return null + } +} + +/** + * Recursively unwrap stringified JSON values inside a structure. + * A field whose string value parses as JSON is replaced by its parsed value. + * + * Returns the transformed value and a `didUnwrap` flag indicating whether + * any string leaf was replaced. + */ +export const unwrapStringifiedJson = (value: unknown): {value: unknown; didUnwrap: boolean} => { + if (typeof value === "string") { + const parsed = parseStructuredJson(value) + if (parsed === null) return {value, didUnwrap: false} + const nested = unwrapStringifiedJson(parsed) + return {value: nested.value, didUnwrap: true} + } + + if (Array.isArray(value)) { + let didUnwrap = false + const rendered = value.map((item) => { + const next = unwrapStringifiedJson(item) + if (next.didUnwrap) didUnwrap = true + return next.value + }) + return {value: rendered, didUnwrap} + } + + if (value && typeof value === "object") { + let didUnwrap = false + const rendered = Object.fromEntries( + Object.entries(value).map(([key, nestedValue]) => { + const next = unwrapStringifiedJson(nestedValue) + if (next.didUnwrap) didUnwrap = true + return [key, next.value] + }), + ) + return {value: rendered, didUnwrap} + } + + return {value, didUnwrap: false} +} + +/** + * Decode escaped line-break sequences through both single- and double-encoded + * representations, stopping once a pass produces no further change. + */ +export const decodeEscapedLineBreaks = (value: string): string => { + let decoded = value + + for (let i = 0; i < 2; i += 1) { + const next = decoded + .replace(/\\\\r\\\\n/g, "\r\n") + .replace(/\\\\n/g, "\n") + .replace(/\\r\\n/g, "\r\n") + .replace(/\\n/g, "\n") + + if (next === decoded) break + decoded = next + } + + return decoded +} + +/** + * Walk a structure and decode escaped newlines in every string leaf. Real + * newlines are then replaced with \u2028 (line separator) so `JSON.stringify` + * keeps the multiline look inside the code viewer without breaking JSON. + */ +export const formatJsonStringsForDisplay = (value: unknown): unknown => { + if (typeof value === "string") { + return decodeEscapedLineBreaks(value).replace(/\r\n|\n|\r/g, "\u2028") + } + + if (Array.isArray(value)) { + return value.map((item) => formatJsonStringsForDisplay(item)) + } + + if (value && typeof value === "object") { + return Object.fromEntries( + Object.entries(value).map(([key, nestedValue]) => [ + key, + formatJsonStringsForDisplay(nestedValue), + ]), + ) + } + + return value +} + +/** + * Build the final "Decoded JSON" string for display in the JSON code viewer. + * + * Starts from the structure-parsed string (if the raw value was a string that + * parsed as JSON), falling back to the raw value otherwise, then recursively + * unwraps nested stringified JSON fields and decodes escaped newlines. + */ +export const buildDecodedJsonOutput = ( + rawValue: unknown, + parsedStructuredString: unknown | null, +): string => { + const source = typeof rawValue === "string" ? (parsedStructuredString ?? rawValue) : rawValue + const unwrapped = unwrapStringifiedJson(source).value + const formatted = formatJsonStringsForDisplay(unwrapped) + const next = JSON.stringify(formatted, null, 2) + return next ?? "null" +} diff --git a/web/oss/src/components/SharedDrawers/TraceDrawer/components/AccordionTreePanel.tsx b/web/oss/src/components/SharedDrawers/TraceDrawer/components/AccordionTreePanel.tsx index a2c925775c..f7929e3dc8 100644 --- a/web/oss/src/components/SharedDrawers/TraceDrawer/components/AccordionTreePanel.tsx +++ b/web/oss/src/components/SharedDrawers/TraceDrawer/components/AccordionTreePanel.tsx @@ -22,10 +22,15 @@ import { } from "@phosphor-icons/react" import {Button, Collapse, Dropdown, Input, Radio, Space, theme} from "antd" import yaml from "js-yaml" -import JSON5 from "json5" import dynamic from "next/dynamic" import {createUseStyles} from "react-jss" +import {BeautifiedJsonView} from "@/oss/components/DrillInView/BeautifiedJsonView" +import { + buildDecodedJsonOutput, + normalizeEscapedLineBreaks, + parseStructuredJson, +} from "@/oss/components/DrillInView/decodedJsonHelpers" import EnhancedButton from "@/oss/components/EnhancedUIs/Button" import {copyToClipboard} from "@/oss/lib/helpers/copyToClipboard" import {getStringOrJson, sanitizeDataWithBlobUrls} from "@/oss/lib/helpers/utils" @@ -48,133 +53,42 @@ type AccordionTreePanelProps = { viewModePreset?: "default" | "message" } & React.ComponentProps -type PanelViewMode = "json" | "yaml" | "rendered-json" | "text" | "markdown" +/** + * View modes for an accordion panel. + * + * See `VIEW_MODES.md` under `components/DrillInView/` for the full definition + * of each mode — which display target it uses, what cleanup it applies, and + * when it is the default. Keep this union in sync with `RawSpanDisplayMode` + * in `TraceSpanDrillInView.tsx`. + * + * Summary: + * - `json` / `yaml`: faithful — data as stored, no cleanup. + * - `decoded-json`: JSON editor, cleaned (unwrap nested stringified JSON, + * decode escaped newlines). Default for non-message data. + * - `beautified-json`: custom component tree (chat bubbles, per-key fields, + * envelope unwrap, noise stripping). Default for `viewModePreset="message"`. + * - `text` / `markdown`: prose editor. + */ +type PanelViewMode = "json" | "yaml" | "decoded-json" | "beautified-json" | "text" | "markdown" const PANEL_VIEW_MODE_LABELS: Record = { json: "JSON", yaml: "YAML", - "rendered-json": "Rendered JSON", + "decoded-json": "Decoded JSON", + "beautified-json": "Beautified JSON", text: "Text", markdown: "Markdown", } -const getDefaultPanelViewMode = (availableModes: PanelViewMode[]): PanelViewMode => { - if (availableModes.includes("rendered-json")) return "rendered-json" +const getDefaultPanelViewMode = ( + availableModes: PanelViewMode[], + {preferBeautified = false}: {preferBeautified?: boolean} = {}, +): PanelViewMode => { + if (preferBeautified && availableModes.includes("beautified-json")) return "beautified-json" + if (availableModes.includes("decoded-json")) return "decoded-json" return availableModes[0] ?? "json" } -const normalizeEscapedLineBreaks = (value: string): string => - value.replaceAll("\\r\\n", "\n").replaceAll("\\n", "\n") - -const parseStructuredJson = (value: string): unknown | null => { - const tryParseJson = (input: string): unknown | null => { - try { - return JSON.parse(input) - } catch { - return null - } - } - - const toStructured = (parsed: unknown): unknown | null => { - if (parsed && typeof parsed === "object") return parsed - if (typeof parsed !== "string") return null - - const nested = tryParseJson(parsed.trim()) - if (nested && typeof nested === "object") return nested - return null - } - - let candidate = value.trim() - if (!candidate) return null - - const fencedMatch = candidate.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i) - if (fencedMatch?.[1]) { - candidate = fencedMatch[1].trim() - } - - const strictParsed = toStructured(tryParseJson(candidate)) - if (strictParsed !== null) return strictParsed - - try { - return toStructured(JSON5.parse(candidate)) - } catch { - return null - } -} - -const renderStringifiedJson = (value: unknown): {value: unknown; didRender: boolean} => { - if (typeof value === "string") { - const parsed = parseStructuredJson(value) - if (parsed === null) return {value, didRender: false} - const nested = renderStringifiedJson(parsed) - return {value: nested.value, didRender: true} - } - - if (Array.isArray(value)) { - let didRender = false - const rendered = value.map((item) => { - const next = renderStringifiedJson(item) - if (next.didRender) didRender = true - return next.value - }) - return {value: rendered, didRender} - } - - if (value && typeof value === "object") { - let didRender = false - const rendered = Object.fromEntries( - Object.entries(value).map(([key, nestedValue]) => { - const next = renderStringifiedJson(nestedValue) - if (next.didRender) didRender = true - return [key, next.value] - }), - ) - return {value: rendered, didRender} - } - - return {value, didRender: false} -} - -const decodeEscapedLineBreaks = (value: string): string => { - let decoded = value - - // Handle both "\n" and "\\n" style payload encodings. - for (let i = 0; i < 2; i += 1) { - const next = decoded - .replace(/\\\\r\\\\n/g, "\r\n") - .replace(/\\\\n/g, "\n") - .replace(/\\r\\n/g, "\r\n") - .replace(/\\n/g, "\n") - - if (next === decoded) break - decoded = next - } - - return decoded -} - -const formatRenderedJsonStringsForDisplay = (value: unknown): unknown => { - if (typeof value === "string") { - // Decode escaped line breaks and preserve multiline rendering in JSON code view. - return decodeEscapedLineBreaks(value).replace(/\r\n|\n|\r/g, "\u2028") - } - - if (Array.isArray(value)) { - return value.map((item) => formatRenderedJsonStringsForDisplay(item)) - } - - if (value && typeof value === "object") { - return Object.fromEntries( - Object.entries(value).map(([key, nestedValue]) => [ - key, - formatRenderedJsonStringsForDisplay(nestedValue), - ]), - ) - } - - return value -} - const useStyles = createUseStyles((theme: JSSTheme) => ({ collapseContainer: ({bgColor}: {bgColor?: string}) => ({ backgroundColor: "unset", @@ -244,7 +158,7 @@ const LanguageAwareViewer = ({ searchProps, }: { initialValue: string - language: "json" | "yaml" | "rendered-json" + language: "json" | "yaml" | "decoded-json" searchProps?: { searchTerm: string currentResultIndex: number @@ -260,7 +174,7 @@ const LanguageAwareViewer = ({ ) useEffect(() => { - if (language === "json" || language === "rendered-json") { + if (language === "json" || language === "decoded-json") { changeLanguage("json") } else { changeLanguage("yaml") @@ -283,7 +197,7 @@ const LanguageAwareViewer = ({ const editorNode = ( { - if (isStringValue) { - return parsedStructuredString ?? sanitizedValue - } - return sanitizedValue - }, [isStringValue, parsedStructuredString, sanitizedValue]) - - const renderedJsonResult = useMemo(() => { - return renderStringifiedJson(renderedJsonSource) - }, [renderedJsonSource]) + const hasStructuredValue = + (isStringValue && parsedStructuredString !== null) || + (!isStringValue && isObjectOrArrayValue) const availableViewModes = useMemo(() => { if (viewModePreset === "message") { const modes: PanelViewMode[] = ["text", "markdown"] - if ( - (isStringValue && parsedStructuredString !== null) || - (!isStringValue && isObjectOrArrayValue) - ) { - modes.push("rendered-json") + if (hasStructuredValue) { + modes.push("decoded-json", "beautified-json") } return modes } if (isStringValue) { if (parsedStructuredString !== null) { - const modes: PanelViewMode[] = ["json", "yaml", "rendered-json"] - modes.push("text", "markdown") - return modes + return ["json", "yaml", "decoded-json", "beautified-json", "text", "markdown"] } return ["text", "markdown"] } - const modes: PanelViewMode[] = ["json", "yaml", "rendered-json"] - return modes - }, [viewModePreset, isStringValue, isObjectOrArrayValue, parsedStructuredString]) + return ["json", "yaml", "decoded-json", "beautified-json"] + }, [viewModePreset, isStringValue, hasStructuredValue, parsedStructuredString]) const [panelViewMode, setPanelViewMode] = useState(() => - getDefaultPanelViewMode(availableViewModes), + getDefaultPanelViewMode(availableViewModes, { + preferBeautified: viewModePreset === "message", + }), ) useEffect(() => { if (!availableViewModes.includes(panelViewMode)) { - setPanelViewMode(getDefaultPanelViewMode(availableViewModes)) + setPanelViewMode( + getDefaultPanelViewMode(availableViewModes, { + preferBeautified: viewModePreset === "message", + }), + ) } - }, [availableViewModes, panelViewMode]) + }, [availableViewModes, panelViewMode, viewModePreset]) const isCodeMode = - panelViewMode === "json" || panelViewMode === "yaml" || panelViewMode === "rendered-json" + panelViewMode === "json" || panelViewMode === "yaml" || panelViewMode === "decoded-json" + const isBeautifiedMode = panelViewMode === "beautified-json" useEffect(() => { if (!isCodeMode) { @@ -491,15 +399,17 @@ const AccordionTreePanel = ({ } }, [panelViewMode, isStringValue, parsedStructuredString, sanitizedValue]) - const renderedJsonOutput = useMemo(() => { - if (panelViewMode !== "rendered-json") return "" - const next = JSON.stringify( - formatRenderedJsonStringsForDisplay(renderedJsonResult.value), - null, - 2, - ) - return next ?? "null" - }, [panelViewMode, renderedJsonResult.value]) + const decodedJsonOutput = useMemo(() => { + if (panelViewMode !== "decoded-json") return "" + return buildDecodedJsonOutput(sanitizedValue, parsedStructuredString) + }, [panelViewMode, sanitizedValue, parsedStructuredString]) + + const beautifiedJsonSource = useMemo(() => { + if (isStringValue) { + return parsedStructuredString ?? sanitizedValue + } + return sanitizedValue + }, [isStringValue, parsedStructuredString, sanitizedValue]) const textOutput = useMemo(() => { if (typeof sanitizedValue === "string") { @@ -523,11 +433,13 @@ const AccordionTreePanel = ({ const copyText = panelViewMode === "yaml" ? yamlOutput - : panelViewMode === "rendered-json" - ? renderedJsonOutput + : panelViewMode === "decoded-json" + ? decodedJsonOutput : panelViewMode === "json" ? jsonOutput - : textOutput + : panelViewMode === "beautified-json" + ? JSON.stringify(beautifiedJsonSource, null, 2) + : textOutput const collapse = (
@@ -595,7 +507,7 @@ const AccordionTreePanel = ({ value={{ enabled: false, decodeEscapedJsonStrings: - panelViewMode === "rendered-json", + panelViewMode === "decoded-json", }} > + ) : isBeautifiedMode ? ( + ) : ( Date: Mon, 20 Apr 2026 15:09:46 +0200 Subject: [PATCH 05/13] Add 'Copy ID' in prompt page --- .../p/[project_id]/apps/[app_id]/overview/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/overview/index.tsx b/web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/overview/index.tsx index 0e8d24c257..78f7a13c95 100644 --- a/web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/overview/index.tsx +++ b/web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/overview/index.tsx @@ -2,7 +2,7 @@ import {memo, useState} from "react" import {PageLayout} from "@agenta/ui" import {MoreOutlined} from "@ant-design/icons" -import {PencilSimple, Trash} from "@phosphor-icons/react" +import {Copy, PencilSimple, Trash} from "@phosphor-icons/react" // TEMPORARY: Disabling name editing // import {PencilLine} from "@phosphor-icons/react" import {Button, Dropdown, Space, Typography} from "antd" @@ -15,6 +15,7 @@ import {openDeleteAppModalAtom} from "@/oss/components/pages/app-management/moda // import {openEditAppModalAtom} from "@/oss/components/pages/app-management/modals/EditAppModal/store/editAppModalStore" import DeploymentOverview from "@/oss/components/pages/overview/deployments/DeploymentOverview" import VariantsOverview from "@/oss/components/pages/overview/variants/VariantsOverview" +import {copyToClipboard} from "@/oss/lib/helpers/copyToClipboard" import {useAppsData} from "@/oss/state/app" const CustomWorkflowHistory: any = dynamic( @@ -78,6 +79,12 @@ const AppDetailsSection = memo(() => { // onClick: () => openEditAppModal(currentApp!), // }, ]), + { + key: "copy_id", + label: "Copy ID", + icon: , + onClick: () => copyToClipboard(currentApp!.id), + }, { key: "delete_app", label: "Delete", From 1b120186206127e04fb19185f56e60a92e34aaef Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 20 Apr 2026 15:11:02 +0200 Subject: [PATCH 06/13] Fix random slug in human feedback in both the observability drawer and the annotation queue page --- api/oss/src/core/annotations/service.py | 2 +- api/oss/src/core/tracing/service.py | 17 ++++++++++++----- .../ScenarioAnnotationPanel/index.tsx | 2 +- .../AnnotateDrawer/assets/transforms.ts | 1 - .../controllers/annotationFormController.ts | 2 -- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/api/oss/src/core/annotations/service.py b/api/oss/src/core/annotations/service.py index 5bd333c6b4..35d020c273 100644 --- a/api/oss/src/core/annotations/service.py +++ b/api/oss/src/core/annotations/service.py @@ -195,7 +195,7 @@ async def create( project_id=project_id, user_id=user_id, # - name=simple_evaluator.name if simple_evaluator else None, + name=simple_evaluator.slug if simple_evaluator else None, # flags=annotation_flags, tags=annotation_create.tags, diff --git a/api/oss/src/core/tracing/service.py b/api/oss/src/core/tracing/service.py index 2249ac5d20..d041e6fe2c 100644 --- a/api/oss/src/core/tracing/service.py +++ b/api/oss/src/core/tracing/service.py @@ -1007,17 +1007,17 @@ async def _resolve_evaluator_references( references.evaluator = Reference( id=evaluator_revision.evaluator_id, - slug=(references.evaluator.slug if references.evaluator else None) - or (evaluator.slug if evaluator else None), + slug=(evaluator.slug if evaluator else None) + or (references.evaluator.slug if references.evaluator else None), ) references.evaluator_variant = Reference( id=evaluator_revision.evaluator_variant_id, - slug=( + slug=(evaluator_variant.slug if evaluator_variant else None) + or ( references.evaluator_variant.slug if references.evaluator_variant else None - ) - or (evaluator_variant.slug if evaluator_variant else None), + ), ) references.evaluator_revision = Reference( id=evaluator_revision.id, @@ -1061,6 +1061,12 @@ async def create( references=_references, ) + span_name = ( + references.evaluator.slug + if references.evaluator and references.evaluator.slug + else "annotation" + ) + otel_links = await self.tracing_service.create_trace( organization_id=organization_id, project_id=project_id, @@ -1070,6 +1076,7 @@ async def create( trace_id=trace_id, span_id=span_id, span_type=SpanType.TASK, + span_name=span_name, attributes=_attributes, links=_links, ) diff --git a/web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioAnnotationPanel/index.tsx b/web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioAnnotationPanel/index.tsx index acbef4161a..47e53d303f 100644 --- a/web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioAnnotationPanel/index.tsx +++ b/web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioAnnotationPanel/index.tsx @@ -210,7 +210,7 @@ const ScenarioAnnotationPanel = ({ if (!hasValue) continue const references: Record = { - evaluator: {id: evaluator.id, slug: evaluator.slug}, + evaluator: {id: evaluator.id}, } if (testsetId) references.testset = {id: testsetId} if (testcaseId) references.testcase = {id: testcaseId} diff --git a/web/oss/src/components/SharedDrawers/AnnotateDrawer/assets/transforms.ts b/web/oss/src/components/SharedDrawers/AnnotateDrawer/assets/transforms.ts index 59ce05bd78..c2113441c3 100644 --- a/web/oss/src/components/SharedDrawers/AnnotateDrawer/assets/transforms.ts +++ b/web/oss/src/components/SharedDrawers/AnnotateDrawer/assets/transforms.ts @@ -470,7 +470,6 @@ export const generateNewAnnotationPayloadData = ({ const references: Record = { evaluator: { id: evaluator.id, - slug: evaluator.slug, }, } if (testsetId) references.testset = {id: testsetId} diff --git a/web/packages/agenta-annotation/src/state/controllers/annotationFormController.ts b/web/packages/agenta-annotation/src/state/controllers/annotationFormController.ts index 945a17a3b1..f2fa928756 100644 --- a/web/packages/agenta-annotation/src/state/controllers/annotationFormController.ts +++ b/web/packages/agenta-annotation/src/state/controllers/annotationFormController.ts @@ -1488,7 +1488,6 @@ const submitAnnotationsAtom = atom(null, async (get, set, payload: SubmitAnnotat references: { evaluator: { id: evalWorkflowId, - slug: evaluator.slug ?? undefined, }, ...(stepRefs?.evaluator_revision?.id ? {evaluator_revision: stepRefs.evaluator_revision} @@ -1526,7 +1525,6 @@ const submitAnnotationsAtom = atom(null, async (get, set, payload: SubmitAnnotat references: { evaluator: { id: evalWorkflowId, - slug: evaluator.slug ?? undefined, }, ...(stepRefs?.evaluator_revision?.id ? {evaluator_revision: stepRefs.evaluator_revision} From 2e0a8507757f5d88ec0a045e03ebeef547f77a4f Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 20 Apr 2026 15:42:13 +0200 Subject: [PATCH 07/13] Fix corrupted simple queries and live evaluations --- api/oss/src/core/evaluations/service.py | 127 ++++++++++++++++++++++++ api/oss/src/core/queries/service.py | 31 +++++- 2 files changed, 157 insertions(+), 1 deletion(-) diff --git a/api/oss/src/core/evaluations/service.py b/api/oss/src/core/evaluations/service.py index 39d9f60d79..610b597d03 100644 --- a/api/oss/src/core/evaluations/service.py +++ b/api/oss/src/core/evaluations/service.py @@ -156,6 +156,32 @@ def _first_reference_id( return None +def _is_invocation_query(data: Any) -> bool: + """Live evaluations require the query filter to target invocation traces. + + Returns True only when the query's filtering contains a top-level + condition with field="trace_type", operator="is", value="invocation". + """ + filtering = getattr(data, "filtering", None) + if filtering is None: + return False + + for condition in filtering.conditions or []: + field = getattr(condition, "field", None) + if field != "trace_type": + continue + + operator = getattr(condition, "operator", None) + if operator != "is": + continue + + value = getattr(condition, "value", None) + if value == "invocation": + return True + + return False + + class EvaluationsService: def __init__( self, @@ -209,6 +235,22 @@ async def refresh_runs( user_id = run.created_by_id try: + if not await self._is_live_run_valid( + project_id=project_id, + run=run, + ): + log.warning( + "[LIVE] Closing invalid live run (null data or non-invocation trace_type).", + project_id=project_id, + run_id=run.id, + ) + await self._close_live_run( + project_id=project_id, + user_id=user_id, + run=run, + ) + continue + log.info( "[LIVE] Dispatching...", project_id=project_id, @@ -239,6 +281,71 @@ async def refresh_runs( return True + async def _is_live_run_valid( + self, + *, + project_id: UUID, + run: EvaluationRun, + ) -> bool: + """Every query step must reference a revision with data targeting invocation traces.""" + if not run.data or not run.data.steps: + return False + + query_revision_ids: List[UUID] = [] + for step in run.data.steps: + query_ref = (step.references or {}).get("query_revision") + if isinstance(query_ref, Reference) and query_ref.id: + query_revision_ids.append(query_ref.id) + + if not query_revision_ids: + return False + + for query_revision_id in query_revision_ids: + query_revision = await self.queries_service.fetch_query_revision( + project_id=project_id, + # + query_revision_ref=Reference(id=query_revision_id), + ) + + if not query_revision or not query_revision.data: + return False + + if not _is_invocation_query(query_revision.data): + return False + + return True + + async def _close_live_run( + self, + *, + project_id: UUID, + user_id: UUID, + run: EvaluationRun, + ) -> None: + flags = run.flags.model_copy() if run.flags else EvaluationRunFlags() + flags.is_active = False + flags.is_closed = True + + await self.edit_run( + project_id=project_id, + user_id=user_id, + # + run=EvaluationRunEdit( + id=run.id, + # + name=run.name, + description=run.description, + # + flags=flags, + tags=run.tags, + meta=run.meta, + # + status=run.status, + # + data=run.data, + ), + ) + async def fetch_live_runs( self, *, @@ -1706,6 +1813,8 @@ async def create( evaluator_steps=evaluation.data.evaluator_steps, # repeats=evaluation.data.repeats, + # + is_live=evaluation.flags.is_live, ) if not run_data: @@ -1882,6 +1991,8 @@ async def edit( evaluator_steps=evaluation.data.evaluator_steps, # repeats=_evaluation.data.repeats, + # + is_live=(_evaluation.flags.is_live if _evaluation.flags else None), ) run_edit = EvaluationRunEdit( @@ -2351,6 +2462,8 @@ async def _make_evaluation_run_data( evaluator_steps: Optional[Target] = None, # repeats: Optional[int] = None, + # + is_live: Optional[bool] = None, ) -> Optional[EvaluationRunData]: # IMPLICIT FLAG: is_multivariate=False # IMPLICIT FLAG: all_inputs=True @@ -2385,6 +2498,20 @@ async def _make_evaluation_run_data( ) return None + if is_live and not query_revision.data: + log.warning( + "[EVAL] [run] [make] [failure] live evaluation requires query with data", + id=query_revision_ref.id, + ) + return None + + if is_live and not _is_invocation_query(query_revision.data): + log.warning( + "[EVAL] [run] [make] [failure] live evaluation requires trace_type=invocation", + id=query_revision_ref.id, + ) + return None + query_variant_ref = Reference(id=query_revision.variant_id) query_variant = await self.queries_service.fetch_query_variant( diff --git a/api/oss/src/core/queries/service.py b/api/oss/src/core/queries/service.py index d2c7bdd580..aeda7d8f4e 100644 --- a/api/oss/src/core/queries/service.py +++ b/api/oss/src/core/queries/service.py @@ -983,7 +983,36 @@ async def create( return None # ---------------------------------------------------------------------- - # Query revision + # Query revision (placeholder v0 — first revision has its fields nulled) + # ---------------------------------------------------------------------- + placeholder_revision_slug = uuid4().hex[-12:] + + _query_revision_create = QueryRevisionCreate( + slug=placeholder_revision_slug, + # + name=simple_query_create.name, + description=simple_query_create.description, + # + flags=simple_query_create.flags, + tags=simple_query_create.tags, + meta=simple_query_create.meta, + # + query_id=query.id, + query_variant_id=query_variant.id, + ) + + placeholder_revision = await self.queries_service.create_query_revision( + project_id=project_id, + user_id=user_id, + # + query_revision_create=_query_revision_create, + ) + + if placeholder_revision is None: + return None + + # ---------------------------------------------------------------------- + # Query revision (v1 — carries the actual data) # ---------------------------------------------------------------------- query_revision_slug = uuid4().hex[-12:] From 69bd8dfd781b91ec93b9134ced3ab4cbb4e49093 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 20 Apr 2026 15:50:58 +0200 Subject: [PATCH 08/13] Fix Online/Live Evals copy --- web/oss/src/components/EvalRunDetails/components/Page.tsx | 2 +- web/oss/src/components/pages/evaluations/EvaluationsView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/oss/src/components/EvalRunDetails/components/Page.tsx b/web/oss/src/components/EvalRunDetails/components/Page.tsx index 9784943a5c..ebad5df214 100644 --- a/web/oss/src/components/EvalRunDetails/components/Page.tsx +++ b/web/oss/src/components/EvalRunDetails/components/Page.tsx @@ -49,7 +49,7 @@ const EvalRunPreviewPage = ({runId, evaluationType, projectId = null}: EvalRunPr const typeMap: Record = { auto: {label: "Auto Evals", kind: "auto"}, human: {label: "Human Evals", kind: "human"}, - online: {label: "Online Evals", kind: "online"}, + online: {label: "Live Evals", kind: "online"}, } const config = typeMap[evaluationType] ?? {label: "Evaluations", kind: "auto"} return { diff --git a/web/oss/src/components/pages/evaluations/EvaluationsView.tsx b/web/oss/src/components/pages/evaluations/EvaluationsView.tsx index d55465fa1d..14e20368d4 100644 --- a/web/oss/src/components/pages/evaluations/EvaluationsView.tsx +++ b/web/oss/src/components/pages/evaluations/EvaluationsView.tsx @@ -37,7 +37,7 @@ const PROJECT_TAB_ITEMS: {key: AppTabKey; label: string}[] = [ {key: "all", label: "All Evals"}, {key: "auto", label: "Auto Evals"}, {key: "human", label: "Human Evals"}, - {key: "online", label: "Online Evals"}, + {key: "online", label: "Live Evals"}, {key: "custom", label: "SDK Evals"}, ] From 6c998a00d81b4b0ce27dfb6d3597c8bc9b9d361e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 20 Apr 2026 16:15:52 +0200 Subject: [PATCH 09/13] Fix updated Stripe field --- api/ee/src/apis/fastapi/billing/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/ee/src/apis/fastapi/billing/router.py b/api/ee/src/apis/fastapi/billing/router.py index 3215c91b1c..f0f43d078e 100644 --- a/api/ee/src/apis/fastapi/billing/router.py +++ b/api/ee/src/apis/fastapi/billing/router.py @@ -576,7 +576,7 @@ async def create_checkout( }, }, # - ui_mode="hosted", + ui_mode="hosted_page", success_url=success_url, ) From 517256970cd54c34f27a707fbac401e1a6ee0c19 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 20 Apr 2026 20:41:36 +0600 Subject: [PATCH 10/13] persist testset selection in localStorage to restore state across drawer sessions --- .../components/ConfigureEvaluator/atoms.ts | 32 +++++++++++++++++++ .../WorkflowRevisionDrawerWrapper/index.tsx | 31 +++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts index f36ec5ba13..e4f2cef6d6 100644 --- a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts +++ b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts @@ -50,6 +50,38 @@ export const persistedAppSelectionAtom = atom( }, ) +// ============================================================================ +// PERSISTED TESTSET SELECTION +// ============================================================================ + +interface PersistedTestsetSelection { + revisionId: string + testsetId: string | null + sourceName: string | null +} + +const persistedTestsetSelectionByProjectAtom = atomWithStorage< + Record +>("agenta:evaluator:selected-testset", {}) + +export const persistedTestsetSelectionAtom = atom( + (get) => { + const projectId = get(projectIdAtom) || "__global__" + const all = get(persistedTestsetSelectionByProjectAtom) + return all[projectId] ?? null + }, + (get, set, next: PersistedTestsetSelection | null) => { + const projectId = get(projectIdAtom) || "__global__" + const all = get(persistedTestsetSelectionByProjectAtom) + if (next) { + set(persistedTestsetSelectionByProjectAtom, {...all, [projectId]: next}) + } else { + const {[projectId]: _, ...rest} = all + set(persistedTestsetSelectionByProjectAtom, rest) + } + }, +) + // ============================================================================ // DERIVED SELECTORS // ============================================================================ diff --git a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx index 853a2d5c3e..72a1a2cd3b 100644 --- a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx +++ b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx @@ -57,6 +57,7 @@ import SimpleSharedEditor from "@/oss/components/EditorViews/SimpleSharedEditor" import { connectAppToEvaluatorAtom, persistedAppSelectionAtom, + persistedTestsetSelectionAtom, selectedAppLabelAtom, } from "@/oss/components/Evaluators/components/ConfigureEvaluator/atoms" import EvaluatorPlaygroundHeader from "@/oss/components/Evaluators/components/ConfigureEvaluator/EvaluatorPlaygroundHeader" @@ -184,13 +185,15 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { const setSelectedAppLabel = useSetAtom(selectedAppLabelAtom) const setConnectedTestset = useSetAtom(connectedTestsetAtom) const connectApp = useSetAtom(connectAppToEvaluatorAtom) + const setPersistedTestset = useSetAtom(persistedTestsetSelectionAtom) useEffect(() => { if (entityId) { addPrimaryNode({type: "workflow", id: entityId, label: "Evaluator"}) setInitialized(true) - // Restore persisted app selection (survives drawer close/reopen and commits) const store = getDefaultStore() + + // Restore persisted app selection (survives drawer close/reopen and commits) const persisted = store.get(persistedAppSelectionAtom) if (persisted) { setSelectedAppLabel(persisted.appLabel) @@ -201,6 +204,16 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { evaluatorLabel: "Evaluator", }) } + + // Restore persisted testset selection — fetches fresh rows, no stale outputs + const persistedTestset = store.get(persistedTestsetSelectionAtom) + if (persistedTestset) { + store.set(playgroundController.actions.restoreLoadableConnection, { + revisionId: persistedTestset.revisionId, + sourceName: persistedTestset.sourceName, + testsetId: persistedTestset.testsetId, + }) + } } return () => { const store = getDefaultStore() @@ -250,6 +263,22 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { connectApp, ]) + // Save testset connection to localStorage whenever the user connects a testset + const connectedTestset = useAtomValue(connectedTestsetAtom) + useEffect(() => { + if (!connectedTestset) return + const store = getDefaultStore() + const loadableId = store.get(derivedLoadableIdAtom) + if (!loadableId) return + const loadableState = store.get(loadableStateAtomFamily(loadableId)) + if (!loadableState.connectedSourceId) return + setPersistedTestset({ + revisionId: loadableState.connectedSourceId, + testsetId: connectedTestset.id, + sourceName: connectedTestset.name ?? null, + }) + }, [connectedTestset, setPersistedTestset]) + const selectedAppLabel = useAtomValue(selectedAppLabelAtom) const nodes = useAtomValue(useMemo(() => playgroundController.selectors.nodes(), [])) From 839c4c97fb4ab10f5dcfe2d03dc0e4d4f7a1b112 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 20 Apr 2026 21:00:16 +0600 Subject: [PATCH 11/13] feat: persist and restore exact testcase data in workflow revision drawer --- .../components/ConfigureEvaluator/atoms.ts | 1 + .../WorkflowRevisionDrawerWrapper/index.tsx | 28 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts index e4f2cef6d6..fdbd5d271b 100644 --- a/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts +++ b/web/oss/src/components/Evaluators/components/ConfigureEvaluator/atoms.ts @@ -58,6 +58,7 @@ interface PersistedTestsetSelection { revisionId: string testsetId: string | null sourceName: string | null + testcases: ({id: string} & Record)[] } const persistedTestsetSelectionByProjectAtom = atomWithStorage< diff --git a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx index 72a1a2cd3b..ce3712600f 100644 --- a/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx +++ b/web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx @@ -205,14 +205,19 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { }) } - // Restore persisted testset selection — fetches fresh rows, no stale outputs + // Restore the exact testcases that were loaded (same path as TestsetDropdown) const persistedTestset = store.get(persistedTestsetSelectionAtom) - if (persistedTestset) { - store.set(playgroundController.actions.restoreLoadableConnection, { - revisionId: persistedTestset.revisionId, - sourceName: persistedTestset.sourceName, - testsetId: persistedTestset.testsetId, - }) + if (persistedTestset?.testcases?.length) { + const loadableId = store.get(derivedLoadableIdAtom) + if (loadableId) { + store.set(playgroundController.actions.connectToTestset, { + loadableId, + revisionId: persistedTestset.revisionId, + testcases: persistedTestset.testcases, + testsetName: persistedTestset.sourceName ?? undefined, + testsetId: persistedTestset.testsetId ?? undefined, + }) + } } } return () => { @@ -272,10 +277,19 @@ const DrawerEvaluatorPlayground = memo(({entityId}: {entityId: string}) => { if (!loadableId) return const loadableState = store.get(loadableStateAtomFamily(loadableId)) if (!loadableState.connectedSourceId) return + const rowIds = store.get(testcaseMolecule.atoms.displayRowIds) + const testcases = rowIds + .map((id) => { + const entity = testcaseMolecule.get.data(id) + return entity ? ({...entity, id} as {id: string} & Record) : null + }) + .filter((t): t is {id: string} & Record => t !== null) + setPersistedTestset({ revisionId: loadableState.connectedSourceId, testsetId: connectedTestset.id, sourceName: connectedTestset.name ?? null, + testcases, }) }, [connectedTestset, setPersistedTestset]) From a3643f65b407e13bd9d80910f1e771969cc22829 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 20 Apr 2026 21:44:19 +0600 Subject: [PATCH 12/13] fix: hide deployment options for evaluators --- .../CommitVariantChangesModal/index.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx b/web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx index c0c7aaad1e..cd22d1e1e4 100644 --- a/web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx +++ b/web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx @@ -271,20 +271,24 @@ const CommitVariantChangesModal: React.FC = ({
)} - setShouldDeploy(e.target.checked)} - > - Deploy after commit - + {!isEvaluator && ( + <> + setShouldDeploy(e.target.checked)} + > + Deploy after commit + - {shouldDeploy && ( - setSelectedEnvironment(value)} + options={environmentOptions} + /> + )} + )}
)} From b580001b073e7eb095ff46c34ffcb491be6b4319 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 21 Apr 2026 00:03:57 +0600 Subject: [PATCH 13/13] fix style --- .../src/components/AnnotationSession/FocusView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/packages/agenta-annotation-ui/src/components/AnnotationSession/FocusView.tsx b/web/packages/agenta-annotation-ui/src/components/AnnotationSession/FocusView.tsx index 61af9cffb7..b25958c029 100644 --- a/web/packages/agenta-annotation-ui/src/components/AnnotationSession/FocusView.tsx +++ b/web/packages/agenta-annotation-ui/src/components/AnnotationSession/FocusView.tsx @@ -273,7 +273,7 @@ const FocusView = memo(function FocusView({
{/* Main content area */}
-
+
{/* Scenario content */}
{/* Annotation panel */} -
+