diff --git a/frontend/lib/chat/agents/base-agent.ts b/frontend/lib/chat/agents/base-agent.ts new file mode 100644 index 00000000..8a150099 --- /dev/null +++ b/frontend/lib/chat/agents/base-agent.ts @@ -0,0 +1,167 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { z } from "zod"; +import { generateText } from "ai"; + +export abstract class BaseAgent { + abstract readonly name: string; + abstract readonly outputSchema: z.ZodSchema; + abstract readonly temperature: number; + abstract readonly maxSteps: number; + + /** + * Generates JSON format instructions based on the Zod schema + */ + protected generateJSONInstructions(): string { + try { + const schemaShape = this.getSchemaShape(this.outputSchema); + return `\n\nReturn your response as a JSON object with this exact structure:\n${JSON.stringify( + schemaShape, + null, + 2 + )}\n\nDo not include any other text in your response, only the JSON object.`; + } catch (error) { + return "\n\nReturn your response as a valid JSON object."; + } + } + + /** + * Extracts the shape of a Zod schema for documentation + */ + private getSchemaShape(schema: z.ZodSchema): any { + if (schema instanceof z.ZodObject) { + const shape: any = {}; + const schemaShape = (schema as any).shape; + + for (const key in schemaShape) { + const field = schemaShape[key]; + if (field instanceof z.ZodString) { + shape[key] = field.description || "string"; + } else if (field instanceof z.ZodNumber) { + shape[key] = field.description || "number"; + } else if (field instanceof z.ZodBoolean) { + shape[key] = field.description || "boolean"; + } else if (field instanceof z.ZodArray) { + const elementType = this.getFieldDescription(field._def.type); + shape[key] = field.description || `array of ${elementType}`; + } else if (field instanceof z.ZodEnum) { + const values = (field as any)._def.values; + shape[key] = field.description || `one of: ${values.join(" | ")}`; + } else if (field instanceof z.ZodOptional || field instanceof z.ZodNullable) { + const innerType = (field as any)._def.innerType; + shape[key] = `${this.getFieldDescription(innerType)} (optional)`; + } else if (field instanceof z.ZodAny) { + shape[key] = field.description || "any value"; + } else { + shape[key] = field.description || "value"; + } + } + + return shape; + } + + return {}; + } + + private getFieldDescription(field: any): string { + if (field instanceof z.ZodString) return "string"; + if (field instanceof z.ZodNumber) return "number"; + if (field instanceof z.ZodBoolean) return "boolean"; + if (field instanceof z.ZodAny) return "any"; + return "value"; + } + + async execute(input: TInput): Promise { + try { + const systemPrompt = await this.getSystemPrompt(input); + const userPrompt = this.getUserPrompt(input); + const tools = this.getTools(input); + + // Append JSON format instructions to system prompt + const jsonInstructions = this.generateJSONInstructions(); + const fullSystemPrompt = systemPrompt + jsonInstructions; + + // Check if we have messages in the input + const hasMessages = typeof input === 'object' && input !== null && 'messages' in input && Array.isArray((input as any).messages); + + const generateConfig: any = { + model: this.getModel(input), + system: fullSystemPrompt, + tools, + maxSteps: this.maxSteps, + temperature: this.temperature, + }; + + // Add any provider-specific options + const providerOptions = this.getProviderOptions(input); + if (providerOptions) { + generateConfig.providerOptions = providerOptions; + } + + // Use messages if available, otherwise use prompt + if (hasMessages) { + generateConfig.messages = (input as any).messages; + } else { + generateConfig.prompt = userPrompt; + } + + + const response = await generateText(generateConfig); + + + // Extract and validate JSON from response + return this.extractJSON(response.text); + } catch (error) { + throw this.createError(error); + } + } + + /** + * Extract and validate JSON from the response text + */ + protected extractJSON(text: string): TOutput { + // Remove markdown code block wrappers if present + let cleanText = text.replaceAll(/```json\s*\n?/g, ""); + cleanText = cleanText.replaceAll(/\n?```/g, ""); + + // Try to find JSON in the response + const jsonMatch = cleanText.match(/\{[\s\S]*\}/); + if (!jsonMatch) { + console.error("No JSON found in the response"); + console.error(text); + throw new Error(`${this.name} agent did not return valid JSON`); + } + + const jsonStr = jsonMatch[0]; + let parsedOutput: any; + + try { + parsedOutput = JSON.parse(jsonStr); + } catch (error) { + throw new Error(`Failed to parse ${this.name} JSON: ${error}`); + } + + // Validate against schema + try { + const validatedOutput = this.outputSchema.parse(parsedOutput); + console.log(`${this.name} Agent Output:`, validatedOutput); + return validatedOutput; + } catch (error) { + console.error(`Failed to validate ${this.name} JSON`, error); + throw new Error(`Failed to validate ${this.name} JSON: ${error}`); + } + } + + protected abstract getModel(input: TInput): any; + protected abstract getSystemPrompt(input: TInput): string | Promise; + protected abstract getUserPrompt(input: TInput): string; + protected abstract getTools(input: TInput): Record; + protected abstract createError(error: unknown): Error; + + /** + * Override this method to provide provider-specific options + */ + protected getProviderOptions(_input: TInput): any { + return undefined; + } +} \ No newline at end of file diff --git a/frontend/lib/chat/agents/pipe.ts b/frontend/lib/chat/agents/pipe.ts new file mode 100644 index 00000000..6054881e --- /dev/null +++ b/frontend/lib/chat/agents/pipe.ts @@ -0,0 +1,72 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { z } from 'zod'; +import { pipePrompt } from '../prompts/pipe'; +import { BaseAgent } from './base-agent'; + +// Output schema for pipe agent +export const pipeOutputSchema = z.object({ + tools: z.array(z.string()).describe("Ordered array of tools used to fetch the data"), + explanation: z.string().describe("Brief explanation of why these tools answer the question"), + data: z.any().describe("The actual data returned from executing the tools") +}); + +export type PipeOutput = z.infer; + +interface PipeAgentInput { + model: any; // Bedrock model instance + messages: any[]; + tools: Record; // Filtered pipe tools based on router decision + date: string; + projectName: string; + pipe: string; + parametersString: string; + segmentId: string | null; + reformulatedQuestion: string; + toolNames: string[]; // Array of tool names from router +} + +export class PipeAgent extends BaseAgent { + readonly name = "Pipe"; + readonly outputSchema = pipeOutputSchema; + readonly temperature = 0; + readonly maxSteps = 10; + + protected getModel(input: PipeAgentInput): any { + return input.model; + } + + protected getSystemPrompt(input: PipeAgentInput): string { + return pipePrompt( + input.date, + input.projectName, + input.pipe, + input.parametersString, + input.segmentId, + input.reformulatedQuestion, + input.toolNames + ); + } + + protected getUserPrompt(_input: PipeAgentInput): string { + // Not used when messages are provided, but required by base class + return _input.reformulatedQuestion; + } + + protected getTools(input: PipeAgentInput): Record { + return input.tools; + } + + protected createError(error: unknown): Error { + if (error instanceof Error) { + return new Error(`Pipe agent error: ${error.message}`); + } + return new Error(`Pipe agent error: ${String(error)}`); + } +} + +// Convenience function to maintain backward compatibility +export async function runPipeAgent(params: PipeAgentInput): Promise { + const agent = new PipeAgent(); + return agent.execute(params); +} \ No newline at end of file diff --git a/frontend/lib/chat/agents/router.ts b/frontend/lib/chat/agents/router.ts new file mode 100644 index 00000000..a52588f5 --- /dev/null +++ b/frontend/lib/chat/agents/router.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { z } from 'zod'; +import { routerPrompt } from '../prompts/router'; +import { BaseAgent } from './base-agent'; + +// Output schema for router decisions +export const routerOutputSchema = z.object({ + next_action: z.enum(["stop", "create_query", "pipes"]), + reasoning: z.string().describe("Maximum 2 sentences explaining the decision"), + reformulated_question: z.string().describe("Enhanced query with all parameters"), + tools: z.array(z.string()).describe("Tools needed for next agent") +}); + +export type RouterOutput = z.infer; + +interface RouterAgentInput { + model: any; // Bedrock model instance + messages: any[]; + tools: Record; // Only list_datasources + date: string; + projectName: string; + pipe: string; + parametersString: string; + segmentId: string | null; +} + +export class RouterAgent extends BaseAgent { + readonly name = "Router"; + readonly outputSchema = routerOutputSchema; + readonly temperature = 0; + readonly maxSteps = 3; // Allow router to use list_datasources if needed + + protected getModel(input: RouterAgentInput): any { + return input.model; + } + + protected getSystemPrompt(input: RouterAgentInput): string { + return routerPrompt( + input.date, + input.projectName, + input.pipe, + input.parametersString, + input.segmentId + ); + } + + protected getUserPrompt(_input: RouterAgentInput): string { + // Not used when messages are provided, but required by base class + return ""; + } + + protected getTools(input: RouterAgentInput): Record { + return input.tools; + } + + protected createError(error: unknown): Error { + if (error instanceof Error) { + return new Error(`Router agent error: ${error.message}`); + } + return new Error(`Router agent error: ${String(error)}`); + } + +} + +// Convenience function to maintain backward compatibility +export async function runRouterAgent(params: RouterAgentInput): Promise { + const agent = new RouterAgent(); + return agent.execute(params); +} \ No newline at end of file diff --git a/frontend/lib/chat/agents/text-to-sql.ts b/frontend/lib/chat/agents/text-to-sql.ts new file mode 100644 index 00000000..0a1dddcb --- /dev/null +++ b/frontend/lib/chat/agents/text-to-sql.ts @@ -0,0 +1,78 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { z } from 'zod'; +import { textToSqlPrompt } from '../prompts/text-to-sql'; +import { BaseAgent } from './base-agent'; + +// Output schema for SQL agent +export const sqlOutputSchema = z.object({ + sql: z.string().describe("The SQL query used to fetch the data"), + explanation: z.string().describe("Brief explanation of why this query answers the question"), + data: z.any().describe("The actual data returned from executing the query") +}); + +export type SqlOutput = z.infer; + +interface TextToSqlAgentInput { + model: any; // Bedrock model instance + messages: any[]; + tools: Record; // list_datasources and execute_query + date: string; + projectName: string; + pipe: string; + parametersString: string; + segmentId: string | null; + reformulatedQuestion: string; +} + +export class TextToSqlAgent extends BaseAgent { + readonly name = "SQL"; + readonly outputSchema = sqlOutputSchema; + readonly temperature = 0; + readonly maxSteps = 10; // Allow multiple steps for SQL generation and execution + + protected getModel(input: TextToSqlAgentInput): any { + return input.model; + } + + protected getSystemPrompt(input: TextToSqlAgentInput): string { + return textToSqlPrompt( + input.date, + input.projectName, + input.pipe, + input.parametersString, + input.segmentId, + input.reformulatedQuestion + ); + } + + protected getUserPrompt(input: TextToSqlAgentInput): string { + // Not used when messages are provided, but required by base class + return input.reformulatedQuestion; + } + + protected getTools(input: TextToSqlAgentInput): Record { + return input.tools; + } + + protected createError(error: unknown): Error { + if (error instanceof Error) { + return new Error(`SQL agent error: ${error.message}`); + } + return new Error(`SQL agent error: ${String(error)}`); + } + + protected getProviderOptions(_input: TextToSqlAgentInput): any { + return { + bedrock: { + reasoningConfig: { type: "enabled", budgetTokens: 3000 }, + }, + }; + } +} + +// Convenience function to maintain backward compatibility +export async function runTextToSqlAgent(params: TextToSqlAgentInput): Promise { + const agent = new TextToSqlAgent(); + return agent.execute(params); +} \ No newline at end of file diff --git a/frontend/lib/chat/chart/analysis.ts b/frontend/lib/chat/chart/analysis.ts new file mode 100644 index 00000000..eb2d6f2f --- /dev/null +++ b/frontend/lib/chat/chart/analysis.ts @@ -0,0 +1,197 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import type { Result } from "./types"; + +export interface DataProfile { + rowCount: number; + columns: ColumnProfile[]; + dataShape: 'single-value' | 'time-series' | 'categorical' | 'multi-dimensional'; + pivotNeeded: boolean; + pivotConfig?: { + groupBy: string; + categories: string; + values: string; + }; +} + +export interface ColumnProfile { + name: string; + type: 'numeric' | 'date' | 'category' | 'text'; + uniqueCount: number; + isGroupable: boolean; + sample: any[]; + role?: 'dimension' | 'measure' | 'time'; +} + +export function analyzeDataForChart( + results: Result[], + userQuestion: string = "" +): DataProfile | null { + if (!results.length) return null; + + const columns = Object.keys(results[0]); + const columnProfiles = analyzeColumns(results, columns); + const dataShape = detectDataShape(results, columnProfiles); + + const pivotAnalysis = detectPivotNeeds(results, columnProfiles); + + return { + rowCount: results.length, + columns: columnProfiles, + dataShape, + pivotNeeded: pivotAnalysis.needed, + pivotConfig: pivotAnalysis.config, + }; +} + +function analyzeColumns(results: Result[], columns: string[]): ColumnProfile[] { + return columns.map(col => { + const values = results.map(row => row[col]); + const uniqueValues = new Set(values.map(v => String(v))); + const numericValues = values.filter(v => typeof v === 'number' || (!isNaN(Number(v)) && v !== '' && v !== null)); + + // Detect column type + let type: ColumnProfile['type'] = 'text'; + let role: ColumnProfile['role'] | undefined; + + if (numericValues.length > values.length * 0.8) { + type = 'numeric'; + role = 'measure'; + } else if (isDateColumn(values)) { + type = 'date'; + role = 'time'; + } else if (uniqueValues.size <= 20 && uniqueValues.size < values.length * 2) { + // Consider as category if: + // - Has 20 or fewer unique values AND + // - Not every row has a different value (allows some duplicates) + type = 'category'; + role = 'dimension'; + } + + return { + name: col, + type, + uniqueCount: uniqueValues.size, + isGroupable: type === 'category' && uniqueValues.size < 20, + sample: Array.from(uniqueValues).slice(0, 3), + role, + }; + }); +} + +function isDateColumn(values: any[]): boolean { + const dateFormats = [ + /^\d{4}-\d{2}-\d{2}/, // YYYY-MM-DD + /^\d{4}-\d{2}/, // YYYY-MM + /^\d{2}\/\d{2}\/\d{4}/, // MM/DD/YYYY + ]; + + const dateCount = values.filter(v => { + const str = String(v); + return dateFormats.some(format => format.test(str)) || !isNaN(Date.parse(str)); + }).length; + + return dateCount > values.length * 0.6; +} + +function detectDataShape(results: Result[], columns: ColumnProfile[]): DataProfile['dataShape'] { + if (results.length === 1) return 'single-value'; + + const dateColumns = columns.filter(c => c.type === 'date'); + const numericColumns = columns.filter(c => c.type === 'numeric'); + const categoryColumns = columns.filter(c => c.type === 'category'); + + if (dateColumns.length > 0) return 'time-series'; + if (categoryColumns.length === 1 && numericColumns.length >= 1) return 'categorical'; + if (categoryColumns.length >= 2 && numericColumns.length >= 1) return 'multi-dimensional'; + + return 'categorical'; +} + + +function detectPivotNeeds(results: Result[], columns: ColumnProfile[]) { + if (columns.length !== 3) return { needed: false }; + + const possibleGroupCol = columns.find(c => + c.type === 'category' && c.uniqueCount < results.length * 0.5 + ); + + const possibleCategoryCol = columns.find(c => + c.type === 'category' && + c !== possibleGroupCol && + c.uniqueCount < 20 + ); + + const possibleValueCol = columns.find(c => c.type === 'numeric'); + + if (possibleGroupCol && possibleCategoryCol && possibleValueCol) { + return { + needed: true, + config: { + groupBy: possibleGroupCol.name, + categories: possibleCategoryCol.name, + values: possibleValueCol.name, + } + }; + } + + return { needed: false }; +} + +export function pivotLongToWide( + data: Result[], + groupColumn: string, + categoryColumn: string, + valueColumn: string +): Result[] { + if (!data || data.length === 0) return data; + + const categories = Array.from(new Set(data.map(row => String(row[categoryColumn])))).sort(); + + const groupedData = data.reduce((acc, row) => { + const groupKey = String(row[groupColumn]); + if (!acc[groupKey]) { + acc[groupKey] = { [groupColumn]: groupKey }; + } + + const categoryKey = String(row[categoryColumn]); + const value = Number(row[valueColumn]) || 0; + const cleanCategoryKey = categoryKey.toLowerCase().replace(/[^a-z0-9]/g, '_'); + acc[groupKey][cleanCategoryKey] = value; + + return acc; + }, {} as Record); + + return Object.values(groupedData).map(group => { + const result = { ...group }; + categories.forEach(category => { + const cleanCategoryKey = category.toLowerCase().replace(/[^a-z0-9]/g, '_'); + if (!(cleanCategoryKey in result)) { + result[cleanCategoryKey] = 0; + } + }); + return result; + }); +} + +export function shouldStackBars(profile: DataProfile): boolean { + const yColumns = profile.columns + .filter(c => c.type === 'numeric') + .map(c => c.name.toLowerCase()); + + const stackingPatterns = [ + ['bronze', 'silver', 'gold'], + ['small', 'medium', 'large'], + ['low', 'medium', 'high'], + ['q1', 'q2', 'q3', 'q4'], + ]; + + for (const pattern of stackingPatterns) { + const matches = pattern.filter(p => + yColumns.some(col => col.includes(p)) + ); + if (matches.length >= 2) return true; + } + + return false; +} \ No newline at end of file diff --git a/frontend/lib/chat/chart/generator.ts b/frontend/lib/chat/chart/generator.ts new file mode 100644 index 00000000..1298c539 --- /dev/null +++ b/frontend/lib/chat/chart/generator.ts @@ -0,0 +1,179 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'; +import { generateObject } from "ai"; +import { configSchema } from "./types"; +import type { Config, Result } from "./types"; +import { analyzeDataForChart, shouldStackBars } from "./analysis"; + +const bedrock = createAmazonBedrock({ + accessKeyId: process.env.NUXT_AWS_BEDROCK_ACCESS_KEY_ID, + secretAccessKey: process.env.NUXT_AWS_BEDROCK_SECRET_ACCESS_KEY, + region: process.env.NUXT_AWS_BEDROCK_REGION, +}); + +const model = bedrock("us.anthropic.claude-sonnet-4-20250514-v1:0"); + +export async function generateChartConfig( + results: Result[], + userQuery: string +): Promise<{ config: Config | null; isMetric?: boolean }> { + const dataProfile = analyzeDataForChart(results, userQuery); + + // Skip chart for single values - show as metric + if (!dataProfile || dataProfile.dataShape === "single-value") { + return { config: null, isMetric: true }; + } + + try { + const { object: config } = await generateObject({ + model, + output: "object" as const, + schema: configSchema, + system: "You are a data visualization expert. Create simple, effective chart configurations.", + prompt: createChartGenerationPrompt(dataProfile, results, userQuery), + temperature: 0.3, + }); + + // Apply default colors + const colors = generateDefaultColors(config.yKeys); + const finalConfig = { ...config, colors }; + + return { config: finalConfig }; + } catch (e) { + console.error("Chart generation failed, using fallback", e); + // Smart fallback + const fallbackConfig = generateFallbackConfig(dataProfile); + return { config: fallbackConfig }; + } +} + +export async function modifyChartConfig( + currentConfig: Config, + currentData: Result[], + userRequest: string +): Promise { + const dataProfile = analyzeDataForChart(currentData, userRequest); + + try { + const { object: newConfig } = await generateObject({ + model, + output: "object" as const, + schema: configSchema, + system: "You are a chart modification expert. Make minimal changes based on user requests.", + prompt: createChartModificationPrompt(currentConfig, dataProfile, currentData, userRequest), + temperature: 0.3, + }); + + // Preserve colors if not updated + if (!newConfig.customColors && !newConfig.colors) { + newConfig.colors = currentConfig.colors; + } + + return newConfig; + } catch (e) { + console.error("Failed to modify chart config", e); + throw new Error("Failed to modify chart configuration"); + } +} + +function generateDefaultColors(yKeys: string[]): Record { + const colors: Record = {}; + yKeys.forEach((key, index) => { + colors[key] = `hsl(var(--chart-${index + 1}))`; + }); + return colors; +} + +function generateFallbackConfig(profile: any): Config { + // Choose appropriate chart type based on data shape + let type: Config["type"] = "bar"; + if (profile.dataShape === "time-series") { + type = "line"; + } else if (profile.dataShape === "categorical" && profile.rowCount <= 6) { + type = "pie"; + } + + // Find best columns for axes + const dateCol = profile.columns.find((c: any) => c.type === "date"); + const categoryCol = profile.columns.find((c: any) => c.type === "category"); + const numericCols = profile.columns.filter((c: any) => c.type === "numeric"); + + const xKey = dateCol?.name || categoryCol?.name || profile.columns[0].name; + const yKeys = + numericCols.length > 0 + ? numericCols.map((c: any) => c.name) + : [ + profile.columns.find((c: any) => c.name !== xKey)?.name || + profile.columns[1]?.name, + ]; + + const config: Config = { + type, + title: `Data Visualization`, + xKey, + yKeys, + legend: yKeys.length > 1, + }; + + // Add pivot config if needed + if (profile.pivotNeeded && profile.pivotConfig) { + config.pivotData = { + enabled: true, + ...profile.pivotConfig, + }; + } + + // Auto-detect stacking + if (type === "bar" && yKeys.length > 1) { + config.stacked = shouldStackBars(profile); + } + + // Add colors + config.colors = generateDefaultColors(yKeys); + + return config; +} + +function createChartGenerationPrompt(dataProfile: any, results: Result[], userQuery: string): string { + const columns = dataProfile.columns.map((c: any) => `${c.name} (${c.type})`).join(", "); + const sampleData = JSON.stringify(results.slice(0, 3), null, 2); + + return `Based on this data analysis and user query, generate an optimal chart configuration. + +User Query: ${userQuery} +Data Shape: ${dataProfile.dataShape} +Row Count: ${dataProfile.rowCount} +Columns: ${columns} +Sample Data: ${sampleData} + +Create a chart configuration that: +1. Uses the appropriate chart type (bar, line, area, or pie) +2. Selects the right columns for x and y axes +3. Includes a clear, descriptive title +4. Enables features like stacking or legends when beneficial +5. Considers data pivoting if needed for better visualization + +Return a valid chart configuration object.`; +} + +function createChartModificationPrompt(currentConfig: Config, dataProfile: any, currentData: Result[], userRequest: string): string { + return `Modify the existing chart configuration based on the user's request. + +Current Configuration: +${JSON.stringify(currentConfig, null, 2)} + +User Request: ${userRequest} + +Data Shape: ${dataProfile?.dataShape || 'unknown'} +Row Count: ${currentData.length} + +Make minimal changes to satisfy the user's request. Common modifications: +- Change chart type (bar, line, area, pie) +- Toggle stacking on/off +- Change which columns are used +- Update the title +- Toggle legend visibility + +Return a complete, valid chart configuration object.`; +} \ No newline at end of file diff --git a/frontend/lib/chat/chart/types.ts b/frontend/lib/chat/chart/types.ts new file mode 100644 index 00000000..324e82f1 --- /dev/null +++ b/frontend/lib/chat/chart/types.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { z } from "zod"; + +export type Result = Record; + +export const configSchema = z.object({ + type: z.enum(['bar', 'line', 'area', 'pie']), + title: z.string(), + xKey: z.string(), + yKeys: z.array(z.string()), + stacked: z.boolean().optional(), + legend: z.boolean().optional(), + colors: z.record(z.string(), z.string()).optional(), + customColors: z.array(z.string()).optional(), + pivotData: z.object({ + enabled: z.boolean(), + groupColumn: z.string(), + categoryColumn: z.string(), + valueColumn: z.string(), + }).optional(), +}); + +export type Config = z.infer; \ No newline at end of file diff --git a/frontend/lib/chat/data-copilot.ts b/frontend/lib/chat/data-copilot.ts new file mode 100644 index 00000000..65a8f9f9 --- /dev/null +++ b/frontend/lib/chat/data-copilot.ts @@ -0,0 +1,151 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'; +import { streamText, experimental_createMCPClient as createMCPClient, createDataStreamResponse } from 'ai'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; + +import { runRouterAgent } from './agents/router'; +import { runTextToSqlAgent } from './agents/text-to-sql'; +import { runPipeAgent } from './agents/pipe'; + +const bedrock = createAmazonBedrock({ + accessKeyId: process.env.NUXT_AWS_BEDROCK_ACCESS_KEY_ID, + secretAccessKey: process.env.NUXT_AWS_BEDROCK_SECRET_ACCESS_KEY, + region: process.env.NUXT_AWS_BEDROCK_REGION, +}); + +export async function streamingAgentRequestHandler({ + messages, + segmentId, + projectName, + pipe, + parameters, +}: { + messages: any[]; + segmentId?: string; + projectName?: string; + pipe: string; + parameters?: Record; +}) { + const url = new URL( + `https://mcp.tinybird.co?token=${process.env.NUXT_INSIGHTS_TINYBIRD_TOKEN}&host=${process.env.NUXT_TINYBIRD_BASE_URL}` + ); + + const mcpClient = await createMCPClient({ + transport: new StreamableHTTPClientTransport(url, { + sessionId: `session_${Date.now()}`, + }), + }); + + const tbTools = await mcpClient.tools({}); + const parametersString = JSON.stringify(parameters || {}); + const dateString = new Date().toISOString().split("T")[0]; + const model = bedrock("us.anthropic.claude-sonnet-4-20250514-v1:0"); + + return createDataStreamResponse({ + execute: async (dataStream) => { + try { + dataStream.writeData({ + type: "router-status", + status: "analyzing", + }); + + const routerOutput = await runRouterAgent({ + model, + messages, + tools: tbTools, + date: dateString as string, + projectName: projectName as string, + pipe, + parametersString, + segmentId: segmentId as string, + }); + + if (routerOutput.next_action === "stop") { + dataStream.writeData({ + type: "router-status", + status: "complete", + reasoning: routerOutput.reasoning, + }); + + const result = streamText({ + model, + messages: [ + ...messages, + { + role: "assistant", + content: routerOutput.reasoning || "I cannot answer this question based on the available data.", + }, + ], + }); + + result.mergeIntoDataStream(dataStream); + return; + } + + dataStream.writeData({ + type: "router-status", + status: "complete", + reasoning: routerOutput.reasoning, + reformulatedQuestion: routerOutput.reformulated_question, + }); + + const followUpTools: Record = {}; + for (const toolName of routerOutput.tools) { + if (tbTools[toolName]) { + followUpTools[toolName] = tbTools[toolName]; + } + } + + if (routerOutput.next_action === "create_query") { + const sqlOutput = await runTextToSqlAgent({ + model, + messages, + tools: followUpTools, + date: dateString as string, + projectName: projectName as string, + pipe, + parametersString, + segmentId: segmentId as string, + reformulatedQuestion: routerOutput.reformulated_question, + }); + + dataStream.writeData({ + type: "sql-result", + sql: sqlOutput.sql, + explanation: sqlOutput.explanation, + data: sqlOutput.data + }); + } else if (routerOutput.next_action === "pipes") { + const pipeOutput = await runPipeAgent({ + model, + messages, + tools: followUpTools, + date: dateString as string, + projectName: projectName as string, + pipe, + parametersString, + segmentId: segmentId as string, + reformulatedQuestion: routerOutput.reformulated_question, + toolNames: routerOutput.tools, + }); + + dataStream.writeData({ + type: "pipe-result", + tools: pipeOutput.tools, + explanation: pipeOutput.explanation, + data: pipeOutput.data + }); + } + } catch (error) { + dataStream.writeData({ + type: "router-status", + status: "error", + error: error instanceof Error ? error.message : "An error occurred", + }); + + throw error; + } + }, + }); +} diff --git a/frontend/lib/chat/prompts/pipe.ts b/frontend/lib/chat/prompts/pipe.ts new file mode 100644 index 00000000..c4cafd64 --- /dev/null +++ b/frontend/lib/chat/prompts/pipe.ts @@ -0,0 +1,65 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +export const pipePrompt = ( + date: string, + projectName: string, + pipe: string, + parametersString: string, + segmentId: string | null, + reformulatedQuestion: string, + tools: string[] +) => ` +You are a pipe tool specialist that executes Tinybird tools to answer: "${reformulatedQuestion}" + +# DATE AND CONTEXT +Today's date: ${date} +Current dashboard: Project "${projectName}" using ${pipe} tool with parameters: ${parametersString} +Segment ID: ${segmentId || "not specified"} + +# AVAILABLE TOOLS +${tools.join(", ")} + +# YOUR APPROACH + +**SELECT AND EXECUTE TOOLS** +- Use ${pipe} with different parameters if it can answer the question +- Use other available tools if they're more appropriate +- Combine multiple tools if needed for comprehensive answers +- Execute tools in logical order with precise parameters + +**RESPONSE GUIDELINES** +- Execute the necessary tools to answer the question +- Combine results from multiple tools if needed +- Return data in CSV format with proper headers +- Provide a brief explanation of your tool selection + +# QUERY ENHANCEMENT RULES + +**Core Principles:** +- Limit results to 20 rows unless specified otherwise +- Sort by most relevant metric for the question +- Include human-readable names, not just IDs +- For single values, return one row and skip null/0 values + +**Time-based Queries:** +- No time range specified → use year-to-date (YTD) +- Time range specified → use appropriate granularity +- Always sort chronologically (oldest to newest) +- Use timestamp parameters when available + +# CRITICAL REMINDERS + +1. **Tool Usage Discipline:** + - Use tools efficiently - avoid unnecessary calls + - Think through parameters before executing + - Validate results make sense + +2. **Always Apply Filters:** + - Use segmentId when relevant + - Apply timestamp filters for time-based queries + - Use provided parameters as defaults + +3. **Focus on the Task:** + - Answer the reformulated question directly + - Use the tools specified by the router + - Be concise and accurate in your response`; \ No newline at end of file diff --git a/frontend/lib/chat/prompts/router.ts b/frontend/lib/chat/prompts/router.ts new file mode 100644 index 00000000..817522ff --- /dev/null +++ b/frontend/lib/chat/prompts/router.ts @@ -0,0 +1,63 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +export const routerPrompt = ( + date: string, + projectName: string, + pipe: string, + parametersString: string, + segmentId: string | null +) => `You are a routing agent that analyzes user questions and determines the appropriate next action. Your job is to evaluate questions and decide whether they can be answered with existing tools, need custom queries, or cannot be answered. + +# DATE AND CONTEXT +Today's date: ${date} +Current dashboard: Project "${projectName}" using ${pipe} tool with parameters: ${parametersString} +Segment ID: ${segmentId || "not specified"} + +# YOUR ROLE +You are a ROUTER that decides the next action based on the user's question. You DO NOT execute queries or retrieve data - you only analyze and route. + +# AVAILABLE TOOLS +You have access to the list_datasources tool to examine available tables and fields when needed. + +# ROUTING LOGIC - VALIDATE ANSWERABILITY + +**Step 1: Check Existing Tools** +- Can ${pipe} tool answer this with different parameters? +- IMPORTANT: Only the parameters listed in the tool's parameters are valid. You cannot add extra parameters. + - For example, adding a country code parameter to a tool that doesn't support it is invalid. +- Can other available tools answer this question? +- Can a combination of tools provide the answer? +- If YES to any → Question is VALID, route to "pipes" action + +**Step 2: Check Data Sources (only if Step 1 is NO)** +- Use list_datasources to examine available tables and fields +- Check if the required fields exist in any data source +- Pay special attention to the activityRelations_deduplicated_cleaned_ds table +- If the needed fields exist → Question is VALID, route to "create_query" action +- If fields don't exist → Question is INVALID, route to "stop" action +- If the question is referencing a field about contributors/people that we have only for organizations, the question is INVALID + +# ROUTING DECISIONS +- "stop": The question cannot be answered with available data +- "create_query": Custom SQL query needed using available data sources +- "pipes": Existing tools can answer the question (specify which tools in the tools array) + +# IMPORTANT +- Always check data availability before routing +- Be precise in your reasoning +- Reformulate questions to be clearer and more specific +- For "pipes" action, always specify which tools should be used +- NEVER execute the plan. YOUR ONLY JOB IS TO ROUTE AND REFORMAT THE QUESTIONS. + +# RESPONSE GUIDELINES + +## Reformulated question: +It must help the following agent to answer the question. You must capture the intent and leave no room for ambiguity. +You can include things you learned/know, like country codes, timeframes, granularity, etc. + +## Reasoning: +It must be something user-friendly. +- If the action is "stop", the reasoning must be something like "I'm unable to answer this question with the available data sources, I am missing access to {DATA, explained in non-technical natural language}. If this looks like a mistake, please contact us." +- If the action is "create_query", the reasoning must be something like "I'll create a query to answer the question." +- If the action is "pipes", the reasoning must be something like "I'll use the widgets and to answer the question." +`; \ No newline at end of file diff --git a/frontend/lib/chat/prompts/text-to-sql.ts b/frontend/lib/chat/prompts/text-to-sql.ts new file mode 100644 index 00000000..a7bb033f --- /dev/null +++ b/frontend/lib/chat/prompts/text-to-sql.ts @@ -0,0 +1,157 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +export const textToSqlPrompt = ( + date: string, + projectName: string, + pipe: string, + parametersString: string, + segmentId: string | null, + reformulatedQuestion: string +) => ` +You are a SQL query generation assistant specialized in creating custom Tinybird queries to answer: "${reformulatedQuestion}" + +# DATE AND CONTEXT +Today's date: ${date} +Current dashboard: Project "${projectName}" using ${pipe} tool with parameters: ${parametersString} +Segment ID: ${segmentId || "not specified"} + +# YOUR APPROACH + +**STEP 1: UNDERSTAND DATA SOURCES** +- ALWAYS use list_datasources first to see available tables and schemas +- Study the schema carefully, noting column names and types +- Identify which tables contain the data you need + +**STEP 2: BUILD COMPLETE QUERY** +Before testing, construct your ENTIRE query following these rules: +- Use Tinybird's ClickHouse SQL syntax +- ALWAYS filter by segmentId on activityRelations_deduplicated_cleaned_ds +- ALWAYS include timestamp filters when querying time-based data +- Apply query enhancement rules (see below) +- Double-check all table and column names match the schema exactly + +**STEP 3: VALIDATE ONCE** +- Use execute_query ONCE with your complete query +- This is for validation only, not experimentation +- If it succeeds, return the exact same query +- If it fails, fix the specific error and test again (avoid multiple iterations) + +**STEP 4: RETURN RESULTS** +- Include the complete SQL query that was executed +- Provide a brief explanation of the query logic +- Include the actual data/results from the query execution + +# QUERY ENHANCEMENT RULES + +**Core Principles:** +- Sort by most relevant metric for the question +- Include human-readable names, not just IDs +- For single values, return one row and skip null/0 values + +**Time-based Queries:** +- No time range specified → use year-to-date (YTD) +- Time range specified → use appropriate granularity +- Always sort chronologically (oldest to newest) +- Use timestamp parameters when available + +# TINYBIRD SQL COMPLETE REFERENCE + +## ALLOWED SQL STATEMENTS +- **ONLY SELECT statements are supported** +- All SELECT clauses are fully supported: FROM, JOIN, WHERE, GROUP BY, ORDER BY, LIMIT, OFFSET, HAVING, WITH +- NO INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, or any other DDL/DML statements + +## SUPPORTED DATA TYPES +**Numeric Types:** +- Integers: Int8, Int16, Int32, Int64, Int128, Int256 +- Unsigned: UInt8, UInt16, UInt32, UInt64, UInt128, UInt256 +- Float: Float32, Float64 +- Decimal: Decimal(P,S), Decimal32(S), Decimal64(S), Decimal128(S), Decimal256(S) + +**String Types:** +- String (variable-length) +- FixedString(N) (fixed-length) + +**Date/Time Types:** +- Date, Date32 +- DateTime([timezone]), DateTime64(precision, [timezone]) + +**Other Types:** +- Bool +- UUID +- Array(T) +- Map(K,V) +- Nullable(T) +- LowCardinality(T) +- JSON (private beta - DO NOT USE) + +## AVAILABLE FUNCTIONS + +This is an EXHAUSTIVE list of available functions. You should only use functions that are available here: + +**Aggregate Functions:** +count(), avg(), sum(), min(), max(), argMin(), argMax(), any(), anyLast(), +stddevPop(), stddevSamp(), varPop(), varSamp(), corr(), covarPop(), covarSamp(), +groupArray(), groupUniqArray(), groupBitmap(), uniq(), uniqExact(), uniqHLL12(), +median(), quantile(), quantileExact(), quantileTiming() + +**String Functions:** +length(), empty(), notEmpty(), lower(), upper(), lowerUTF8(), upperUTF8(), +reverse(), reverseUTF8(), concat(), substring(), substringUTF8(), +trim(), trimLeft(), trimRight(), trimBoth(), startsWith(), endsWith(), +replace(), replaceAll(), replaceOne(), position(), positionCaseInsensitive(), +match(), extract(), extractAll(), like(), notLike(), ilike(), notILike(), +splitByChar(), splitByString(), arrayStringConcat(), format() + +**Date/Time Functions:** +now(), today(), yesterday(), toYear(), toMonth(), toDayOfMonth(), toDayOfWeek(), +toHour(), toMinute(), toSecond(), toStartOfYear(), toStartOfMonth(), toStartOfDay(), +toStartOfHour(), toStartOfMinute(), toMonday(), toDate(), toDateTime(), +formatDateTime(), dateDiff(), dateAdd(), dateSub(), addDays(), addMonths(), +addYears(), subtractDays(), subtractMonths(), subtractYears() + +**Math Functions:** +abs(), round(), floor(), ceil(), trunc(), sqrt(), cbrt(), exp(), log(), log2(), +log10(), sin(), cos(), tan(), asin(), acos(), atan(), pow(), pi(), e(), +greatest(), least(), max2(), min2() + +**Type Conversion Functions:** +toString(), toInt32(), toInt64(), toUInt32(), toUInt64(), toFloat32(), toFloat64(), +toDate(), toDateTime(), toDecimal32(), toDecimal64(), toDecimal128(), +CAST(x AS type) + +**Conditional Functions:** +if(cond, then, else), multiIf(), case when...then...else...end + +**Array Functions:** +length(), empty(), notEmpty(), arrayElement(), has(), hasAll(), hasAny(), +indexOf(), arrayCount(), arraySum(), arrayAvg(), arrayMin(), arrayMax(), +arrayUniq(), arrayJoin(), arrayConcat(), arraySlice(), arraySort(), arrayReverse() + +**JSON Functions (if table has JSON columns):** +JSONExtract(), JSONExtractString(), JSONExtractInt(), JSONExtractFloat(), +JSONExtractBool(), JSONExtractArrayRaw(), JSONHas(), JSONLength() + +## CRITICAL CONSTRAINTS +1. **NO subqueries in FROM clause** - Use JOINs instead +2. **LIMIT is recommended** - Always include LIMIT unless you need all results +3. **Aggregations require GROUP BY** - Include all non-aggregate columns +4. **SETTINGS clause** - Goes at the very end: SETTINGS join_use_nulls = 1 +5. **Table references** - Use database.schema.table format when available + +# CRITICAL REMINDERS + +1. **Tool Usage Discipline:** + - list_datasources: Use ONCE at the beginning if crafting custom SQL + - execute_query: Use ONCE for validation, not experimentation. + - Think through the ENTIRE query before testing + +2. **Always Apply Filters:** + - segmentId filter on activityRelations_deduplicated_cleaned_ds + - timestamp filters for time-based queries + - Use provided parameters as defaults + +3. **Efficiency:** + - Build complete, correct queries before testing + - Avoid iterative trial-and-error approaches + - Use existing tools when possible`; \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index ebde9bd2..74823300 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,7 +45,11 @@ "slugify": "^1.6.6", "vitepress": "^1.6.3", "vue": "^3.5.17", - "vue-router": "latest" + "vue-router": "latest", + "@ai-sdk/amazon-bedrock": "^2.2.12", + "@modelcontextprotocol/sdk": "^1.16.0", + "ai": "^4.3.19", + "zod": "^3.25.76" }, "devDependencies": { "@chromatic-com/storybook": "^3.2.4", diff --git a/frontend/server/api/chat/chart.ts b/frontend/server/api/chat/chart.ts new file mode 100644 index 00000000..8343a057 --- /dev/null +++ b/frontend/server/api/chat/chart.ts @@ -0,0 +1,61 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { generateChartConfig, modifyChartConfig } from '../../../lib/chat/chart/generator'; +import { Result, Config } from '../../../lib/chat/chart/types'; + +export const maxDuration = 30; + +interface IChartRequestBody { + results: Result[]; + userQuery?: string; + currentConfig?: Config; + instructions: string; +} + +export default defineEventHandler(async (event): Promise => { +try { + const { results, userQuery, currentConfig, instructions } = await readBody(event); + + if (!results || !Array.isArray(results)) { + return createError({statusCode: 400, statusMessage: 'Results array is required'}); + + } + + // If we have a current config and instructions, this is a modification request + if (currentConfig && instructions) { + const updatedConfig = await modifyChartConfig( + currentConfig as Config, + results as Result[], + instructions + ); + + return { + success: true, + config: updatedConfig, + isModification: true, + }; + } + + // Otherwise, generate a new chart config + if (!userQuery) { + return createError({statusCode: 400, statusMessage: 'User query is required for chart generation'}); + } + + const chartGeneration = await generateChartConfig( + results as Result[], + userQuery + ); + + return { + success: true, + isMetric: chartGeneration.isMetric, + config: chartGeneration.config, + isModification: false, + }; + } catch (error: any) { + console.error("Chart generation/modification error:", error); + return createError({statusCode: 500, statusMessage: 'Failed to process chart request'}); + } +}); + + diff --git a/frontend/server/api/chat/stream.ts b/frontend/server/api/chat/stream.ts new file mode 100644 index 00000000..764f5607 --- /dev/null +++ b/frontend/server/api/chat/stream.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT +import { streamingAgentRequestHandler } from '../../../lib/chat/data-copilot'; + +export const maxDuration = 30; + +interface IStreamRequestBody { + messages: any[]; + segmentId?: string; + projectName?: string; + pipe: string; + parameters?: Record; +} + +export default defineEventHandler(async (event): Promise => { + try { + const { messages, segmentId, projectName, pipe, parameters } = await readBody(event); + + + if (!pipe) { + return createError({statusCode: 400, statusMessage: 'Pipe is required'}); + } + + return await streamingAgentRequestHandler({ + messages, + segmentId, + projectName, + pipe, + parameters, + }); + } catch (error) { + return createError({statusCode: 500, statusMessage: error instanceof Error ? error.message : 'An error occurred processing your request'}); + } + +}); + + diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 8355d81e..52622464 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -8,6 +8,7 @@ "include": [ "app/**/*.ts", "app/**/*.vue", + "lib/**/*.ts", "shims-vue.d.ts" ], "compilerOptions": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 605e0418..d16b513d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,15 @@ importers: frontend: dependencies: + '@ai-sdk/amazon-bedrock': + specifier: ^2.2.12 + version: 2.2.12(zod@3.25.76) '@linuxfoundation/lfx-ui-core': specifier: ^0.0.18 version: 0.0.18(prettier@3.6.0) + '@modelcontextprotocol/sdk': + specifier: ^1.16.0 + version: 1.17.1 '@nuxt/eslint': specifier: ^0.7.5 version: 0.7.6(@typescript-eslint/utils@8.33.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.16)(eslint-import-resolver-node@0.3.9)(eslint-webpack-plugin@4.2.0(eslint@9.29.0(jiti@2.4.2))(webpack@5.99.9(esbuild@0.25.5)))(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)) @@ -50,6 +56,9 @@ importers: '@vuelidate/validators': specifier: ^2.0.4 version: 2.0.4(vue@3.5.17(typescript@5.8.3)) + ai: + specifier: ^4.3.19 + version: 4.3.19(react@18.3.1)(zod@3.25.76) echarts: specifier: ^5.6.0 version: 5.6.0 @@ -67,7 +76,7 @@ importers: version: 3.6.1 nuxt: specifier: ^3.15.3 - version: 3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0) + version: 3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0) nuxt-gtag: specifier: ^3.0.2 version: 3.0.2(magicast@0.3.5) @@ -98,6 +107,9 @@ importers: vue-router: specifier: latest version: 4.5.1(vue@3.5.17(typescript@5.8.3)) + zod: + specifier: ^3.25.76 + version: 3.25.76 devDependencies: '@chromatic-com/storybook': specifier: ^3.2.4 @@ -122,7 +134,7 @@ importers: version: 4.1.0(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(webpack@5.99.9(esbuild@0.25.5)) '@nuxtjs/storybook': specifier: 8.3.3 - version: 8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(prettier@3.6.0)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0) + version: 8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(prettier@3.6.0)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0) '@nuxtjs/tailwindcss': specifier: ^6.13.1 version: 6.14.0(magicast@0.3.5) @@ -349,22 +361,6 @@ importers: specifier: ^5.6.3 version: 5.8.3 - submodules/crowd.dev/services/libs/audit-logs: - dependencies: - '@crowd/data-access-layer': - specifier: workspace:* - version: link:../data-access-layer - deep-object-diff: - specifier: ^1.1.9 - version: 1.1.9 - devDependencies: - '@types/node': - specifier: ^18.16.3 - version: 18.19.110 - typescript: - specifier: ^5.6.3 - version: 5.8.3 - submodules/crowd.dev/services/libs/common: dependencies: '@crowd/types': @@ -602,37 +598,6 @@ importers: specifier: ^5.6.3 version: 5.8.3 - submodules/crowd.dev/services/libs/nango: - dependencies: - '@crowd/common': - specifier: workspace:* - version: link:../common - '@crowd/logging': - specifier: workspace:* - version: link:../logging - '@crowd/types': - specifier: workspace:* - version: link:../types - '@nangohq/frontend': - specifier: ^0.52.4 - version: 0.52.5 - '@nangohq/node': - specifier: ^0.50.0 - version: 0.50.0 - '@nangohq/types': - specifier: ^0.50.0 - version: 0.50.0 - axios: - specifier: ^1.8.4 - version: 1.9.0 - devDependencies: - '@types/node': - specifier: ^18.16.3 - version: 18.19.110 - typescript: - specifier: ^5.6.3 - version: 5.8.3 - submodules/crowd.dev/services/libs/opensearch: dependencies: '@crowd/common': @@ -975,6 +940,38 @@ packages: '@adobe/css-tools@4.4.3': resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} + '@ai-sdk/amazon-bedrock@2.2.12': + resolution: {integrity: sha512-m8gARnh45pr1s08Uu4J/Pm8913mwJPejPOm59b+kUqMsP9ilhUtH/bp8432Ra/v+vHuMoBrglG2ZvXtctAaH2g==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + '@algolia/autocomplete-core@1.17.7': resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} @@ -2527,15 +2524,9 @@ packages: '@types/react': '>=16' react: '>=16' - '@nangohq/frontend@0.52.5': - resolution: {integrity: sha512-KQ5Rc91AShd8pYHmHs+Egsroqei5qaX5z+/qAuBku7uQIr96bfkwtTaJkhYf8my7XyxcPExDq0HKXavrhoKOHA==} - - '@nangohq/node@0.50.0': - resolution: {integrity: sha512-azQs+oc628q37DF26Y471YpKeL2xnP1R/4WhllpraaExpaanG2VP/xshVTsuVqsRpo4YykquJBZAh5w4bQtcZw==} - engines: {node: '>=18.0'} - - '@nangohq/types@0.50.0': - resolution: {integrity: sha512-RgVFq37gW/EFRQi8zBjd2PbV7reYYmYNFrCgNrCrQg+QKIkDPwCocnp6qsa8Jv7R8meO9K6X7SFf8Y/oaMOr/w==} + '@modelcontextprotocol/sdk@1.17.1': + resolution: {integrity: sha512-CPle1OQehbWqd25La9Ack5B07StKIxh4+Bf19qnpZKJC1oI22Y0czZHbifjw1UoczIfKBwBDAp/dFxvHG13B5A==} + engines: {node: '>=18'} '@napi-rs/wasm-runtime@0.2.10': resolution: {integrity: sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==} @@ -3963,6 +3954,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -4788,6 +4782,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -4821,6 +4819,16 @@ packages: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} + ai@4.3.19: + resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -5023,6 +5031,9 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + axios@1.9.0: resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} @@ -5076,6 +5087,10 @@ packages: bn.js@5.2.2: resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -5151,6 +5166,10 @@ packages: engines: {'0': node >=0.10.0} hasBin: true + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + c12@3.0.4: resolution: {integrity: sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==} peerDependencies: @@ -5414,6 +5433,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -5440,6 +5463,14 @@ packages: cookie-es@2.0.0: resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -5462,6 +5493,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cosmiconfig-typescript-loader@6.1.0: resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} engines: {node: '>=v18'} @@ -5658,9 +5693,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deep-object-diff@1.1.9: - resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -5794,6 +5826,9 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff@7.0.0: resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} engines: {node: '>=0.3.1'} @@ -6363,6 +6398,14 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.0.3: + resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} + engines: {node: '>=20.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} @@ -6379,6 +6422,16 @@ packages: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + exsolve@1.0.5: resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} @@ -6474,6 +6527,10 @@ packages: resolution: {integrity: sha512-xdMtCAODmPloU9qtmPcdBV9Kd27NtMse+4ayThxqIHUES5Z2S6bGpap5PpdmNM56ub7y3i1eyr+vJJIIgWGKmA==} engines: {node: '>=18'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-package-json@1.2.0: resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==} @@ -6535,6 +6592,10 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -6967,6 +7028,10 @@ packages: resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} engines: {node: '>=12.22.0'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -7114,6 +7179,9 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -7305,6 +7373,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -7325,6 +7396,11 @@ packages: engines: {node: '>=6'} hasBin: true + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -7649,6 +7725,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@4.17.2: resolution: {integrity: sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==} engines: {node: '>= 4.0.0'} @@ -7660,6 +7740,10 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-options@3.0.4: resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} engines: {node: '>=10'} @@ -7865,6 +7949,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -8243,6 +8331,10 @@ packages: path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -8361,6 +8453,10 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -8689,6 +8785,10 @@ packages: protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -8767,6 +8867,10 @@ packages: rate-limiter-flexible@4.0.1: resolution: {integrity: sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ==} + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -8987,6 +9091,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -9571,6 +9679,11 @@ packages: '@swc/core': ^1.2.147 webpack: '>=2' + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -9670,6 +9783,10 @@ packages: peerDependencies: tslib: ^2 + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -9859,6 +9976,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -9972,6 +10093,10 @@ packages: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + unplugin-utils@0.2.4: resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==} engines: {node: '>=18.12.0'} @@ -10099,6 +10224,11 @@ packages: urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -10349,8 +10479,8 @@ packages: vue-component-type-helpers@2.2.10: resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==} - vue-component-type-helpers@3.0.4: - resolution: {integrity: sha512-WtR3kPk8vqKYfCK/HGyT47lK/T3FaVyWxaCNuosaHYE8h9/k0lYRZ/PI/+T/z2wP+uuNKmL6z30rOcBboOu/YA==} + vue-component-type-helpers@3.0.5: + resolution: {integrity: sha512-uoNZaJ+a1/zppa/Vgmi8zIOP2PHXDN2rT8NyF+zQRK6ZG94lNB9prcV0GdLJbY9i9lrD47JOVIH92SaiA7oJ1A==} vue-demi@0.13.11: resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} @@ -10637,8 +10767,13 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} - zod@3.25.48: - resolution: {integrity: sha512-0X1mz8FtgEIvaxGjdIImYpZEaZMrund9pGXm3M6vM7Reba0e2eI71KPjSCGXBfwKDPwPoywf6waUKc3/tFvX2Q==} + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} zrender@5.6.1: resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} @@ -10650,6 +10785,43 @@ snapshots: '@adobe/css-tools@4.4.3': {} + '@ai-sdk/amazon-bedrock@2.2.12(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@smithy/eventstream-codec': 4.0.4 + '@smithy/util-utf8': 4.0.0 + aws4fetch: 1.0.20 + zod: 3.25.76 + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.76 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@18.3.1)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + react: 18.3.1 + swr: 2.3.4(react@18.3.1) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3)': dependencies: '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3) @@ -12477,15 +12649,22 @@ snapshots: '@types/react': 18.3.23 react: 18.3.1 - '@nangohq/frontend@0.52.5': {} - - '@nangohq/node@0.50.0': + '@modelcontextprotocol/sdk@1.17.1': dependencies: - axios: 1.9.0 + ajv: 6.12.6 + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.3 + express: 5.1.0 + express-rate-limit: 7.5.1(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) transitivePeerDependencies: - - debug - - '@nangohq/types@0.50.0': {} + - supports-color '@napi-rs/wasm-runtime@0.2.10': dependencies: @@ -12582,7 +12761,7 @@ snapshots: unixify: 1.0.0 urlpattern-polyfill: 8.0.2 yargs: 17.7.2 - zod: 3.25.48 + zod: 3.25.76 transitivePeerDependencies: - encoding - rollup @@ -13014,11 +13193,11 @@ snapshots: - magicast - vue - '@nuxtjs/storybook@8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(prettier@3.6.0)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)': + '@nuxtjs/storybook@8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(prettier@3.6.0)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)': dependencies: '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)) '@nuxt/kit': 3.17.4(magicast@0.3.5) - '@storybook-vue/nuxt': 8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(storybook@8.4.7(prettier@3.6.0))(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0) + '@storybook-vue/nuxt': 8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(storybook@8.4.7(prettier@3.6.0))(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0) '@storybook/core-common': 8.4.7(storybook@8.4.7(prettier@3.6.0)) '@storybook/core-server': 8.4.7(storybook@8.4.7(prettier@3.6.0)) chalk: 5.4.1 @@ -13996,7 +14175,7 @@ snapshots: '@speed-highlight/core@1.2.7': {} - '@storybook-vue/nuxt@8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(storybook@8.4.7(prettier@3.6.0))(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)': + '@storybook-vue/nuxt@8.3.3(@types/node@24.0.10)(eslint@9.29.0(jiti@2.4.2))(magicast@0.3.5)(nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0))(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(storybook@8.4.7(prettier@3.6.0))(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)': dependencies: '@nuxt/kit': 3.17.4(magicast@0.3.5) '@nuxt/schema': 3.17.4 @@ -14007,7 +14186,7 @@ snapshots: '@storybook/vue3-vite': 8.4.7(storybook@8.4.7(prettier@3.6.0))(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(vue@3.5.17(typescript@5.8.3)) json-stable-stringify: 1.3.0 mlly: 1.7.4 - nuxt: 3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0) + nuxt: 3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0) ofetch: 1.4.1 pathe: 1.1.2 unctx: 2.4.1 @@ -14292,7 +14471,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.17(typescript@5.8.3) - vue-component-type-helpers: 3.0.4 + vue-component-type-helpers: 3.0.5 '@storybook/vue3@8.4.7(storybook@8.4.7(prettier@3.6.0))(vue@3.5.17(typescript@5.8.3))': dependencies: @@ -14306,7 +14485,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.17(typescript@5.8.3) - vue-component-type-helpers: 3.0.4 + vue-component-type-helpers: 3.0.5 '@stylistic/eslint-plugin@3.1.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: @@ -14521,6 +14700,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/diff-match-patch@1.0.36': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -15540,6 +15721,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-import-attributes@1.9.5(acorn@8.14.1): dependencies: acorn: 8.14.1 @@ -15566,6 +15752,18 @@ snapshots: agent-base@7.1.3: {} + ai@4.3.19(react@18.3.1)(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@18.3.1)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.76 + optionalDependencies: + react: 18.3.1 + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -15794,6 +15992,8 @@ snapshots: aws4@1.13.2: {} + aws4fetch@1.0.20: {} + axios@1.9.0: dependencies: follow-redirects: 1.15.9 @@ -15839,6 +16039,20 @@ snapshots: bn.js@5.2.2: {} + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1(supports-color@5.5.0) + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + boolbase@1.0.0: {} bowser@2.11.0: {} @@ -15912,6 +16126,8 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 + bytes@3.1.2: {} + c12@3.0.4(magicast@0.3.5): dependencies: chokidar: 4.0.3 @@ -16164,6 +16380,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + content-type@1.0.5: {} conventional-changelog-angular@7.0.0: @@ -16187,6 +16407,10 @@ snapshots: cookie-es@2.0.0: {} + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + cookie@1.0.2: {} cookies@0.9.1: @@ -16209,6 +16433,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig-typescript-loader@6.1.0(@types/node@24.0.10)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): dependencies: '@types/node': 24.0.10 @@ -16426,8 +16655,6 @@ snapshots: deep-is@0.1.4: {} - deep-object-diff@1.1.9: {} - deepmerge@4.3.1: {} default-browser-id@5.0.0: {} @@ -16545,6 +16772,8 @@ snapshots: didyoumean@1.2.2: {} + diff-match-patch@1.0.5: {} + diff@7.0.0: {} dir-glob@3.0.1: @@ -17376,6 +17605,12 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.0.3: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.3 + execa@7.2.0: dependencies: cross-spawn: 7.0.6 @@ -17406,6 +17641,42 @@ snapshots: expect-type@1.2.1: {} + express-rate-limit@7.5.1(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.5: {} extend@3.0.2: {} @@ -17494,6 +17765,17 @@ snapshots: filter-obj@6.1.0: {} + finalhandler@2.1.0: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-package-json@1.2.0: {} find-up-simple@1.0.1: {} @@ -17557,6 +17839,8 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded@0.2.0: {} + fraction.js@4.3.7: {} fresh@0.5.2: {} @@ -18060,6 +18344,8 @@ snapshots: transitivePeerDependencies: - supports-color + ipaddr.js@1.9.1: {} + iron-webcrypto@1.2.1: {} is-arguments@1.2.0: @@ -18191,6 +18477,8 @@ snapshots: is-promise@2.2.2: {} + is-promise@4.0.0: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.7 @@ -18392,6 +18680,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-stable-stringify@1.3.0: @@ -18410,6 +18700,12 @@ snapshots: json5@2.2.3: {} + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -18770,6 +19066,8 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@4.17.2: dependencies: '@jsonjoy.com/json-pack': 1.2.0(tslib@2.8.1) @@ -18783,6 +19081,8 @@ snapshots: meow@12.1.1: {} + merge-descriptors@2.0.0: {} + merge-options@3.0.4: dependencies: is-plain-obj: 2.1.0 @@ -18947,6 +19247,8 @@ snapshots: negotiator@0.6.3: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} netlify@13.3.5: @@ -18958,7 +19260,7 @@ snapshots: p-wait-for: 5.0.2 qs: 6.14.0 - nitropack@2.11.12: + nitropack@2.11.12(aws4fetch@1.0.20): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@netlify/functions': 3.1.10(rollup@4.41.1) @@ -19026,7 +19328,7 @@ snapshots: unenv: 2.0.0-rc.17 unimport: 5.0.1 unplugin-utils: 0.2.4 - unstorage: 1.16.0(db0@0.3.2)(ioredis@5.6.1) + unstorage: 1.16.0(aws4fetch@1.0.20)(db0@0.3.2)(ioredis@5.6.1) untyped: 2.0.0 unwasm: 0.3.9 youch: 4.1.0-beta.8 @@ -19185,7 +19487,7 @@ snapshots: - magicast - vue - nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0): + nuxt@3.17.4(@parcel/watcher@2.5.1)(@types/node@24.0.10)(aws4fetch@1.0.20)(db0@0.3.2)(eslint@9.29.0(jiti@2.4.2))(ioredis@5.6.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.20.3)(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)(sass-embedded@1.89.1)(sass@1.89.1)(terser@5.40.0))(yaml@2.8.0): dependencies: '@nuxt/cli': 3.25.1(magicast@0.3.5) '@nuxt/devalue': 2.0.2 @@ -19221,7 +19523,7 @@ snapshots: mlly: 1.7.4 mocked-exports: 0.1.1 nanotar: 0.2.0 - nitropack: 2.11.12 + nitropack: 2.11.12(aws4fetch@1.0.20) nypm: 0.6.0 ofetch: 1.4.1 ohash: 2.0.11 @@ -19243,7 +19545,7 @@ snapshots: unimport: 5.0.1 unplugin: 2.3.5 unplugin-vue-router: 0.12.0(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - unstorage: 1.16.0(db0@0.3.2)(ioredis@5.6.1) + unstorage: 1.16.0(aws4fetch@1.0.20)(db0@0.3.2)(ioredis@5.6.1) untyped: 2.0.0 vue: 3.5.17(typescript@5.8.3) vue-bundle-renderer: 2.1.1 @@ -19562,6 +19864,8 @@ snapshots: path-to-regexp@6.3.0: {} + path-to-regexp@8.2.0: {} + path-type@4.0.0: {} path-type@6.0.0: {} @@ -19655,6 +19959,8 @@ snapshots: pirates@4.0.7: {} + pkce-challenge@5.0.0: {} + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -19991,6 +20297,11 @@ snapshots: protocols@2.0.2: {} + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + proxy-from-env@1.1.0: {} pstree.remy@1.1.8: {} @@ -20093,6 +20404,13 @@ snapshots: rate-limiter-flexible@4.0.1: {} + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -20365,6 +20683,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.41.1 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + rrweb-cssom@0.8.0: {} run-applescript@7.0.0: {} @@ -21022,6 +21350,12 @@ snapshots: '@swc/counter': 0.1.3 webpack: 5.99.9(@swc/core@1.11.29) + swr@2.3.4(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + symbol-tree@3.2.4: {} sync-child-process@1.0.2: @@ -21164,6 +21498,8 @@ snapshots: dependencies: tslib: 2.8.1 + throttleit@2.1.0: {} + through@2.3.8: {} tiny-invariant@1.3.3: {} @@ -21306,6 +21642,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -21471,6 +21813,8 @@ snapshots: dependencies: normalize-path: 2.1.1 + unpipe@1.0.0: {} + unplugin-utils@0.2.4: dependencies: pathe: 2.0.3 @@ -21572,7 +21916,7 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.9.2 '@unrs/resolver-binding-win32-x64-msvc': 1.9.2 - unstorage@1.16.0(db0@0.3.2)(ioredis@5.6.1): + unstorage@1.16.0(aws4fetch@1.0.20)(db0@0.3.2)(ioredis@5.6.1): dependencies: anymatch: 3.1.3 chokidar: 4.0.3 @@ -21583,6 +21927,7 @@ snapshots: ofetch: 1.4.1 ufo: 1.6.1 optionalDependencies: + aws4fetch: 1.0.20 db0: 0.3.2 ioredis: 5.6.1 @@ -21625,6 +21970,10 @@ snapshots: urlpattern-polyfill@8.0.2: {} + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + util-deprecate@1.0.2: {} util@0.12.5: @@ -21921,7 +22270,7 @@ snapshots: vue-component-type-helpers@2.2.10: {} - vue-component-type-helpers@3.0.4: {} + vue-component-type-helpers@3.0.5: {} vue-demi@0.13.11(vue@3.5.17(typescript@5.8.3)): dependencies: @@ -22283,7 +22632,11 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 - zod@3.25.48: {} + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} zrender@5.6.1: dependencies: