feat(ai): add ai context usage info

- Upgraded the version of `@folo-services/ai-tools` from 0.2.20 to 0.2.31 in the package.json file.
- Introduced a new `TokenUsagePill` component to display token usage information in the AI chat message.
- Updated various components to utilize the new `TokenUsagePill` for better visibility of token metrics.
- Refactored type definitions in the chat store and related components to improve type safety and clarity.

These changes aim to enhance the user experience by providing clearer insights into token usage and improving the overall structure of the AI chat components.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-08-27 23:08:32 +08:00
parent a05c3950db
commit 70bf3fad2b
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
21 changed files with 322 additions and 137 deletions

View File

@ -119,7 +119,7 @@
"@follow/models": "workspace:*",
"@follow/types": "workspace:*",
"@follow/utils": "workspace:*",
"@folo-services/ai-tools": "0.2.20",
"@folo-services/ai-tools": "0.2.31",
"@types/node": "24.0.10",
"@vite-pwa/assets-generator": "1.0.0",
"fake-indexeddb": "6.0.1",

View File

@ -96,7 +96,7 @@ const AIDisplayFeedsPartBase = ({
return (
<DisplayCardWrapper title={title || "RSS Feeds"} emoji="📡" description={`${totalFeeds} feeds`}>
{/* Statistics Overview */}
<div className="@[600px]:grid-cols-4 grid grid-cols-2 gap-4 md:grid-cols-4">
<div className="@[600px]:grid-cols-3 @[400px]:grid-cols-2 grid grid-cols-1 gap-4">
<StatCard title="Total Feeds" value={totalFeeds} emoji="📊" />
<StatCard
title="Active Feeds"

View File

@ -31,6 +31,7 @@ import {
useMessages,
} from "~/modules/ai-chat/store/hooks"
import type { AIChatContextBlock } from "../../store/types"
import { convertLexicalToMarkdown } from "../../utils/lexical-markdown"
import { GlobalFileDropZone } from "../file/GlobalFileDropZone"
import { AIErrorFallback } from "./AIErrorFallback"
@ -119,7 +120,7 @@ const ChatInterfaceContent = ({ centerInputOnEmpty }: ChatInterfaceProps) => {
(message: string | EditorState, editor: LexicalEditor | null) => {
resetScrollState()
const blocks = [] as any[]
const blocks = [] as AIChatContextBlock[]
for (const block of blockActions.getBlocks()) {
if (block.type === "fileAttachment" && block.attachment.serverUrl) {

View File

@ -7,12 +7,13 @@ import * as React from "react"
import { toast } from "sonner"
import { copyToClipboard } from "~/lib/clipboard"
import type { BizUIMetadata, BizUITools } from "~/modules/ai-chat/store/types"
import type { BizUIMessage, BizUIMetadata, BizUITools } from "~/modules/ai-chat/store/types"
import { MentionPlugin } from "../../editor"
import type { RichTextPart } from "../../types/ChatSession"
import { convertLexicalToMarkdown } from "../../utils/lexical-markdown"
import { AIMessageParts } from "./AIMessageParts"
import { TokenUsagePill } from "./TokenUsagePill"
export interface ChatMessage {
id: string
@ -22,7 +23,7 @@ export interface ChatMessage {
}
interface AIChatMessageProps {
message: UIMessage<BizUIMetadata, UIDataTypes, BizUITools>
message: BizUIMessage
}
// Utility function for converting message to markdown
@ -96,6 +97,16 @@ export const AIChatMessage: React.FC<AIChatMessageProps> = React.memo(({ message
<i className="i-mgc-copy-2-cute-re size-3" />
<span>Copy</span>
</button>
{message.metadata && (
<TokenUsagePill metadata={message.metadata}>
<button
type="button"
className="text-text-secondary hover:bg-fill-tertiary flex items-center gap-1 rounded-md px-2 py-1 text-xs transition-colors"
>
<i className="i-mgc-information-cute-re size-3" />
</button>
</TokenUsagePill>
)}
</div>
<div className="h-6" />

View File

@ -23,7 +23,7 @@ export const AIDataBlockPart: React.FC<AIDataBlockPartProps> = React.memo(({ blo
<div className="min-w-0 max-w-full text-left">
<div
className={cn(
"inline-flex flex-wrap items-center gap-1.5 rounded-lg px-2 py-1",
"inline-flex flex-wrap items-center gap-1.5 rounded-lg py-1 pl-2 pr-1",
"bg-fill-secondary border-border/50 border",
)}
>

View File

@ -0,0 +1,114 @@
import { Tooltip, TooltipContent, TooltipTrigger } from "@follow/components/ui/tooltip/index.js"
import type { BizUIMetadata } from "@folo-services/ai-tools"
import * as React from "react"
import { formatTokenCountString } from "~/modules/settings/tabs/ai/usage/utils"
interface TokenUsagePillProps {
metadata: BizUIMetadata | undefined
className?: string
children: React.ReactNode
}
const formatDuration = (ms: number): string => {
if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`
return `${ms}ms`
}
export const TokenUsagePill: React.FC<TokenUsagePillProps> = ({ metadata, children }) => {
if (!metadata) return null
const hasReasoningTokens = metadata.reasoningTokens != null && metadata.reasoningTokens > 0
const hasCachedInputTokens = metadata.cachedInputTokens != null && metadata.cachedInputTokens > 0
const hasBillingMultiplier =
metadata.billingMultiplier != null && metadata.billingMultiplier !== 1
const hasDuration = metadata.duration != null
return (
<Tooltip>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent side="top" align="center" sideOffset={8}>
<div className="mb-2 flex flex-col gap-2">
<div className="text-text text-xs">Model Info</div>
<div className="text-text-secondary font-mono text-xs">
{metadata.modelUsed ?? "Unknown"}
</div>
</div>
<div className="space-y-2 text-xs">
<div className="text-text font-medium">Token Usage</div>
<div className="grid grid-cols-2 gap-x-4 gap-y-1">
{metadata.totalTokens != null && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Total:</span>
<span className="text-text font-mono">
{formatTokenCountString(metadata.totalTokens)}
</span>
</div>
)}
{metadata.billedTokens != null && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Billed:</span>
<span className="text-accent font-mono">
{formatTokenCountString(metadata.billedTokens)}
</span>
</div>
)}
{metadata.contextTokens != null && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Context:</span>
<span className="text-text font-mono">
{formatTokenCountString(metadata.contextTokens)}
</span>
</div>
)}
{metadata.outputTokens != null && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Output:</span>
<span className="text-text font-mono">
{formatTokenCountString(metadata.outputTokens)}
</span>
</div>
)}
{hasReasoningTokens && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Reasoning:</span>
<span className="text-text font-mono">
{formatTokenCountString(metadata.reasoningTokens!)}
</span>
</div>
)}
{hasCachedInputTokens && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Cached:</span>
<span className="text-text font-mono">
{formatTokenCountString(metadata.cachedInputTokens!)}
</span>
</div>
)}
</div>
{(hasDuration || hasBillingMultiplier) && (
<>
<hr className="border-fill-secondary" />
<div className="grid grid-cols-2 gap-x-4 gap-y-1">
{hasDuration && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Duration:</span>
<span className="text-text font-mono">
{formatDuration(metadata.duration!)}
</span>
</div>
)}
{hasBillingMultiplier && (
<div className="flex justify-between gap-2">
<span className="text-text-secondary">Multiplier:</span>
<span className="text-text font-mono">{metadata.billingMultiplier!}×</span>
</div>
)}
</div>
</>
)}
</div>
</TooltipContent>
</Tooltip>
)
}

View File

@ -1,13 +1,12 @@
import { Spring } from "@follow/components/constants/spring.js"
import { stopPropagation, thenable } from "@follow/utils"
import type { UIDataTypes, UIMessage } from "ai"
import type { LexicalEditor, SerializedEditorState } from "lexical"
import { AnimatePresence, m } from "motion/react"
import * as React from "react"
import { useEditingMessageId, useSetEditingMessageId } from "~/modules/ai-chat/atoms/session"
import { useChatActions } from "~/modules/ai-chat/store/hooks"
import type { AIChatContextBlock, BizUIMetadata, BizUITools } from "~/modules/ai-chat/store/types"
import type { AIChatContextBlock, BizUIMessage } from "~/modules/ai-chat/store/types"
import type { RichTextPart } from "../../types/ChatSession"
import { convertLexicalToMarkdown } from "../../utils/lexical-markdown"
@ -16,7 +15,7 @@ import { EditableMessage } from "./EditableMessage"
import { UserMessageParts } from "./UserMessageParts"
interface UserChatMessageProps {
message: UIMessage<BizUIMetadata, UIDataTypes, BizUITools>
message: BizUIMessage
}
export const UserChatMessage: React.FC<UserChatMessageProps> = React.memo(({ message }) => {

View File

@ -157,8 +157,6 @@ class AIPersistServiceStatic {
messages
.filter((message) => message.parts.length > 0)
.map((message) => {
const convertedParts = message.parts as any[]
return {
id: message.id,
chatId,
@ -169,7 +167,7 @@ class AIPersistServiceStatic {
finishedAt: message.metadata?.finishTime
? new Date(message.metadata.finishTime)
: undefined,
messageParts: convertedParts,
messageParts: message.parts,
metadata: message.metadata,
} as typeof aiChatMessagesTable.$inferInsert
}),

View File

@ -12,7 +12,7 @@ import type { ChatSlice } from "./types"
export class ChatSliceActions {
constructor(
private params: Parameters<StateCreator<ChatSlice, [], [], ChatSlice>>,
private chatInstance: ZustandChat<BizUIMessage>,
private chatInstance: ZustandChat,
) {
return autoBindThis(this)
}
@ -182,7 +182,7 @@ export class ChatSliceActions {
this.chatInstance.destroy()
// Create new chat instance
const newChatInstance = new ZustandChat<BizUIMessage>(
const newChatInstance = new ZustandChat(
{
id: newChatId,
messages: [],
@ -221,7 +221,7 @@ export class ChatSliceActions {
this.chatInstance.destroy()
// Create new chat instance with loaded messages
const newChatInstance = new ZustandChat<BizUIMessage>(
const newChatInstance = new ZustandChat(
{
id: chatId,
messages,

View File

@ -6,12 +6,12 @@ import { ZustandChatState } from "./chat-state"
import type { ChatSlice } from "./types"
// Custom Chat class that uses Zustand-integrated state
export class ZustandChat<UI_MESSAGE extends BizUIMessage> extends AbstractChat<UI_MESSAGE> {
override state: ZustandChatState<UI_MESSAGE>
export class ZustandChat extends AbstractChat<BizUIMessage> {
override state: ZustandChatState
#unsubscribeFns: (() => void)[] = []
constructor(
{ messages, ...init }: ChatInit<UI_MESSAGE>,
{ messages, ...init }: ChatInit<BizUIMessage>,
updateZustandState: (updater: (state: ChatSlice) => ChatSlice) => void,
) {
const state = new ZustandChatState(messages, updateZustandState, init.id || "")

View File

@ -1,5 +1,7 @@
/* eslint-disable unicorn/no-for-loop */
import type { ChatState, ChatStatus } from "ai"
import { throttle } from "es-toolkit/compat"
import { produce } from "immer"
import { AIPersistService } from "../../services"
import { ChatStateEventEmitter } from "../event-system/event-emitter"
@ -7,14 +9,14 @@ import type { BizUIMessage } from "../types"
import type { ChatSlice } from "./types"
// Zustand Chat State that implements AI SDK ChatState interface
export class ZustandChatState<UI_MESSAGE extends BizUIMessage> implements ChatState<UI_MESSAGE> {
#messages: UI_MESSAGE[]
export class ZustandChatState implements ChatState<BizUIMessage> {
#messages: BizUIMessage[]
#status: ChatStatus = "ready"
#error: Error | undefined = undefined
#eventEmitter = new ChatStateEventEmitter<UI_MESSAGE>()
#eventEmitter = new ChatStateEventEmitter()
constructor(
initialMessages: UI_MESSAGE[] = [],
initialMessages: BizUIMessage[] = [],
private updateZustandState: (updater: (state: ChatSlice) => ChatSlice) => void,
private chatId: string,
) {
@ -25,10 +27,36 @@ export class ZustandChatState<UI_MESSAGE extends BizUIMessage> implements ChatSt
#setupEventHandlers(): void {
// Setup event handlers for automatic Zustand synchronization
this.#eventEmitter.on("messages", ({ messages }) => {
this.updateZustandState((state) => ({
...state,
messages: [...messages],
}))
this.updateZustandState(
produce((state) => {
const stateMessages = state.messages
for (let i = 0; i < messages.length; i++) {
const message = messages[i]!
if (!stateMessages[i]) {
stateMessages[i] = structuredClone(message) as any
} else {
const stateMessage = stateMessages[i]!
stateMessage.id = message.id
for (let j = 0; j < message.parts.length; j++) {
const statePart = stateMessage.parts[j] || {}
const messagePart = message.parts[j]!
Object.assign(statePart, messagePart)
stateMessage.parts[j] = statePart as any
}
stateMessage.parts.length = message.parts.length
stateMessage.role = message.role
stateMessage.metadata = stateMessage.metadata ?? {}
Object.assign(stateMessage.metadata, message.metadata)
}
}
stateMessages.length = messages.length
}),
)
})
this.#eventEmitter.on("status", ({ status }) => {
@ -69,19 +97,20 @@ export class ZustandChatState<UI_MESSAGE extends BizUIMessage> implements ChatSt
this.#eventEmitter.emit("error", { error: newError })
}
get messages(): UI_MESSAGE[] {
get messages(): BizUIMessage[] {
return this.#messages
}
set messages(newMessages: UI_MESSAGE[]) {
set messages(newMessages: BizUIMessage[]) {
this.#messages = [...newMessages]
this.#eventEmitter.emit("messages", { messages: this.#messages })
// Auto-persist messages when they change
this.#persistMessages()
}
pushMessage = (message: UI_MESSAGE) => {
pushMessage = (message: BizUIMessage) => {
this.messages = this.#messages.concat(message)
}
@ -91,7 +120,7 @@ export class ZustandChatState<UI_MESSAGE extends BizUIMessage> implements ChatSt
this.messages = this.#messages.slice(0, -1)
}
replaceMessage = (index: number, message: UI_MESSAGE) => {
replaceMessage = (index: number, message: BizUIMessage) => {
if (index < 0 || index >= this.#messages.length) return
this.messages = [
@ -123,7 +152,7 @@ export class ZustandChatState<UI_MESSAGE extends BizUIMessage> implements ChatSt
}
// Internal event subscription with payload access
onMessagesChange = (listener: (messages: UI_MESSAGE[]) => void): (() => void) => {
onMessagesChange = (listener: (messages: BizUIMessage[]) => void): (() => void) => {
return this.#eventEmitter.on("messages", ({ messages }) => listener(messages))
}

View File

@ -2,12 +2,12 @@ import type { BizUIMessage } from "../types"
import type { ChatStateEvents, ChatStateEventType } from "./types"
// Event emitter for AI chat state changes with typed payloads
export class ChatStateEventEmitter<UI_MESSAGE extends BizUIMessage> {
export class ChatStateEventEmitter {
#listeners = new Map<ChatStateEventType, Set<(payload: any) => void>>()
on<T extends ChatStateEventType>(
event: T,
listener: (payload: ChatStateEvents<UI_MESSAGE>[T]) => void,
listener: (payload: ChatStateEvents<BizUIMessage>[T]) => void,
): () => void {
if (!this.#listeners.has(event)) {
this.#listeners.set(event, new Set())
@ -20,7 +20,7 @@ export class ChatStateEventEmitter<UI_MESSAGE extends BizUIMessage> {
}
}
emit<T extends ChatStateEventType>(event: T, payload: ChatStateEvents<UI_MESSAGE>[T]): void {
emit<T extends ChatStateEventType>(event: T, payload: ChatStateEvents<BizUIMessage>[T]): void {
this.#listeners.get(event)?.forEach((listener) => {
try {
listener(payload)

View File

@ -7,14 +7,13 @@ import { ChatSliceActions } from "../chat-core/chat-actions"
import { ZustandChat } from "../chat-core/chat-instance"
import type { ChatSlice } from "../chat-core/types"
import { createChatTransport } from "../transport"
import type { BizUIMessage } from "../types"
export const createChatSlice: StateCreator<ChatSlice, [], [], ChatSlice> = (...params) => {
const [set, get] = params
const chatId = nanoid()
// Create chat instance with Zustand integration
const chatInstance = new ZustandChat<BizUIMessage>(
const chatInstance = new ZustandChat(
{
id: chatId,
messages: [],

View File

@ -5,9 +5,9 @@ export interface FileAttachment {
name: string
type: string
size: number
dataUrl: string
dataUrl?: string
previewUrl?: string
uploadStatus: "processing" | "uploading" | "completed" | "error"
uploadStatus?: "processing" | "uploading" | "completed" | "error"
serverUrl?: string
errorMessage?: string
/** Upload progress percentage (0-100) */

View File

@ -5,7 +5,7 @@ import { useModalStack } from "~/components/ui/modal/stacked/hooks"
import { useAIConfiguration } from "~/modules/ai-chat/hooks/useAIConfiguration"
import { DetailedUsageModal, UsageProgressRing, UsageWarningBanner } from "./components"
import { formatTimeRemaining, formatTokenCount } from "./utils"
import { formatTimeRemaining, formatTokenCountString } from "./utils"
export const UsageAnalysisSection = () => {
const { t } = useTranslation("ai")
@ -57,8 +57,7 @@ export const UsageAnalysisSection = () => {
<div className="flex-1 space-y-2">
<div className="flex items-baseline gap-2">
<span className="text-text text-lg font-semibold">
{formatTokenCount(rateLimit.remainingTokens).value}
{formatTokenCount(rateLimit.remainingTokens).unit}
{formatTokenCountString(rateLimit.remainingTokens)}
</span>
<span className="text-text-secondary text-sm">
{t("usage_analysis.tokens_remaining")}
@ -66,9 +65,7 @@ export const UsageAnalysisSection = () => {
</div>
<div className="text-text-tertiary text-xs">
{formatTokenCount(usage.used).value}
{formatTokenCount(usage.used).unit} / {formatTokenCount(usage.total).value}
{formatTokenCount(usage.total).unit} used
{formatTokenCountString(usage.used)} / {formatTokenCountString(usage.total)} used
</div>
<div className="text-text-secondary text-xs">

View File

@ -78,6 +78,11 @@ export const DetailedUsageModal = () => {
})
const maxHourCount = Math.max(1, ...hourBuckets)
const formattedUsageTokens = formatTokenCount(usage.used)
const formattedRemainingTokens = formatTokenCount(rateLimit.remainingTokens)
const formattedTotalTokens = formatTokenCount(usage.total)
const formattedAvgDaily = formatTokenCount(avgDaily)
return (
<div className="max-h-[80vh] min-h-[640px] w-[500px] max-w-full space-y-6 overflow-y-auto">
<p className="text-text-secondary text-sm">
@ -106,15 +111,15 @@ export const DetailedUsageModal = () => {
<div className="bg-fill-secondary/40 rounded-lg p-4">
<Metric
label={t("usage_analysis.tokens_used")}
value={formatTokenCount(usage.used).value}
unit={formatTokenCount(usage.used).unit}
value={formattedUsageTokens.value}
unit={formattedUsageTokens.unit}
/>
</div>
<div className="bg-fill-secondary/40 rounded-lg p-4">
<Metric
label={t("usage_analysis.tokens_remaining")}
value={formatTokenCount(rateLimit.remainingTokens).value}
unit={formatTokenCount(rateLimit.remainingTokens).unit}
value={formattedRemainingTokens.value}
unit={formattedRemainingTokens.unit}
/>
</div>
</div>
@ -122,8 +127,8 @@ export const DetailedUsageModal = () => {
<div className="bg-fill-secondary/20 rounded-lg p-3">
<StatCompact
label={t("usage_analysis.total_limit")}
value={formatTokenCount(usage.total).value}
unit={formatTokenCount(usage.total).unit}
value={formattedTotalTokens.value}
unit={formattedTotalTokens.unit}
/>
</div>
<div className="bg-fill-secondary/20 rounded-lg p-3">
@ -139,8 +144,8 @@ export const DetailedUsageModal = () => {
<div className="border-fill-tertiary bg-fill-secondary/10 rounded-lg border p-4">
<StatCompact
label={t("analytics.avg_per_day", { defaultValue: "Avg/day" })}
value={formatTokenCount(avgDaily).value}
unit={formatTokenCount(avgDaily).unit}
value={formattedAvgDaily.value}
unit={formattedAvgDaily.unit}
/>
</div>
<div className="border-fill-tertiary bg-fill-secondary/10 rounded-lg border p-4">

View File

@ -2,7 +2,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@follow/components/ui/
import type { ModelPattern } from "@follow-app/client-sdk"
import { useTranslation } from "react-i18next"
import { formatTokenCount } from "../utils"
import { formatTokenCount, formatTokenCountString } from "../utils"
import { BarList } from "./charts"
interface EfficiencyTabProps {
@ -22,12 +22,15 @@ export const EfficiencyTab = ({ byModel }: EfficiencyTabProps) => {
<CardContent>
{byModel?.length > 0 ? (
<BarList
data={byModel.map((m) => ({
label: m.model ?? "unknown",
value: m.avgEfficiency || 0,
right: `${formatTokenCount(m.totalTokens ?? 0).value}${formatTokenCount(m.totalTokens ?? 0).unit}`,
}))}
format={(v) => `${formatTokenCount(v).value}${formatTokenCount(v).unit}`}
data={byModel.map((m) => {
const formatted = formatTokenCount(m.totalTokens ?? 0)
return {
label: m.model ?? "unknown",
value: m.avgEfficiency || 0,
right: `${formatted.value}${formatted.unit}`,
}
})}
format={(v) => formatTokenCountString(v)}
/>
) : (
<div className="text-text-tertiary py-8 text-center text-sm">

View File

@ -2,7 +2,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@follow/components/ui/
import type { DailyPattern } from "@follow-app/client-sdk"
import { useTranslation } from "react-i18next"
import { formatTokenCount } from "../utils"
import { formatTokenCountString } from "../utils"
import { Sparkline } from "./charts"
interface OverviewTabProps {
@ -33,10 +33,7 @@ export const OverviewTab = ({ dailyTotals, peakDay }: OverviewTabProps) => {
{peakDay?.date ? (
<span>
<span>{t("analytics.peak", { defaultValue: "Peak" })}: </span>
<span>
{formatTokenCount(peakDay.totalTokens).value}
{formatTokenCount(peakDay.totalTokens).unit}
</span>
<span>{formatTokenCountString(peakDay.totalTokens)}</span>
<span>{" · "}</span>
<span>{new Date(peakDay.date).toLocaleDateString()} </span>
<span>

View File

@ -2,7 +2,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@follow/components/ui/
import type { UsagePattern } from "@follow-app/client-sdk"
import { useTranslation } from "react-i18next"
import { formatTokenCount } from "../utils"
import { formatTokenCountString } from "../utils"
import { BarList, TinyBars } from "./charts"
interface PatternsTabProps {
@ -53,7 +53,8 @@ export const PatternsTab = ({ hourBuckets, maxHourCount, byOperation }: Patterns
data={byOperation.map((o) => ({
label: o.operationType ?? "unknown",
value: o.percentage || 0,
right: `${formatTokenCount(o.totalTokens ?? 0).value}${formatTokenCount(o.totalTokens ?? 0).unit}`,
right: formatTokenCountString(o.totalTokens ?? 0),
}))}
suffix="%"
/>

View File

@ -15,3 +15,8 @@ export const formatTimeRemaining = (ms: number): string => {
const rem = minutes % 60
return rem ? `${hours}h ${rem}m` : `${hours}h`
}
export const formatTokenCountString = (count: number): string => {
const formatted = formatTokenCount(count)
return `${formatted.value}${formatted.unit}`
}

View File

@ -768,8 +768,8 @@ importers:
specifier: workspace:*
version: link:../../../../packages/internal/utils
'@folo-services/ai-tools':
specifier: 0.2.20
version: 0.2.20(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
specifier: 0.2.31
version: 0.2.31(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
'@types/node':
specifier: 24.0.10
version: 24.0.10
@ -1967,20 +1967,20 @@ packages:
graphql:
optional: true
'@ai-sdk/gateway@1.0.15':
resolution: {integrity: sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/gateway@1.0.2':
resolution: {integrity: sha512-RCE6v6/7JMzSHmjB0R7oWT54y/VYeEv+6NYLtkQKdxN871UawFFHpL7TW7IRnn+/SHdRlr7XoWxxGdHEaGRKiA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/openai@2.0.2':
resolution: {integrity: sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/provider-utils@3.0.0':
resolution: {integrity: sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw==}
'@ai-sdk/openai@2.0.22':
resolution: {integrity: sha512-qjSIPL5+LNM9flcBPeR64ZWeAZdYg4XWkAK34H3FaY61dSbuIaeqFPSzmQUrxotVcphAzgfL5tuYRqRYP2ZYyg==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
@ -1991,6 +1991,12 @@ packages:
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/provider-utils@3.0.7':
resolution: {integrity: sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/provider@2.0.0':
resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
engines: {node: '>=18'}
@ -4048,20 +4054,17 @@ packages:
'@follow-app/client-sdk@0.3.48':
resolution: {integrity: sha512-nPRnKQwzTTvPaIMOfZierMi9+TUlxc2his1FrGmkXOUbyMe6VtnChMtxHWU03Kdb2mnCJrVmOzJa0CwW6NAvmQ==}
'@folo-services/ai-tools@0.2.20':
resolution: {integrity: sha512-1AJI48H2g64JhuTruSqQKUrn4Tc353hMFdKAbohh65AxTAMN/1pIw9w17Dtjm+g5BVlZUXYKaiIvaN9tHsXLNA==}
'@folo-services/ai-tools@0.2.31':
resolution: {integrity: sha512-oqTVMBc6UCLOt/3M9GX4I+DJ8WT2U8Hlok1MN/Sruw8sQu0ZvtoDvkuiJEtssOQAGDifzHm7z8AVmOgNoGrKUg==}
'@folo-services/constants@0.1.27':
resolution: {integrity: sha512-ZCTgPvLiNfKiOU3E7m0x6Y6w/8PIkEHVl7C5pH+Tt36DrM81vHZjy+zPnZISgjOd3DYTJFHTOCI2ReFRx8Ir3g==}
'@folo-services/drizzle@0.1.13':
resolution: {integrity: sha512-h6tndGnTLrAmfvxK5XsXGdZaMi2GBmFZBFiBqtabC7Y05A1ZSOaWg/d9f60Ydh586LmVonz5VGrhvzTdn5ukhg==}
'@folo-services/drizzle@0.1.21':
resolution: {integrity: sha512-yJAPJz05h2uio5wPOHYbzaeFhJNn5UU9S4HPLQa3L8DNE9IG9DMsNAJqFTEpXQPwEQOf2n3k8FCF8GP/SygA7w==}
'@folo-services/exceptions@0.1.11':
resolution: {integrity: sha512-OMKat8KcIxjw9pBfG3pC1kRcIs0YiuaYmMDA2MRy8AEGcki8F9u5e5QH7OKgP/LlNR7Fs2Csz1XetvcC4nB+RQ==}
'@folo-services/drizzle@0.1.22':
resolution: {integrity: sha512-n4L0bBA5AxqJn5K3k+BTzmtuZh90LAO7G6zALCo3H+BOsnMsQ1OCsQH/xFg7m6ICQ80LJVbHdUN6b4ZKUoQJ/g==}
'@folo-services/exceptions@0.1.16':
resolution: {integrity: sha512-ThmLLe1Dv2CFsDIgcuY6kJhYJnjiQ7UEsewaqc25MkuImDabhzWnnad+3zRnJGLpveaMeEZ69uqUsTbKC+fR7g==}
@ -7002,6 +7005,12 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
ai@5.0.26:
resolution: {integrity: sha512-bGNtG+nYQ2U+5mzuLbxIg9WxGQJ2u5jv2gYgP8C+CJ1YI4qqIjvjOgGEZWzvNet8jiOGIlqstsht9aQefKzmBw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
ai@5.0.4:
resolution: {integrity: sha512-nizyqRxFlJShCAh/xPqor9EYpuaiGqCcYqau9A2GaSL+di+Qf6pA+Hos5x1orlodmT+EciZfoC2Sq+I12OmIUg==}
engines: {node: '>=18'}
@ -9428,6 +9437,10 @@ packages:
resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==}
engines: {node: '>=20.0.0'}
eventsource-parser@3.0.5:
resolution: {integrity: sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==}
engines: {node: '>=20.0.0'}
evp_bytestokey@1.0.3:
resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
@ -15845,6 +15858,12 @@ snapshots:
optionalDependencies:
graphql: 16.8.1
'@ai-sdk/gateway@1.0.15(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.7(zod@3.25.76)
zod: 3.25.76
'@ai-sdk/gateway@1.0.2(zod@3.25.75)':
dependencies:
'@ai-sdk/provider': 2.0.0
@ -15857,20 +15876,12 @@ snapshots:
'@ai-sdk/provider-utils': 3.0.1(zod@3.25.76)
zod: 3.25.76
'@ai-sdk/openai@2.0.2(zod@3.25.76)':
'@ai-sdk/openai@2.0.22(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.0(zod@3.25.76)
'@ai-sdk/provider-utils': 3.0.7(zod@3.25.76)
zod: 3.25.76
'@ai-sdk/provider-utils@3.0.0(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@standard-schema/spec': 1.0.0
eventsource-parser: 3.0.3
zod: 3.25.76
zod-to-json-schema: 3.24.5(zod@3.25.76)
'@ai-sdk/provider-utils@3.0.1(zod@3.25.75)':
dependencies:
'@ai-sdk/provider': 2.0.0
@ -15887,6 +15898,13 @@ snapshots:
zod: 3.25.76
zod-to-json-schema: 3.24.5(zod@3.25.76)
'@ai-sdk/provider-utils@3.0.7(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@standard-schema/spec': 1.0.0
eventsource-parser: 3.0.5
zod: 3.25.76
'@ai-sdk/provider@2.0.0':
dependencies:
json-schema: 0.4.0
@ -18973,11 +18991,11 @@ snapshots:
- sql.js
- sqlite3
'@folo-services/ai-tools@0.2.20(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
'@folo-services/ai-tools@0.2.31(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
dependencies:
'@ai-sdk/openai': 2.0.2(zod@3.25.76)
'@folo-services/drizzle': 0.1.13(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
ai: 5.0.4(zod@3.25.76)
'@ai-sdk/openai': 2.0.22(zod@3.25.76)
'@folo-services/drizzle': 0.1.22(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
ai: 5.0.26(zod@3.25.76)
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
zod: 3.25.76
transitivePeerDependencies:
@ -19016,45 +19034,6 @@ snapshots:
dependencies:
zod: 3.25.76
'@folo-services/drizzle@0.1.13(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)':
dependencies:
'@folo-services/exceptions': 0.1.11
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
drizzle-zod: 0.7.1(drizzle-orm@0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3))(zod@3.25.76)
nanoid: 5.1.5
pg: 8.16.3
zod: 3.25.76
transitivePeerDependencies:
- '@aws-sdk/client-rds-data'
- '@cloudflare/workers-types'
- '@electric-sql/pglite'
- '@libsql/client'
- '@libsql/client-wasm'
- '@neondatabase/serverless'
- '@op-engineering/op-sqlite'
- '@opentelemetry/api'
- '@planetscale/database'
- '@prisma/client'
- '@tidbcloud/serverless'
- '@types/better-sqlite3'
- '@types/pg'
- '@types/sql.js'
- '@upstash/redis'
- '@vercel/postgres'
- '@xata.io/client'
- better-sqlite3
- bun-types
- expo-sqlite
- gel
- knex
- kysely
- mysql2
- pg-native
- postgres
- prisma
- sql.js
- sqlite3
'@folo-services/drizzle@0.1.21(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)':
dependencies:
'@folo-services/exceptions': 0.1.16
@ -19094,7 +19073,44 @@ snapshots:
- sql.js
- sqlite3
'@folo-services/exceptions@0.1.11': {}
'@folo-services/drizzle@0.1.22(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)':
dependencies:
'@folo-services/exceptions': 0.1.16
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
drizzle-zod: 0.7.1(drizzle-orm@0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3))(zod@3.25.76)
nanoid: 5.1.5
pg: 8.16.3
zod: 3.25.76
transitivePeerDependencies:
- '@aws-sdk/client-rds-data'
- '@cloudflare/workers-types'
- '@electric-sql/pglite'
- '@libsql/client'
- '@libsql/client-wasm'
- '@neondatabase/serverless'
- '@op-engineering/op-sqlite'
- '@opentelemetry/api'
- '@planetscale/database'
- '@prisma/client'
- '@tidbcloud/serverless'
- '@types/better-sqlite3'
- '@types/pg'
- '@types/sql.js'
- '@upstash/redis'
- '@vercel/postgres'
- '@xata.io/client'
- better-sqlite3
- bun-types
- expo-sqlite
- gel
- knex
- kysely
- mysql2
- pg-native
- postgres
- prisma
- sql.js
- sqlite3
'@folo-services/exceptions@0.1.16': {}
@ -22653,6 +22669,14 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
ai@5.0.26(zod@3.25.76):
dependencies:
'@ai-sdk/gateway': 1.0.15(zod@3.25.76)
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.7(zod@3.25.76)
'@opentelemetry/api': 1.9.0
zod: 3.25.76
ai@5.0.4(zod@3.25.75):
dependencies:
'@ai-sdk/gateway': 1.0.2(zod@3.25.75)
@ -25633,6 +25657,8 @@ snapshots:
eventsource-parser@3.0.3: {}
eventsource-parser@3.0.5: {}
evp_bytestokey@1.0.3:
dependencies:
md5.js: 1.3.5