diff --git a/app/plugins/kapa.client.ts b/app/plugins/kapa.client.ts
deleted file mode 100644
index 3c1b1677a..000000000
--- a/app/plugins/kapa.client.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-const kapa = {
- 'key': 'kapa',
- 'src': 'https://widget.kapa.ai/kapa-widget.bundle.js',
- 'data-website-id': 'fb3af718-9db2-440d-9da9-14e6c5fca2aa',
- 'data-button-hide': true,
- 'data-project-name': 'Nuxt',
- 'data-project-color': '#00DC82',
- 'data-button-text-color': '#000000',
- 'data-project-logo': 'https://nuxt.com/assets/design-kit/icon-black.svg',
- 'data-modal-image': 'https://nuxt.com/assets/design-kit/icon-green.svg',
- 'data-button-padding': '0.5rem',
- 'data-button-width': '5.5rem',
- 'data-modal-disclaimer': 'This is a custom LLM for answering questions about Nuxt. Answers are based on the contents of the documentation, GitHub information and Stack Overflow articles. Please note that answers are generated by AI and may not be fully accurate, so please use your best judgement.',
- 'data-user-analytics-fingerprint-enabled': 'true',
- 'crossorigin': false
-} as const
-
-interface OnModalOpenArgs {
- mode: 'search' | 'ai'
-}
-
-interface OnModalCloseArgs {
- mode: 'search' | 'ai'
-}
-
-interface OnAskAIQuerySubmitArgs {
- threadId: string | null
- questionAnswerId: string
- question: string
-}
-
-interface OnAskAIExampleQuerySubmitArgs {
- threadId: string | null
- questionAnswerId: string
- question: string
-}
-
-interface OnAskAIAnswerCompletedArgs {
- threadId: string
- questionAnswerId: string
- question: string
- answer: string
- conversation: { questionAnswerId: string, question: string, answer: string }[]
-}
-
-interface OnAskAIFeedbackSubmitArgs {
- reaction: string
- comment: {
- issue: string
- irrelevant: boolean
- incorrect: boolean
- unaddressed: boolean
- }
- threadId: string
- questionAnswerId: string
- question: string
- answer: string
- conversation: { questionAnswerId: string, question: string, answer: string }[]
-}
-
-interface OnAskAILinkClickArgs {
- href: string
- threadId: string
- questionAnswerId: string
- question: string
- answer: string
-}
-
-interface OnAskAISourceClickArgs {
- source: {
- title: string
- subtitle: string
- url: string
- }
- threadId: string
- questionAnswerId: string
- question: string
- answer: string
-}
-
-interface OnAskAIAnswerCopyArgs {
- threadId: string
- questionAnswerId: string
- question: string
- answer: string
-}
-
-interface OnAskAIGenerationStopArgs {
- threadId: string | null
- question: string
- conversation: { questionAnswerId: string, question: string, answer: string }[]
-}
-
-interface OnAskAIConversationResetArgs {
- threadId: string
- conversation: { questionAnswerId: string, question: string, answer: string }[]
-}
-
-interface OnModeSwitchArgs {
- mode: 'search' | 'ai'
-}
-
-interface OnSearchResultsCompletedArgs {
- query: string
- searchResults: { title: string, subtitle: string, url: string, sourceName: string }[]
-}
-
-interface OnSearchResultsShowMoreClickArgs {
- query: string
- searchResults: { title: string, subtitle: string, url: string, sourceName: string }[]
-}
-
-interface OnSearchResultClickArgs {
- query: string
- searchResult: { title: string, subtitle: string, url: string, sourceName: string }
- rank: number
-}
-
-interface Kapa {
- (event: 'onModalOpen', handler: (args: OnModalOpenArgs) => void): void
- (event: 'onModalClose', handler: (args: OnModalCloseArgs) => void): void
- (event: 'onAskAIQuerySubmit', handler: (args: OnAskAIQuerySubmitArgs) => void): void
- (event: 'onAskAIExampleQuerySubmit', handler: (args: OnAskAIExampleQuerySubmitArgs) => void): void
- (event: 'onAskAIAnswerCompleted', handler: (args: OnAskAIAnswerCompletedArgs) => void): void
- (event: 'onAskAIFeedbackSubmit', handler: (args: OnAskAIFeedbackSubmitArgs) => void): void
- (event: 'onAskAILinkClick', handler: (args: OnAskAILinkClickArgs) => void): void
- (event: 'onAskAISourceClick', handler: (args: OnAskAISourceClickArgs) => void): void
- (event: 'onAskAIAnswerCopy', handler: (args: OnAskAIAnswerCopyArgs) => void): void
- (event: 'onAskAIGenerationStop', handler: (args: OnAskAIGenerationStopArgs) => void): void
- (event: 'onAskAIConversationReset', handler: (args: OnAskAIConversationResetArgs) => void): void
- (event: 'onModeSwitch', handler: (args: OnModeSwitchArgs) => void): void
- (event: 'onSearchResultsCompleted', handler: (args: OnSearchResultsCompletedArgs) => void): void
- (event: 'onSearchResultsShowMoreClick', handler: (args: OnSearchResultsShowMoreClickArgs) => void): void
- (event: 'onSearchResultClick', handler: (args: OnSearchResultClickArgs) => void): void
- open(options?: { mode?: 'search' | 'ai', query?: string, submit?: boolean }): void
- close: () => void
-}
-
-declare global {
- interface Window {
- Kapa: Kapa
- }
-}
-
-export default defineNuxtPlugin(() => {
- const script = useScript<{ Kapa: Kapa }>(kapa, {
- trigger: 'manual',
- use() {
- return { Kapa: window.Kapa }
- }
- })
-
- return {
- provide: {
- kapa: {
- async openModal(q?: string) {
- await script.load()
- const open = await getKapaOpen()
-
- if (q) {
- return open?.({
- mode: 'search',
- query: q,
- submit: true
- })
- }
-
- return open?.()
- }
- }
- }
- }
-})
-
-async function getKapaOpen() {
- let i = 0
-
- do {
- const open = window.Kapa?.open
- if (open) {
- return open
- }
- await new Promise(resolve => setTimeout(resolve, 10))
- i++
- } while (i < 200)
- console.log('couldn\'t load kapa')
-}
diff --git a/app/types/agent.ts b/app/types/agent.ts
new file mode 100644
index 000000000..d97cbd870
--- /dev/null
+++ b/app/types/agent.ts
@@ -0,0 +1,6 @@
+export interface FaqCategory {
+ category: string
+ items: string[]
+}
+
+export type FaqQuestions = string[] | FaqCategory[]
diff --git a/app/utils/ai.ts b/app/utils/ai.ts
new file mode 100644
index 000000000..b69be4ee9
--- /dev/null
+++ b/app/utils/ai.ts
@@ -0,0 +1,24 @@
+import type { UIMessage } from 'ai'
+import { isTextUIPart } from 'ai'
+import { sourceToInlineMdc } from '~/utils/tool'
+
+export function getMergedParts(parts: UIMessage['parts']): UIMessage['parts'] {
+ const result: UIMessage['parts'] = []
+ for (const part of parts) {
+ const prev = result[result.length - 1]
+ if (part.type === 'source-url') {
+ if (prev && isTextUIPart(prev)) {
+ result[result.length - 1] = { type: 'text', text: prev.text + sourceToInlineMdc(part.url) }
+ } else {
+ result.push({ type: 'text', text: sourceToInlineMdc(part.url) })
+ }
+ continue
+ }
+ if (isTextUIPart(part) && prev && isTextUIPart(prev)) {
+ result[result.length - 1] = { type: 'text', text: prev.text + part.text }
+ } else {
+ result.push(part)
+ }
+ }
+ return result
+}
diff --git a/app/utils/tool.ts b/app/utils/tool.ts
new file mode 100644
index 000000000..b42189d58
--- /dev/null
+++ b/app/utils/tool.ts
@@ -0,0 +1,41 @@
+import type { DynamicToolUIPart, ToolUIPart, UITools } from 'ai'
+
+export interface Source {
+ url: string
+ title?: string
+}
+
+interface SearchOutput {
+ sources?: { url: string, type?: string }[]
+}
+
+type ToolPart = ToolUIPart
| DynamicToolUIPart
+
+export function getSearchQuery(part: ToolPart): string | undefined {
+ return (part.input as { query?: string } | undefined)?.query
+}
+
+export function getSources(part: ToolPart): Source[] {
+ const output = part.output
+ if (!output) return []
+
+ if (Array.isArray(output)) {
+ return output.filter((s: Source) => s.url).map((s: Source) => ({ url: s.url, title: s.title }))
+ }
+
+ const typed = output as SearchOutput
+ if (typed.sources) {
+ return typed.sources.filter(s => s.url).map(s => ({ url: s.url }))
+ }
+
+ return []
+}
+
+export function sourceToInlineMdc(url: string): string {
+ const domain = getDomain(url)
+ const favicon = getFaviconUrl(url)
+ const safeUrl = url.replace(/"/g, '"')
+ const safeFavicon = favicon.replace(/"/g, '"')
+
+ return ` :source-link{url="${safeUrl}" favicon="${safeFavicon}" label="${domain}"}`
+}
diff --git a/app/utils/url.ts b/app/utils/url.ts
new file mode 100644
index 000000000..15502b272
--- /dev/null
+++ b/app/utils/url.ts
@@ -0,0 +1,24 @@
+function stripWww(host: string): string {
+ return host.replace(/^www\./, '')
+}
+
+export function getDomain(url: string): string {
+ try {
+ return stripWww(new URL(url).hostname)
+ } catch {
+ // ignore
+ }
+
+ try {
+ return stripWww(new URL(`https://${url}`).hostname)
+ } catch {
+ // ignore
+ }
+
+ const host = url.split('/')[0]?.split('?')[0] ?? url
+ return stripWww(host)
+}
+
+export function getFaviconUrl(url: string): string {
+ return `https://www.google.com/s2/favicons?sz=32&domain=${encodeURIComponent(getDomain(url))}`
+}
diff --git a/content/blog/44.introducing-nuxt-agent.md b/content/blog/44.introducing-nuxt-agent.md
new file mode 100644
index 000000000..5b7c2dd71
--- /dev/null
+++ b/content/blog/44.introducing-nuxt-agent.md
@@ -0,0 +1,174 @@
+---
+title: Introducing the Nuxt Agent
+description: A first-party AI agent on nuxt.com, grounded in the official documentation and the Nuxt ecosystem, replacing our Kapa AI widget with a deeply integrated, fully agentic experience.
+navigation: false
+image: /nuxt-chat-og.jpg
+authors:
+ - name: Hugo Richard
+ avatar:
+ src: https://github.com/hugorcd.png
+ to: https://x.com/hugorcd
+ - name: Sébastien Chopin
+ avatar:
+ src: https://github.com/Atinux.png
+ to: https://x.com/Atinux
+date: 2026-04-16T10:00:00.000Z
+category: Article
+---
+
+Today we're rolling out the **Nuxt Agent** (Beta) on nuxt.com: a first-party AI agent that lives right inside the site, deeply wired into our documentation, modules catalog, blog, deployment guides, and the wider Nuxt ecosystem. It replaces the third-party Kapa AI widget we used to ship, and takes the experience from "answer a question" to "help me get things done".
+
+::callout{icon="i-lucide-sparkles"}
+Open the agent anywhere on nuxt.com with **⌘I** (or **Ctrl+I**), or jump straight into the full-screen experience at [/chat](/chat).
+::
+
+::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1776779145/nuxt/nuxt-agent-built-in-chat_lpaii9.jpg" controls class="rounded-lg"}
+ :source{src="https://res.cloudinary.com/nuxt/video/upload/v1776779145/nuxt/nuxt-agent-built-in-chat_lpaii9.mp4" type="video/mp4"}
+::
+
+## From a docs widget to a first-party agent
+
+For the past couple of years, the Kapa AI widget served as our docs Q&A experience. It did one thing well: search the docs and summarize an answer. But the Nuxt experience is more than just docs. It's modules, templates, deployment providers, the changelog, GitHub issues, playgrounds, and real navigation into the site.
+
+We wanted something that could do all of that in one place, with the same design language as the rest of nuxt.com, the same content pipeline (Nuxt Content), and the same infrastructure we already run. So we built our own, on top of the [Nuxt MCP server](/blog/building-nuxt-mcp) we shipped last November.
+
+The result is an agent that:
+
+- **Grounds every answer** in the official Nuxt documentation and ecosystem data, through structured MCP tools rather than retrieved text chunks.
+- **Renders rich UI**: modules, templates, blog posts, hosting providers, and playground links come back as cards you can click, not as plain links buried in prose.
+- **Streams everything** end to end, with tool call progress visible as it runs.
+- **Owns the loop**: feedback, voting, and issue reporting flow directly into our internal tools so we can improve the agent over time.
+
+## Meet the agent
+
+### Three ways to talk to it
+
+The agent is available everywhere on nuxt.com:
+
+- **As a side panel** pinned to the right on large screens, slide-over on smaller ones. Toggle it from the header or with **⌘I**.
+- **As a floating ask bar** on `/docs` and `/blog` pages: a bottom-centered "Ask anything…" input that lets you jump into the agent without taking your eyes off the page.
+- **As a full-screen chat** at [/chat](/chat) for longer sessions.
+
+::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1776779145/nuxt/nuxt-agent-fullscreen-chat_gxxzmh.jpg" controls class="rounded-lg"}
+ :source{src="https://res.cloudinary.com/nuxt/video/upload/v1776779145/nuxt/nuxt-agent-fullscreen-chat_gxxzmh.mp4" type="video/mp4"}
+::
+
+### It knows what page you're on
+
+If you're reading a doc and ask "how do I customize this for my app?", the agent already knows which page you mean and pulls it in as context. A small "Agent is using this page" indicator in the footer makes it explicit, and you can dismiss it any time.
+
+### Rich answers, not just text
+
+The agent doesn't just talk — it shows. When you ask about a module, it renders a module card with metadata pulled live from `api.nuxt.com`. Ask for starter templates and you'll get clickable template cards. Ask about deployment and you'll get provider cards linking to the right guide. Need to reproduce a bug? The agent can generate a [StackBlitz](https://stackblitz.com) playground link straight from the conversation.
+
+::callout{icon="i-lucide-lightbulb"}
+Try asking: _"Show me official starter templates"_ — you'll get the full lineup (`nuxt-ui-dashboard`, `nuxt-ui-saas`, `nuxt-ui-landing`, `nuxt-ui-chat`, `nuxt-ui-docs`, `nuxt-ui-portfolio`) as cards you can open in one click.
+::
+
+### Feedback built in
+
+Every assistant message has a thumbs up/down to let us know how it went. And if you'd like to share more — a missing piece, a wrong answer, an idea — the **Report issue** action opens a short form and creates a Linear ticket on our side with the conversation attached, so we have everything we need to follow up.
+
+The agent can also trigger that flow on its own. If you ask to "submit feedback" or "report an issue", or if the conversation tilts toward frustration, the agent calls the `report_issue` tool and the same form opens inline — no need to go hunt for a button.
+
+Conversations are persisted and resumed across reloads, so you can step away and pick up where you left off.
+
+## What the agent can actually do
+
+The agent's grounding comes from our own [Nuxt MCP server](https://nuxt.com/mcp) — the exact same one Cursor, Claude Desktop or ChatGPT connect to. That means the Nuxt Agent and your local AI assistant are looking at the same structured data: the official docs, the modules catalog, the blog, deployment guides, and the Nuxt repositories' changelog.
+
+On top of that grounding, it has a set of native tools that render as UI inside the chat — module and template cards, hosting providers, blog posts, StackBlitz playground links, and a GitHub issue search across `nuxt`, `nuxt-modules` and `nuxt-content` (the agent reaches for this one first when you paste an error).
+
+The web is available too, via Anthropic's native `web_search`, but strictly as a fallback for things the model couldn't possibly know — the latest Vue release, a freshly published RFC, recent ecosystem news. It's not a general-purpose search engine, and the system prompt is explicit about it: never use `web_search` for anything that should be answered from the docs or the rest of the Nuxt content exposed through MCP.
+
+## Under the hood
+
+### The stack
+
+A single Nitro handler at [`server/api/agent.post.ts`](https://github.com/nuxt/nuxt.com/blob/main/server/api/agent.post.ts) drives everything. The client is an `@ai-sdk/vue` `Chat` instance pointed at `/api/agent`; the server uses AI SDK v6 `streamText` against `claude-sonnet-4.6`, with tools merged from our own MCP server (`/mcp`, same origin) and a handful of native ones (`show_*`, `open_playground`, `report_issue`…). Chat state is persisted with Drizzle ORM, and [`evlog`](https://evlog.dev) wraps the model for structured telemetry — tokens, cost, tool calls.
+
+### UIMessage streaming with AI SDK v6
+
+The whole pipeline is built on the [AI SDK v6](https://ai-sdk.dev) `UIMessage` streaming model. On the server:
+
+::code-collapse
+```ts [server/api/agent.post.ts]
+const stream = createUIMessageStream({
+ execute: async ({ writer }) => {
+ const result = streamText({
+ model: ai.wrap(MODEL),
+ maxOutputTokens: 4000,
+ stopWhen: stopWhenResponseComplete,
+ system: systemPrompt,
+ messages: await convertToModelMessages(messages),
+ tools: {
+ ...mcpTools as ToolSet,
+ web_search: anthropic.tools.webSearch_20250305(),
+ search_github_issues: createSearchGitHubIssuesTool(event),
+ show_module: showModuleTool,
+ show_template: createShowTemplateTool(event),
+ show_blog_post: createShowBlogPostTool(event),
+ show_hosting: createShowHostingTool(event),
+ open_playground: openPlaygroundTool,
+ report_issue: reportIssueTool
+ },
+ experimental_telemetry: {
+ isEnabled: true,
+ integrations: [createEvlogIntegration(ai)]
+ }
+ })
+
+ writer.merge(result.toUIMessageStream({
+ sendSources: true,
+ originalMessages: messages,
+ onFinish: ({ messages: finalizedMessages }) => {
+ event.waitUntil(saveChat(finalizedMessages))
+ }
+ }))
+ }
+})
+```
+::
+
+The two details worth highlighting: `stopWhen: stopWhenResponseComplete` is a custom predicate that ends the loop as soon as the model produces text without a new tool call (with a hard ceiling of 10 steps), avoiding the classic "model loops forever on tools" failure mode. And `event.waitUntil(saveChat(...))` pushes persistence outside the response lifecycle — the stream finishes for the user immediately, the chat row is upserted asynchronously.
+
+### One MCP server, two consumers
+
+The most important architectural decision is that the agent and external AI assistants talk to the **same** MCP server. The route handler opens an HTTP MCP client pointed at its own `/mcp` endpoint:
+
+```ts [server/api/agent.post.ts]
+const mcpUrl = import.meta.dev
+ ? `http://localhost:3000${MCP_PATH}`
+ : `${getRequestURL(event).origin}${MCP_PATH}`
+
+const httpClient = await createMCPClient({
+ transport: { type: 'http', url: mcpUrl }
+})
+const mcpTools = await httpClient.tools()
+```
+
+Those tools are then merged with the native UI tools into a single `tools` object passed to `streamText`. The nice consequence: any new tool we add to the MCP server is instantly available to the Nuxt Agent — and to every external assistant pointed at it — without any extra wiring. If you want to dig into how the MCP server itself is built, we wrote a [dedicated blog post](/blog/building-nuxt-mcp) back in November.
+
+### Persistence, cost, and rate limiting
+
+Chats are persisted in a single `agent_chats` table keyed by the `x-chat-id` header the client sends on every request. We use Drizzle's `onConflictDoUpdate` to accumulate token usage, estimated cost, duration, and request count across the lifetime of a conversation — per-chat analytics at zero runtime cost.
+
+To keep things sustainable, every request goes through a small `consumeAgentRateLimit` helper before streaming starts. The current cap is 20 messages per day per IP fingerprint — enough for real use, low enough to prevent runaway costs from accidental loops.
+
+### A tight system prompt
+
+A lot of agent quality comes from the prompt. The most impactful rules: always reach for `search_github_issues` first when the user pastes an error, prefer `show_module` over `get_module` for anything that should render as a card, never call `web_search` unless the question is genuinely outside what the model could know, and never end a turn with just a tool call. Together they cut tool-spam and hallucinations down dramatically and make the agent feel like it actually knows what it's doing.
+
+## What's next
+
+The agent is launching in Beta. The near-term focus is on the basics: improving answer quality, giving the agent richer memory across turns, and tightening source citations.
+
+Looking a bit further, we want nuxt.com to become more of an actual application than a static site. The next step there is user accounts: each logged-in user with their own session, their full chat history saved and resumable across devices, and the Nuxt Agent as the first real building block of that more app-like nuxt.com.
+
+We'd love your help shaping it. If the agent gets something wrong, or misses a feature you'd want, use the **Report issue** button right inside the chat — it creates a ticket on our side with the full conversation attached, and we read every one.
+
+::callout{icon="i-lucide-message-square"}
+Try the Nuxt Agent now: open it with **⌘I**, use the ask bar on any [docs](/docs) page, or head to [/chat](/chat) for the full-screen experience.
+::
+
+The complete source code for nuxt.com — including the agent, the MCP server, and all the tools described above — is available on [GitHub](https://github.com/nuxt/nuxt.com). The agent handler lives at [`server/api/agent.post.ts`](https://github.com/nuxt/nuxt.com/blob/main/server/api/agent.post.ts), the native tools at [`server/utils/tools/`](https://github.com/nuxt/nuxt.com/tree/main/server/utils/tools), and the UI components at [`app/components/agent/`](https://github.com/nuxt/nuxt.com/tree/main/app/components/agent). Feel free to use it as inspiration for your own apps — and if you want to build your own MCP server, the [Nuxt MCP Toolkit](https://mcp-toolkit.nuxt.dev) makes it a few-minute job.
diff --git a/nuxt.config.ts b/nuxt.config.ts
index e60920625..df3a490ef 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -25,7 +25,9 @@ export default defineNuxtConfig({
'@nuxtjs/mcp-toolkit',
'@nuxt/hints',
'@vercel/analytics',
- '@vercel/speed-insights'
+ '@vercel/speed-insights',
+ '@comark/nuxt',
+ 'evlog/nuxt'
],
$development: {
site: {
@@ -94,9 +96,13 @@ export default defineNuxtConfig({
},
runtimeConfig: {
contactEmail: '',
+ cronSecret: '',
github: {
token: ''
},
+ linear: {
+ apiKey: ''
+ },
newsletter: {
secret: ''
},
@@ -401,6 +407,7 @@ export default defineNuxtConfig({
sourcemap: true,
experimental: {
extractAsyncDataHandlers: true,
+ viewTransition: true,
defaults: {
nuxtLink: {
externalRelAttribute: 'noopener'
@@ -462,6 +469,9 @@ export default defineNuxtConfig({
}
}
},
+ evlog: {
+ env: { service: 'nuxt-com' }
+ },
icon: {
customCollections: [{
prefix: 'custom',
diff --git a/package.json b/package.json
index fa780c0bf..02fb19bf2 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,11 @@
"db:migrate": "nuxt hub database migrate"
},
"dependencies": {
+ "@ai-sdk/anthropic": "^3.0.68",
+ "@ai-sdk/gateway": "^3.0.83",
+ "@ai-sdk/mcp": "^1.0.36",
+ "@ai-sdk/vue": "^3.0.141",
+ "@comark/nuxt": "^0.2.0",
"@iconify-json/heroicons": "^1.2.3",
"@iconify-json/logos": "^1.2.11",
"@iconify-json/lucide": "^1.2.102",
@@ -34,7 +39,7 @@
"@nuxt/hints": "1.0.3",
"@nuxt/image": "^2.0.0",
"@nuxt/scripts": "^0.13.4",
- "@nuxt/ui": "^4.6.1",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@8d08ef3",
"@nuxthub/core": "^0.10.7",
"@nuxtjs/html-validator": "^2.1.0",
"@nuxtjs/mcp-toolkit": "^0.13.4",
@@ -48,8 +53,10 @@
"@vueuse/core": "^14.2.1",
"@vueuse/nuxt": "^14.2.1",
"better-sqlite3": "^12.9.0",
+ "ai": "^6.0.168",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.45.2",
+ "evlog": "^2.12.0",
"feed": "^5.2.0",
"h3": "^1.15.11",
"little-date": "^1.2.1",
@@ -62,13 +69,13 @@
"ofetch": "^1.5.1",
"resend": "^6.12.0",
"scule": "^1.3.0",
+ "shaders": "^2.5.93",
"sitemap": "^9.0.1",
"std-env": "^4.1.0",
"ufo": "^1.6.3",
"valibot": "^1.3.1"
},
"devDependencies": {
- "@ai-sdk/mcp": "^1.0.36",
"@iconify-json/vscode-icons": "^1.2.45",
"@nuxt/devtools": "^3.2.4",
"@nuxt/eslint": "^1.15.2",
@@ -80,7 +87,6 @@
"@types/semver": "^7.7.1",
"@types/youtube": "^0.2.0",
"@vue/test-utils": "^2.4.6",
- "ai": "^6.0.168",
"capture-website": "^5.1.0",
"drizzle-kit": "^0.31.10",
"eslint": "^10.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0fe845e6f..4fb08ca62 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,21 @@ importers:
.:
dependencies:
+ '@ai-sdk/anthropic':
+ specifier: ^3.0.68
+ version: 3.0.68(zod@4.3.6)
+ '@ai-sdk/gateway':
+ specifier: ^3.0.83
+ version: 3.0.91(zod@4.3.6)
+ '@ai-sdk/mcp':
+ specifier: ^1.0.36
+ version: 1.0.36(zod@4.3.6)
+ '@ai-sdk/vue':
+ specifier: ^3.0.141
+ version: 3.0.154(vue@3.5.32(typescript@6.0.3))(zod@4.3.6)
+ '@comark/nuxt':
+ specifier: ^0.2.0
+ version: 0.2.1(@types/markdown-it@14.1.2)(magicast@0.5.2)(markdown-it@14.1.1)(nuxt@4.4.2(ec9ba8feacf9ea1d35ac88469523465a))(shiki@4.0.2)(vue@3.5.32(typescript@6.0.3))
'@iconify-json/heroicons':
specifier: ^1.2.3
version: 1.2.3
@@ -22,7 +37,7 @@ importers:
version: 1.2.22
'@iconify-json/simple-icons':
specifier: ^1.2.78
- version: 1.2.78
+ version: 1.2.79
'@libsql/client':
specifier: ^0.17.2
version: 0.17.2
@@ -42,8 +57,8 @@ importers:
specifier: ^0.13.4
version: 0.13.4(@types/youtube@0.2.0)(@unhead/vue@2.1.13(vue@3.5.32(typescript@6.0.3)))(@vercel/functions@3.4.3)(db0@0.3.4(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vue@3.5.32(typescript@6.0.3))
'@nuxt/ui':
- specifier: ^4.6.1
- version: 4.6.1(@nuxt/content@3.13.0(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(magicast@0.5.2)(valibot@1.3.1(typescript@6.0.3)))(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(@vercel/functions@3.4.3)(change-case@5.4.4)(db0@0.3.4(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@6.0.3)(valibot@1.3.1(typescript@6.0.3))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@6.0.3))(yjs@13.6.30)(zod@4.3.6)
+ specifier: https://pkg.pr.new/@nuxt/ui@8d08ef3
+ version: https://pkg.pr.new/@nuxt/ui@8d08ef3(7f3fd92f21d5ecaed314c37ee5a01ec7)
'@nuxthub/core':
specifier: ^0.10.7
version: 0.10.7(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vercel/functions@3.4.3)(db0@0.3.4(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vue-tsc@3.2.6(typescript@6.0.3))
@@ -80,6 +95,9 @@ importers:
'@vueuse/nuxt':
specifier: ^14.2.1
version: 14.2.1(magicast@0.5.2)(nuxt@4.4.2(ec9ba8feacf9ea1d35ac88469523465a))(vue@3.5.32(typescript@6.0.3))
+ ai:
+ specifier: ^6.0.168
+ version: 6.0.168(zod@4.3.6)
better-sqlite3:
specifier: ^12.9.0
version: 12.9.0
@@ -89,6 +107,9 @@ importers:
drizzle-orm:
specifier: ^0.45.2
version: 0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)
+ evlog:
+ specifier: ^2.12.0
+ version: 2.12.0(@nuxt/kit@4.4.2(magicast@0.5.2))(ai@6.0.168(zod@4.3.6))(express@5.2.1)(fastify@5.8.4)(h3@1.15.11)(hono@4.12.12)(nitropack@2.13.3(@libsql/client@0.17.2)(@vercel/functions@3.4.3)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(rolldown@1.0.0-beta.57(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(srvx@0.11.15))(ofetch@1.5.1)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
feed:
specifier: ^5.2.0
version: 5.2.0
@@ -125,6 +146,9 @@ importers:
scule:
specifier: ^1.3.0
version: 1.3.0
+ shaders:
+ specifier: ^2.5.93
+ version: 2.5.93(vue@3.5.32(typescript@6.0.3))
sitemap:
specifier: ^9.0.1
version: 9.0.1
@@ -138,9 +162,6 @@ importers:
specifier: ^1.3.1
version: 1.3.1(typescript@6.0.3)
devDependencies:
- '@ai-sdk/mcp':
- specifier: ^1.0.36
- version: 1.0.36(zod@4.3.6)
'@iconify-json/vscode-icons':
specifier: ^1.2.45
version: 1.2.45
@@ -174,9 +195,6 @@ importers:
'@vue/test-utils':
specifier: ^2.4.6
version: 2.4.6
- ai:
- specifier: ^6.0.168
- version: 6.0.168(zod@4.3.6)
capture-website:
specifier: ^5.1.0
version: 5.1.0(typescript@6.0.3)
@@ -231,12 +249,30 @@ packages:
bcrypt:
optional: true
+ '@ai-sdk/anthropic@3.0.68':
+ resolution: {integrity: sha512-BAd+fmgYoJMmGw0/uV+jRlXX60PyGxelA6Clp4cK/NI0dsyv9jOOwzQmKNaz2nwb+Jz7HqI7I70KK4XtU5EcXQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
'@ai-sdk/gateway@3.0.104':
resolution: {integrity: sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
+ '@ai-sdk/gateway@3.0.91':
+ resolution: {integrity: sha512-J39Dh6Gyg6HjG3A7OFKnJMp3QyZ3Eex+XDiX8aFBdRwwZm3jGWaMhkCxQPH7yiQ9kRiErZwHXX/Oexx4SyGGGA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/gateway@3.0.94':
+ resolution: {integrity: sha512-uDDwLZhCkvC89crVS3S90D5L7AcVN8WriGuYVNYgVAaVcvy3Mthy3R9ICfzG75BObhz6pm2FWnhxDfNRK+t69Q==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
'@ai-sdk/mcp@1.0.36':
resolution: {integrity: sha512-THQKwlknp7OU2ViLPfIU7W01XvDRM2eqH+4UULQgP64AopnwI9mGqqJeGIx2l/pxUu9yIDQtW9YtYM8kHm2CQg==}
engines: {node: '>=18'}
@@ -253,6 +289,12 @@ packages:
resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==}
engines: {node: '>=18'}
+ '@ai-sdk/vue@3.0.154':
+ resolution: {integrity: sha512-dPhHMhRgBD6Si8JUeJRQ+P/lQhyumB67JRl8q5kumImXG6LXXF12wy3KvNgU6fVitvi9VhwPyVitZtBzwgO8DA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ vue: ^3.3.4
+
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -428,6 +470,32 @@ packages:
'@colordx/core@5.0.3':
resolution: {integrity: sha512-xBQ0MYRTNNxW3mS2sJtlQTT7C3Sasqgh1/PsHva7fyDb5uqYY+gv9V0utDdX8X80mqzbGz3u/IDJdn2d/uW09g==}
+ '@comark/markdown-it@0.3.3':
+ resolution: {integrity: sha512-qG+dbiwPKcaO3kzABw0dxtLqTxHAgqpklLwnu56jiYX3V/0dpVYCTIhXRhKgAo1JasBhcPP+OeHAENziXxGYbg==}
+ peerDependencies:
+ '@types/markdown-it': '*'
+ markdown-it: ^14.0.0
+
+ '@comark/nuxt@0.2.1':
+ resolution: {integrity: sha512-cluseC8YBC/+toX47LTDXCBEOpk1KAJYiK/Vyc6sZ4Q+ct0fG/q74dT2jVoEsR1es8JNmCUzEFSmnuuI8AOCfA==}
+ peerDependencies:
+ nuxt: ^3.0.0
+
+ '@comark/vue@0.2.1':
+ resolution: {integrity: sha512-MOwPsBBrTN1rKb9P5s3Vrj5SKqdHZ5PU16PgCscZLZFbL1Qt/MoRCHr0gwmA8ijrk6C5dih65Xr5BSNdTITGEA==}
+ peerDependencies:
+ beautiful-mermaid: ^1.1.3
+ katex: ^0.16.33
+ shiki: ^4.0.0
+ vue: ^3.5.0
+ peerDependenciesMeta:
+ beautiful-mermaid:
+ optional: true
+ katex:
+ optional: true
+ shiki:
+ optional: true
+
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
@@ -1091,8 +1159,8 @@ packages:
'@iconify-json/octicon@1.2.22':
resolution: {integrity: sha512-6m0IXGRR3ooNDXoQetk4JR8rijoXQGqSKPFsOYBFx7xMJeLgIo1dTRCY8P/4FJbIHUvWj/DfRzkFrPLAW8gHCQ==}
- '@iconify-json/simple-icons@1.2.78':
- resolution: {integrity: sha512-I3lkNp0Qu7q2iZWkdcf/I2hqGhzK6qxdILh9T7XqowQrnpmG/BayDsiCf6PktDoWlW0U971xA5g+panm+NFrfQ==}
+ '@iconify-json/simple-icons@1.2.79':
+ resolution: {integrity: sha512-aNyO7Fd1qej9oQfIyohYFRv0lhQLaZ+6UkK1c1qwax0MDPUOZOdq65MlU500kow97pD/W+b2u1And3e25eE24Q==}
'@iconify-json/vscode-icons@1.2.45':
resolution: {integrity: sha512-ow+ueibMIq79ueM1kv6cOWgHx8jfh1XJQi2RrqMHb4HLbvIBlxpy5PCMvOJXlA68R6fBAHpWQeh6uWx7VKEVsA==}
@@ -1639,12 +1707,15 @@ packages:
vitest:
optional: true
- '@nuxt/ui@4.6.1':
- resolution: {integrity: sha512-mBbBTaVDTR6ohOoAJUiV4T2RPXo2hyLewGPTiHjy1arzHPNFnmb/Tkl/2/JipF3Y8cahV4LhVUdkWKsdgI1OXw==}
+ '@nuxt/ui@https://pkg.pr.new/@nuxt/ui@8d08ef3':
+ resolution: {integrity: sha512-Xhqx724cvR0qDBS62VbyXw8+tW2xkaULTQzNIFCTZzR1CFOrbtp8NzYHNIDPEeGOM8fZeHpW/3epX46e3/kQlA==, tarball: https://pkg.pr.new/@nuxt/ui@8d08ef3}
+ version: 4.6.1
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@inertiajs/vue3': ^2.0.7 || ^3.0.0
+ '@internationalized/date': ^3.0.0
+ '@internationalized/number': ^3.0.0
'@nuxt/content': ^3.0.0
joi: ^18.0.0
superstruct: ^2.0.0
@@ -1657,6 +1728,10 @@ packages:
peerDependenciesMeta:
'@inertiajs/vue3':
optional: true
+ '@internationalized/date':
+ optional: true
+ '@internationalized/number':
+ optional: true
'@nuxt/content':
optional: true
joi:
@@ -2397,9 +2472,6 @@ packages:
'@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
- '@remirror/core-constants@3.0.0':
- resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
-
'@remusao/guess-url-type@2.1.0':
resolution: {integrity: sha512-zI3dlTUxpjvx2GCxp9nLOSK5yEIqDCpxlAVGwb2Y49RKkS72oeNaxxo+VWS5+XQ5+Mf8Zfp9ZXIlk+G5eoEN8A==}
@@ -3120,8 +3192,8 @@ packages:
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
- '@tanstack/virtual-core@3.13.23':
- resolution: {integrity: sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==}
+ '@tanstack/virtual-core@3.14.0':
+ resolution: {integrity: sha512-JLANqGy/D6k4Ujmh8Tr25lGimuOXNiaVyXaCAZS0W+1390sADdGnyUdSWNIfd49gebtIxGMij4IktRVzrdr12Q==}
'@tanstack/vue-table@8.21.3':
resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==}
@@ -3129,8 +3201,8 @@ packages:
peerDependencies:
vue: '>=3.2'
- '@tanstack/vue-virtual@3.13.23':
- resolution: {integrity: sha512-b5jPluAR6U3eOq6GWAYSpj3ugnAIZgGR0e6aGAgyRse0Yu6MVQQ0ZWm9SArSXWtageogn6bkVD8D//c4IjW3xQ==}
+ '@tanstack/vue-virtual@3.13.24':
+ resolution: {integrity: sha512-A0k2qF0zFSUStXSZkGXABouXr2Tw2Ztl/cVIYG9qy84uR8W7UNjAcX3DvzBS3YnDcwvLxab8v7dbmYBZ39itDA==}
peerDependencies:
vue: ^2.7.0 || ^3.0.0
@@ -3148,211 +3220,211 @@ packages:
'@vue/compiler-sfc':
optional: true
- '@tiptap/core@3.22.2':
- resolution: {integrity: sha512-atq35NkpeEphH6vNYJ0pTLLBA73FAbvTV9Ovd3AaTC5s99/KF5Q86zVJXvml8xPRcMGM6dLp+eSSd06oTscMSA==}
+ '@tiptap/core@3.22.4':
+ resolution: {integrity: sha512-vGIGm/HpqLg8EAAQXQ+koV+/S828OEpzocfWcPOwo1u2QUVf9dQG47Yy6JJ8zFFaJwfv4dBcOXli+7BrJwsxDQ==}
peerDependencies:
- '@tiptap/pm': ^3.22.2
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-blockquote@3.22.2':
- resolution: {integrity: sha512-iTdlmGFcgxi4LKaOW2Rc9/yD83qTXgRm5BN3vCHWy5+TbEnReYxYqU5qKsbtTbKy30sO8TJTdAXTZ29uomShQQ==}
+ '@tiptap/extension-blockquote@3.22.4':
+ resolution: {integrity: sha512-7/61kNPbGFhMgM//zMknD0pSb69rGdRIkpulXOWS1JBrFHkH6hjZDfrOETNzgKkO+NlmzVl9rXSTv0xauS3lzA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-bold@3.22.2':
- resolution: {integrity: sha512-bqsPJyKcT/RWse4e16U2EKhraR8a2+98TUuk1amG3yCyFJZStoO/j+pN0IqZdZZjr3WtxFyvwWp7Kc59UN+jUA==}
+ '@tiptap/extension-bold@3.22.4':
+ resolution: {integrity: sha512-jIaPKfNOQu2lhpbLDvtwlQqM+mjF+Kk+auHpzYjBnsuwUli1Cl5ZOau7RH+rru/SQvZe1DtpQlANujDywugZAA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-bubble-menu@3.22.2':
- resolution: {integrity: sha512-5hbyDOSkJwA2uh0v9Mm0Dd9bb9inx6tHBEDSH2tCB9Rm23poz3yOreB7SNX8xDMe5L0/PQesfWC14RitcmhKPg==}
+ '@tiptap/extension-bubble-menu@3.22.4':
+ resolution: {integrity: sha512-v4pux5Ql3THAEjaLMY4ldtdy/Xy2qU7PJLBkq8ugLp8qicaKC+tpqxp6sGif4vLIjz7Ap5hurRbTNbXzszyyHA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-bullet-list@3.22.2':
- resolution: {integrity: sha512-llrTJnA72RGcWLLO+ro0QN4sjHynhaCerhpV+GZE/ATd8BqV/ekQFdBLJrvC/09My2XQfCwLsyCh92NPXUdELA==}
+ '@tiptap/extension-bullet-list@3.22.4':
+ resolution: {integrity: sha512-TB+d3fGcTixYjO7coKqTr1mGTJuqr8hjDCPUFgzuvKyJnBhqWITmBzQ/8CLq4rr6mihgGURbD3N+xkQuPAKFiw==}
peerDependencies:
- '@tiptap/extension-list': ^3.22.2
+ '@tiptap/extension-list': 3.22.4
- '@tiptap/extension-code-block@3.22.2':
- resolution: {integrity: sha512-PEwFlDyvtKF19WCrOFg77qJV9WqhvjCY4ZoXlHP9Hx0KTcOA8W39mtw8d4NWU5pLRK94yHKF1DVVL8UUkEOnww==}
+ '@tiptap/extension-code-block@3.22.4':
+ resolution: {integrity: sha512-MEurzNXfMET3rhjpoPJYUgMfxTdTqbzT9+ToFrqNGAHocdXVm6m1hhO2frVC7fEtHPnxXKsn0Z3NUbCRkRTLuA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-code@3.22.2':
- resolution: {integrity: sha512-iYFY+yzfYA9MKt7nupyW/PzqL9XC2D0mC8l1z2Y10i0/fGL8NbqIYjhNUAyXGqH3QWcI+DirI66842y2OadPOg==}
+ '@tiptap/extension-code@3.22.4':
+ resolution: {integrity: sha512-cnbxmVhAcc7X3G81QUYEmKP0ve2hRmvAiFXBuuv9RUtQlBiRnzmhHoJOMgkX0CsMR7+8kMRpTfeDUYq2xp5s5w==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-collaboration@3.22.2':
- resolution: {integrity: sha512-+viAk2EVoYgJEmJpvnT1NBCK+intvwHEMp7T7luYffkQz8irGKF/7YcgauXp5NBLPTsnIzDWQuY571mo8XMcKg==}
+ '@tiptap/extension-collaboration@3.22.4':
+ resolution: {integrity: sha512-4g4DdpvXZyYYCcWs3cO4DwtzhukqI13waYUKfwOcNnQwZ4YOCR83ET/kgqMk/xMAfbymKghbyAZRCc33zD5xvw==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
'@tiptap/y-tiptap': ^3.0.2
yjs: ^13
- '@tiptap/extension-document@3.22.2':
- resolution: {integrity: sha512-yPw9pQeVC4QDh86TuyKCZxxM4g0NAw7mEtGnAo6EpxaBQr1wyBr9yFpys+QTsQpRTmyTf1VHp4iTTLuWHMljIw==}
+ '@tiptap/extension-document@3.22.4':
+ resolution: {integrity: sha512-XQKla1+703FqQJC48tPDVgt9ucGiFbIEmQdOg5L5o07z9a6/NzuaZAc+1zJ7NxcUZzy+z6wBn1PrVMTiqiSXlw==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-drag-handle-vue-3@3.22.2':
- resolution: {integrity: sha512-AuX2TTXgY/Z7eO+2nR4YJq2ibzKl9zjmmLFBPXlaBmFtDeL5ks5SOkMdGYyJp766VTuT3AWnq/07evwYB88jzA==}
+ '@tiptap/extension-drag-handle-vue-3@3.22.4':
+ resolution: {integrity: sha512-ySedk/4Szd8M3t2yE+5tjfXyTIwanmQ51jIGxro23D383upDxD0c/lrTk+9+KUWJfsUTqOrJDrzogpRF+F30dQ==}
peerDependencies:
- '@tiptap/extension-drag-handle': ^3.22.2
- '@tiptap/pm': ^3.22.2
- '@tiptap/vue-3': ^3.22.2
+ '@tiptap/extension-drag-handle': 3.22.4
+ '@tiptap/pm': 3.22.4
+ '@tiptap/vue-3': 3.22.4
vue: ^3.0.0
- '@tiptap/extension-drag-handle@3.22.2':
- resolution: {integrity: sha512-9L2krYNe+ZxI7hULAuxE0i9wKMxL8eIoiH866hrOenb2C8PySQLWy/BjWwu3Z6fBFwCG+29wiMeRL7WE128oxg==}
+ '@tiptap/extension-drag-handle@3.22.4':
+ resolution: {integrity: sha512-VCheiqy0OGrcrxOYt7Kp7MXWMZoEaZp517HnRrdrvcCCO0j2hcrT5mbo1mMPzP/WvTUHkXCmqcLv+uIk3Bdo/g==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/extension-collaboration': ^3.22.2
- '@tiptap/extension-node-range': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/extension-collaboration': 3.22.4
+ '@tiptap/extension-node-range': 3.22.4
+ '@tiptap/pm': 3.22.4
'@tiptap/y-tiptap': ^3.0.2
- '@tiptap/extension-dropcursor@3.22.2':
- resolution: {integrity: sha512-sDv3fv4LtX0X4nqwh9Gn3C/aZXT+C2JlK7tJovPOpaYP/a6hr03Sn35X5moAfgMCSiWFygEvlTriqwmCsJuxog==}
+ '@tiptap/extension-dropcursor@3.22.4':
+ resolution: {integrity: sha512-N9/yMDC35jJp0V/naL0+6gi4gUDUIcPpWEzFdCDWUSYBA8mt41c1kI1ZU7UTKYIBzTClenhYHRc2XKZxxx0+LQ==}
peerDependencies:
- '@tiptap/extensions': ^3.22.2
+ '@tiptap/extensions': 3.22.4
- '@tiptap/extension-floating-menu@3.22.2':
- resolution: {integrity: sha512-r0ZTeh9rNtj9Api+G0YyaB+tAKPDn7aYWg+qSrmAC5EyUPee6Zjn3zlw0q4renCeQflvNRK20xHM8zokC41jOA==}
+ '@tiptap/extension-floating-menu@3.22.4':
+ resolution: {integrity: sha512-DFuyYxgaZPgxum5z1yvJPbfYCvDdO8geXsdyqt0qYYdiat3aGE4ncJhiLRIFDhSHBhaZg5eCgu/YPYAN6jZnrA==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-gapcursor@3.22.2':
- resolution: {integrity: sha512-rR2OLrl/k2kj7xehaZHq0Y7T+1wy2DOTabir9LsTrktTFEcklrh9qY1KC6rEBkwMKaWrmignR1l39kS6RlKFNw==}
+ '@tiptap/extension-gapcursor@3.22.4':
+ resolution: {integrity: sha512-UYBEUj3SFpKINIE7AdzcyeS3xICK+ee+YLBbuqNXyHStYChjJOohzJehqiqhjR16A88KQQ+ZjgyDcItKGygSog==}
peerDependencies:
- '@tiptap/extensions': ^3.22.2
+ '@tiptap/extensions': 3.22.4
- '@tiptap/extension-hard-break@3.22.2':
- resolution: {integrity: sha512-ChsoqF4XRp6EWatTRlXL4LMFh/ggwRVCyt09brSfjJV5knFaXlECSa5/+rKLMLMULaj6dVlJqoAD15exgu2HHA==}
+ '@tiptap/extension-hard-break@3.22.4':
+ resolution: {integrity: sha512-xq+a4dE7T6VwApCkh/yU3p30gn3F8g8Arb9CyEZm58/WIJUIGvHSTjDdHmvU16+kiWSBg+wOOsaFHhYjJjxcKA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-heading@3.22.2':
- resolution: {integrity: sha512-QPHLef+ikAyf7RVc4EdGeKxH4OEGb3ueCEwJ41RcYPtZ1BX9ueei7FC936guTdL1U7w3vQ65qfy86HznzkYgvw==}
+ '@tiptap/extension-heading@3.22.4':
+ resolution: {integrity: sha512-TUaj5f0Ir5qy9HKKt2ocnwfXKpZDYeHgbbP9gshKFzdq5PLe1RbIgkjfy6bnoI865cYjmPYWRjcT7XsKyIcb9Q==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-horizontal-rule@3.22.2':
- resolution: {integrity: sha512-Oz8KN5KJAWV1mFNE9UIWXdMD6xa5zPf/0yLsT8V4sgaRm+VsdFKllN58BY9qCZf/kIZbaOez5KkaoeAcm0MAZg==}
+ '@tiptap/extension-horizontal-rule@3.22.4':
+ resolution: {integrity: sha512-cCI1HekGQwhY/MbgaKQ0R/7HcH5ZM1oFAyI/J72QGLC0XnF403S/OXoHMuBWr1mCu8hNiQWCzeNRJUty0iytNw==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-image@3.22.2':
- resolution: {integrity: sha512-xFCgwreF6sn5mQ/hFDQKn41NIbbfks/Ou9j763Djf3pWsastgzdgwifQOpXVI3aSsqlKUO3o8/8R/yQczvZcwg==}
+ '@tiptap/extension-image@3.22.4':
+ resolution: {integrity: sha512-ZDc+fLaratTQ4IgnKcJJwfUgUgpcHjbZSBi6UQAILJwkflMy1Zxj8mpbma5P934nLSI+uDnR5ret6ZZLNITKhA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-italic@3.22.2':
- resolution: {integrity: sha512-fmtQu2HDnV3sOZPdz0+1lOLI7UtrIhusohJj2UwOLQxG8qqhLwbvWx2OQTlfblgY0z+CjLRr6ANbNDxOTIblfg==}
+ '@tiptap/extension-italic@3.22.4':
+ resolution: {integrity: sha512-fVSDx5AYXgDI3v2zZIqb7V8EewthwM2NJ/ZCX+XaxRsqNEpnjVhgHs7UlvDqK1wj2OJ6zmUNjPtVlAFRxwT+HQ==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-link@3.22.2':
- resolution: {integrity: sha512-TXfSoKmng5pecvQUZqdsx6ICeob5V5hhYOj2vCEtjfcjWsyCndqFIl1w+Nt/yI5ehrFNOVPyj3ZvcELuuAW6pw==}
+ '@tiptap/extension-link@3.22.4':
+ resolution: {integrity: sha512-uoP3yus02uwGPVzW2QaEPJWVIrUb/r5nKm6c8DiJv9fNSX1+gykZZMg42c6GwRFLZ/vyfWjVCbAE03VMUqafgA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-list-item@3.22.2':
- resolution: {integrity: sha512-Mk+iiLIFh8Pfuarr6mWfTO7QJbd2ZQd0nGNhNWXlGAO7DJCb4BP9nj4bEIJ17SbcykGRjsi4WMqY50z4MHXqKQ==}
+ '@tiptap/extension-list-item@3.22.4':
+ resolution: {integrity: sha512-H659KXTvggSypIDWSOJBZ37jh9pKjQriDDvYPYvOZCdfij0D0hsDXN/wXoypArneUkoBdgruHfTtMkFOaQlgkw==}
peerDependencies:
- '@tiptap/extension-list': ^3.22.2
+ '@tiptap/extension-list': 3.22.4
- '@tiptap/extension-list-keymap@3.22.2':
- resolution: {integrity: sha512-TozU9V2vldMUPpTXnfLCO33EO06jLxn7uEJTMBnN4iX/dLV3cBVCbE4kHyDKS0sLd7joUeekS06vYP9uQb1hFw==}
+ '@tiptap/extension-list-keymap@3.22.4':
+ resolution: {integrity: sha512-t/zhker4oIS78AIGYDdFFfZC6zSBlszfD7z/zqFLGCg5PHNNgkZK5hKj6Vyix6D2SapRn/ajnx+8mhbKIUH5eA==}
peerDependencies:
- '@tiptap/extension-list': ^3.22.2
+ '@tiptap/extension-list': 3.22.4
- '@tiptap/extension-list@3.22.2':
- resolution: {integrity: sha512-Vq9xScgkA2A3Zj9dQ4WUBKK7u7UCzeSFRz9FcKTQVZHRPbZoqFGnlRUVngqsE7JXrCOthXQ1dXxgk40nAsBFRw==}
+ '@tiptap/extension-list@3.22.4':
+ resolution: {integrity: sha512-Xe8UFvvHmyp/c/TJsFwlwU9CWACYbBirNsluJ3U1+H8BTu1wqdrT/AXR5uIXeyCl5kiWKgX5q71eHWbYFOrqrg==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-mention@3.22.2':
- resolution: {integrity: sha512-nWcDiIkh/V7aUZJhirimCuiWUPByiEPwdJCTgty7dbwEfZyrlldAnbzf66o3z2XKiFNeHRaOyo86XXnbY4euDw==}
+ '@tiptap/extension-mention@3.22.4':
+ resolution: {integrity: sha512-ZUJ1gCZlH+JGTAT7lVpZjcMAzvIi9hXIyBjOmjL+737NlF+Cfgo+fjHqFQgOSsiO9LEyc3ZMSclmbzII1Jy6IQ==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
- '@tiptap/suggestion': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
+ '@tiptap/suggestion': 3.22.4
- '@tiptap/extension-node-range@3.22.2':
- resolution: {integrity: sha512-hipsIUXrU9RUcc32BLJ/mtfiCtgV35oMTMxEJTJWxJhebEw0iWd7L6cLwHbKui6HgH4W82Zo1s1Ia0Owq3Nu8w==}
+ '@tiptap/extension-node-range@3.22.4':
+ resolution: {integrity: sha512-AJgZPX9DLDjN1nE4WLQBPHRChxrV3JuWHF6OakZIdO23cuBcg233FCQTqHQ/eNS/tD3J+Kw9gBc4ZUDhBes9gg==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-ordered-list@3.22.2':
- resolution: {integrity: sha512-K7qxoBKmsVkAd3kW64ZRCUPFrDcNGpXRDUBx9YgAO/bTfsfxtH2oil+igsUWGXPczpP4yoHPKjTfhpBpLjGl6Q==}
+ '@tiptap/extension-ordered-list@3.22.4':
+ resolution: {integrity: sha512-w77hPVf7pcHt97vfrybg/l0t5CimCd4y75OJKuHuo3CfgM5xbUP/gaPNMDyLLe7MYole/UHi/XvG3XjgzqTzAw==}
peerDependencies:
- '@tiptap/extension-list': ^3.22.2
+ '@tiptap/extension-list': 3.22.4
- '@tiptap/extension-paragraph@3.22.2':
- resolution: {integrity: sha512-EHZZzxVhvzEPDPWtRBF1YKhB+WCUjd1C2NhjHfL3Dl71PBqM3ZWA6qN7NDGPyNyGGWauui/NR/4X+5AfPqlHyA==}
+ '@tiptap/extension-paragraph@3.22.4':
+ resolution: {integrity: sha512-de6dFkIhigiENESY6rNJ3yTVS/337ybfP30dNPudTwGe9oAu9ZCS+04j6QCvXSjhlI3ULiv7wiSHqrP26Gd+Hw==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-placeholder@3.22.2':
- resolution: {integrity: sha512-xYw733CmSeG7MyYBDdV5NFiwlBdXXzw4Mvjb2t4QRXagkDbHeNY/LtKTcrtcMNfO4Jx0mwivGQZUIEC8oAfvxg==}
+ '@tiptap/extension-placeholder@3.22.4':
+ resolution: {integrity: sha512-Z3wtWL+KufwkC7CkJge5enAxx4q8C3oOYixme02snY9zfjX3V/1pjAmEfP4wxScgM5GIuTEJ83B9Yz3wRzPA6Q==}
peerDependencies:
- '@tiptap/extensions': ^3.22.2
+ '@tiptap/extensions': 3.22.4
- '@tiptap/extension-strike@3.22.2':
- resolution: {integrity: sha512-YFC3elKU1L8PiGbcB6tqd/7vWPF5IbydJz0POJpHzSjstX+VfT8VsvS7ubxVuSIWQ11kGkH3mzX6LX8JHsHZxg==}
+ '@tiptap/extension-strike@3.22.4':
+ resolution: {integrity: sha512-aRHWQj42HiailXSC9LkKYM3jWMcSeGwOjbqM4PiuxQZmHVDRFmeHkfJItOdn2cSHaO0vuEVK+TvrWUWsBFi3pg==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-text@3.22.2':
- resolution: {integrity: sha512-J1w7JwijfSD7ah0WfiwZ/DVWCIGT9x369RM4RJc57i44mIBElj7tl1dh+N5KPGOXKUup4gr7sSJAE38lgeaDMg==}
+ '@tiptap/extension-text@3.22.4':
+ resolution: {integrity: sha512-mM69uUW5cSxIhyEpWXi/YcfyupcJMDLCPEfYi62awH0iOP/LRoCv/nHjJq4Hyj/KxRJbe8HKwIUnqaCUf7m5Pg==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extension-underline@3.22.2':
- resolution: {integrity: sha512-BaV6WOowxdkGTLWiU7DdZ3Twh633O4RGqwUM5dDas5LvaqL8AMWGTO8Wg9yAaaKXzd9MtKI1ZCqS/+MtzusgkQ==}
+ '@tiptap/extension-underline@3.22.4':
+ resolution: {integrity: sha512-08kGdbhIrA6h10GWXqOkqIveaBj5tmxclK208/nUIAlonI9hPd739vu7fmVtpnmqCnSSNpoRtU4u6Gj5at0ZpA==}
peerDependencies:
- '@tiptap/core': ^3.22.2
+ '@tiptap/core': 3.22.4
- '@tiptap/extensions@3.22.2':
- resolution: {integrity: sha512-s7MZmm2Xdq+8feIXgY3v7gVpQ5ClqBZi20KheouS7KSbBlrY4fu2irYR1EGc6r1UUVaHMxEa+cx5knhx+mIPUw==}
+ '@tiptap/extensions@3.22.4':
+ resolution: {integrity: sha512-fOe8VptJvLPs32bNdUYo8SRyljwqKNQVXWW056VoXIc5en/59OdJlJQVeHI0jRRciH3MtrqODi/gfJR0VHNZ8A==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/markdown@3.22.2':
- resolution: {integrity: sha512-ZBna+5fMeaQ8vfzXT1HmMFvLauU0xSQJTA6b+Zqvh2zbD4BWteXf3bfdTrjQqNy0S97S2wFubYruSCr2/OlllQ==}
+ '@tiptap/markdown@3.22.4':
+ resolution: {integrity: sha512-gcoLOq5TBntw13QdeWMy7yc2X+b9yTplcFb5kpMQNgNoxlu0+jlX4LiBR+E6lOV+m+AtkprHHltTYyKG23bdOw==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/pm@3.22.2':
- resolution: {integrity: sha512-G2ENwIazoSKkAnN5MN5yN91TIZNFm6TxB74kPf3Empr2k9W51Hkcier70jHGpArhgcEaL4BVreuU1PRDRwCeGw==}
+ '@tiptap/pm@3.22.4':
+ resolution: {integrity: sha512-hj8Qka6WcHRllHUdeSjDnq2XaisUo4KsoGJc1WcFpoa1Yd+OeD861zUMnV7DFVGdZRy45Obht0CUYJpXQ4yA4w==}
- '@tiptap/starter-kit@3.22.2':
- resolution: {integrity: sha512-+CCKX8tOQ/ZPb2k/z6em4AQCFYAcdd8+0TOzPWiuLxRyCHRPBBVhnPsXOKgKwE4OO3E8BsezquuYRYRwsyzCqg==}
+ '@tiptap/starter-kit@3.22.4':
+ resolution: {integrity: sha512-qWjw+vfdin1rzMRpRU4cC5tLTwMJtUpXeQukv+6mOqqvhptuwuZBjUHImVEJaSPoHXS7+1ut+nTnrLyWyEuE5Q==}
- '@tiptap/suggestion@3.22.2':
- resolution: {integrity: sha512-t2GQSrF4eQyPb+KqXVfcC2cokYIDNfpLLq7B0ELlnWBJURnLOVJ2ssJ6ASI247scu9ZKPG1g5bFP4IXdBhyPgg==}
+ '@tiptap/suggestion@3.22.4':
+ resolution: {integrity: sha512-1buvLZemITTeKmPf2wGFWvvhRFKjdQ+JgMqc67xBraOKeDd8wQi1e2XlhCYAtlVMm5f6j+qlLC/MvwuHI2jHeQ==}
peerDependencies:
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
- '@tiptap/vue-3@3.22.2':
- resolution: {integrity: sha512-oRJQEwcsg8LztIeuKeJea1UhIX2NtGkBDPM/biQlA3SKa/JvgNWGb0+ZJj0AWQjGhKFOoVKnfwVRysUeZeKOSg==}
+ '@tiptap/vue-3@3.22.4':
+ resolution: {integrity: sha512-fcqUWt6LlA5PbcFaDXyV1apWwAs8j80m0kWwoL5+DgKdkGxsB5LgDZU1pTWle0zvR5zmGvJ7LmB6EGAYIBjdmQ==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
- '@tiptap/core': ^3.22.2
- '@tiptap/pm': ^3.22.2
+ '@tiptap/core': 3.22.4
+ '@tiptap/pm': 3.22.4
vue: ^3.0.0
'@tiptap/y-tiptap@3.0.2':
@@ -3693,20 +3765,20 @@ packages:
peerDependencies:
vue: '>=3.5.18'
- '@unocss/core@66.6.8':
- resolution: {integrity: sha512-P9IlQfgms+8/nka7fBhiiWU4SPwrTNKbTdK0z1SLnttXMHHjsB2zpG+Vi1JQDpICfY9Y1/2pWtguPE+zeOVu9Q==}
+ '@unocss/core@66.6.7':
+ resolution: {integrity: sha512-Q8456iWFtdwrUNYKVOQY8ygRggjZOVtLc6Jc8KIkxig7OiNlUWOgXJTfCh4I8g6jBYzC5eHaHFDLgJOmOrxBsg==}
- '@unocss/extractor-arbitrary-variants@66.6.8':
- resolution: {integrity: sha512-cOXstpPTOLt/HYcL0OsqFkNau0e8ktZ5Q8fgnXBZjmLGmi+VzdESNlwxZyCXLuamZGnbrZ8lDsKdsGG7P1pMKQ==}
+ '@unocss/extractor-arbitrary-variants@66.6.7':
+ resolution: {integrity: sha512-PQiBHK0yUJ0BR+3GYnTPU6va6HVSRPV+O+s1zZmt23TWbyIeucoKCNR47TDtv+Z1xuksY8krIjtDYtufdrVWKw==}
- '@unocss/preset-mini@66.6.8':
- resolution: {integrity: sha512-vAechrReO7LtWzFAeF54P7CintG2m65SlVlBsi1x2Ru7IdgUNJEHII0MfXUvf9r1x8vsIlhATyaqqtBVT6ps/w==}
+ '@unocss/preset-mini@66.6.7':
+ resolution: {integrity: sha512-tf0mqiSEhPQ49WZOqjNhxlbZbNakiBLzCoxfLSzqfIGglOPYShP8mxsdp9Jv0n+Ntn0rHcBiX5KTLfax1/Bd9g==}
- '@unocss/preset-wind3@66.6.8':
- resolution: {integrity: sha512-WNTeDAYCatmEFjBJ4itUmz0TElBvNFqjh5i2/ianDJO/vkd+IYUb03jEPLUppVlvMhy8bN8AunP0AtW3Xf2psA==}
+ '@unocss/preset-wind3@66.6.7':
+ resolution: {integrity: sha512-PKyqeRzlIMd3Irdt6fCKMm73zgwweiXESk5edUK8dVWndvPIcZCOqrEq7yg6Pr/Q8tHdq26viYSkVY3a3t8RSg==}
- '@unocss/rule-utils@66.6.8':
- resolution: {integrity: sha512-WR35L07mLP6PElD4hlUHo5KbQ48uz2HT/XCuJyAsHP+15Gv6539hPWA5SresPuva9r8rl+PeGIgMSIKf4A5Ihw==}
+ '@unocss/rule-utils@66.6.7':
+ resolution: {integrity: sha512-4PT/s8yKIShSqP9XPSw4EjbZopcu3wlIB9i3kbGbzQwF91H+0Yy10guK3kHDGtkmWVN6Np6VvaGIj2UcbmaivA==}
engines: {node: '>=14'}
'@unovis/dagre-layout@0.8.8-2':
@@ -3873,6 +3945,10 @@ packages:
engines: {node: '>=20'}
hasBin: true
+ '@vercel/oidc@3.1.0':
+ resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==}
+ engines: {node: '>= 20'}
+
'@vercel/oidc@3.2.0':
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
engines: {node: '>= 20'}
@@ -3931,9 +4007,15 @@ packages:
vite:
optional: true
+ '@vitest/pretty-format@4.1.3':
+ resolution: {integrity: sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==}
+
'@vitest/pretty-format@4.1.4':
resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==}
+ '@vitest/runner@4.1.3':
+ resolution: {integrity: sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA==}
+
'@vitest/runner@4.1.4':
resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==}
@@ -3943,6 +4025,9 @@ packages:
'@vitest/spy@4.1.4':
resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==}
+ '@vitest/utils@4.1.3':
+ resolution: {integrity: sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==}
+
'@vitest/utils@4.1.4':
resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==}
@@ -4145,6 +4230,12 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
+ ai@6.0.154:
+ resolution: {integrity: sha512-HfKJKCTJsDZxqrIUDSVnBQ7DpQlx5WI4ExqtLd7Bl70epLmvkpc/HYMzU1hP9W+g9VEAcvZo4fbMqc3v5D+9gQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
ai@6.0.168:
resolution: {integrity: sha512-2HqCJuO+1V2aV7vfYs5LFEUfxbkGX+5oa54q/gCCTL7KLTdbxcCu5D7TdLA5kwsrs3Szgjah9q6D9tpjHM3hUQ==}
engines: {node: '>=18'}
@@ -4350,7 +4441,6 @@ packages:
basic-ftp@5.2.0:
resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==}
engines: {node: '>=10.0.0'}
- deprecated: Security vulnerability fixed in 5.2.1, please upgrade
better-sqlite3@12.9.0:
resolution: {integrity: sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==}
@@ -4568,6 +4658,20 @@ packages:
colortranslator@5.0.0:
resolution: {integrity: sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w==}
+ comark@0.2.1:
+ resolution: {integrity: sha512-DBc24O/utr7p0TPqxZwm7fsZPmA+2y3ZOrOaXF9Wk3YhETzz7lzN294epUjGEEXkPOxOs8G8RZOPbLCg3bY0Ag==}
+ peerDependencies:
+ beautiful-mermaid: ^1.1.3
+ katex: ^0.16.33
+ shiki: ^4.0.0
+ peerDependenciesMeta:
+ beautiful-mermaid:
+ optional: true
+ katex:
+ optional: true
+ shiki:
+ optional: true
+
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
@@ -4684,9 +4788,6 @@ packages:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
engines: {node: '>= 14'}
- crelt@1.0.6:
- resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
-
croner@10.0.1:
resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==}
engines: {node: '>=18.0'}
@@ -5080,16 +5181,32 @@ packages:
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+ dom-serializer@3.0.0:
+ resolution: {integrity: sha512-x+9D6nkC8tdXOQUS32egtZpZFLP90+HBZmWjuT920srbJvD/zPgFB9t4k3pEhlw5BQrXStQtRc1Y1zuriXk+Nw==}
+ engines: {node: '>=20.19.0'}
+
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+ domelementtype@3.0.0:
+ resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==}
+ engines: {node: '>=20.19.0'}
+
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
+ domhandler@6.0.1:
+ resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==}
+ engines: {node: '>=20.19.0'}
+
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+ domutils@4.0.2:
+ resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==}
+ engines: {node: '>=20.19.0'}
+
dot-prop@10.1.0:
resolution: {integrity: sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==}
engines: {node: '>=20'}
@@ -5334,6 +5451,10 @@ packages:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -5584,6 +5705,59 @@ packages:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
+ evlog@2.12.0:
+ resolution: {integrity: sha512-uCnpBZlrjf51Evz+jTkydviq0QxiwPz4yGDmTjTKePelsIcbCiS0adPd5s26ZPf4tAf97HtWoYdlKd0JhydIkg==}
+ peerDependencies:
+ '@nestjs/common': '>=11.1.18'
+ '@nuxt/kit': ^4.4.2
+ '@tanstack/start-client-core': ^1.167.9
+ ai: '>=6.0.154'
+ elysia: '>=1.4.28'
+ express: '>=5.2.1'
+ fastify: '>=5.8.4'
+ h3: ^1.15.11
+ hono: ''
+ next: '>=16.2.3'
+ nitro: ^3.0.260311-beta
+ nitropack: ^2.13.3
+ ofetch: ^1.5.1
+ react: '>=19.2.5'
+ react-router: '>=7.14.0'
+ vite: ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@nestjs/common':
+ optional: true
+ '@nuxt/kit':
+ optional: true
+ '@tanstack/start-client-core':
+ optional: true
+ ai:
+ optional: true
+ elysia:
+ optional: true
+ express:
+ optional: true
+ fastify:
+ optional: true
+ h3:
+ optional: true
+ hono:
+ optional: true
+ next:
+ optional: true
+ nitro:
+ optional: true
+ nitropack:
+ optional: true
+ ofetch:
+ optional: true
+ react:
+ optional: true
+ react-router:
+ optional: true
+ vite:
+ optional: true
+
execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
@@ -6096,11 +6270,14 @@ packages:
hookable@6.1.0:
resolution: {integrity: sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==}
+ hookable@6.1.1:
+ resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==}
+
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
- html-validate@10.13.0:
- resolution: {integrity: sha512-3Lw/gGLu6EmMsl8LzAYyolDapVfZf60i0azWKxeoklhmS0Y145igwQ+MOPqIGZLGC3uP8SOBNXwLbZVPU9Ok7w==}
+ html-validate@10.13.1:
+ resolution: {integrity: sha512-1ox3gMhul5GwXnftNEY5nqnEQJ1B2K1GNu2uSO3nJ6D3gmGh/xsKtcSi3zelHam1nFNXyC14vTay+gpS+tbsdA==}
engines: {node: ^20.19.0 || ^22.16.0 || >= 24.0.0}
hasBin: true
peerDependencies:
@@ -6146,6 +6323,10 @@ packages:
html-whitespace-sensitive-tag-names@3.0.1:
resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==}
+ htmlparser2@12.0.0:
+ resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==}
+ engines: {node: '>=20.19.0'}
+
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@@ -6766,6 +6947,9 @@ packages:
maplibre-gl@2.4.0:
resolution: {integrity: sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==}
+ markdown-exit@1.0.0-beta.9:
+ resolution: {integrity: sha512-5tzrMKMF367amyBly131vm6eGuWRL2DjBqWaFmPzPbLyuxP0XOmyyyroOAIXuBAMF/3kZbbfqOxvW/SotqKqbQ==}
+
markdown-it@14.1.1:
resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==}
hasBin: true
@@ -7015,8 +7199,8 @@ packages:
'@vueuse/core': '>=10.0.0'
vue: '>=3.0.0'
- motion-v@2.2.0:
- resolution: {integrity: sha512-8rrUBt9UMwy45MfLMwHer9J7xBY/rL31S+0U3ZUAouqjH3AOVHvS0NGuXwF4mfWfvb22rfZEv59ZsBc2f1epxQ==}
+ motion-v@2.2.1:
+ resolution: {integrity: sha512-BYbABe1Ep/u33dHOrK+8SoVU2MuiQqT94JOYsgrge8QbrwkKf2lS6rHW2QyzP6t89wcyBvzZeLQQwfrx76dj9A==}
peerDependencies:
'@vueuse/core': '>=10.0.0'
vue: '>=3.0.0'
@@ -7737,9 +7921,6 @@ packages:
prosemirror-changeset@2.4.0:
resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==}
- prosemirror-collab@1.3.1:
- resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
-
prosemirror-commands@1.7.1:
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
@@ -7752,24 +7933,12 @@ packages:
prosemirror-history@1.5.0:
resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
- prosemirror-inputrules@1.5.1:
- resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
-
prosemirror-keymap@1.2.3:
resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
- prosemirror-markdown@1.13.4:
- resolution: {integrity: sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==}
-
- prosemirror-menu@1.3.0:
- resolution: {integrity: sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==}
-
prosemirror-model@1.25.4:
resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==}
- prosemirror-schema-basic@1.2.4:
- resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==}
-
prosemirror-schema-list@1.5.1:
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
@@ -7779,13 +7948,6 @@ packages:
prosemirror-tables@1.8.5:
resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
- prosemirror-trailing-node@3.0.0:
- resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
- peerDependencies:
- prosemirror-model: ^1.22.1
- prosemirror-state: ^1.4.2
- prosemirror-view: ^1.33.8
-
prosemirror-transform@1.12.0:
resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
@@ -7956,8 +8118,8 @@ packages:
rehype-sort-attributes@5.0.1:
resolution: {integrity: sha512-Bxo+AKUIELcnnAZwJDt5zUDDRpt4uzhfz9d0PVGhcxYWsbFj5Cv35xuWxu5r1LeYNFNhgGqsr9Q2QiIOM/Qctg==}
- reka-ui@2.9.3:
- resolution: {integrity: sha512-C9lCVxsSC7uYD0Nbgik1+14FNndHNprZmf0zGQt0ZDYIt5KxXV3zD0hEqNcfRUsEEJvVmoRsUkJnASBVBeaaUw==}
+ reka-ui@2.9.6:
+ resolution: {integrity: sha512-K6bL457owpvWONc7hsjFxo3HDC9s6IzhRqShW0w9JSKelPGfRbkHD558UQTn/NH1cvrXVHygKyC7fExFmRketg==}
peerDependencies:
vue: '>= 3.4.0'
@@ -8190,6 +8352,29 @@ packages:
engines: {node: '>= 0.10'}
hasBin: true
+ shaders@2.5.93:
+ resolution: {integrity: sha512-ZM41grBXeWzmG/yGCr5IKWxnvEUya/zpxcLstHmqs4kB150vkLg0getvgO0uF0pjppddyCwGJt22Y4v3MogkTg==}
+ peerDependencies:
+ pixi.js: ^8.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ solid-js: ^1.8.0
+ svelte: ^5
+ vue: ^3.5.0
+ peerDependenciesMeta:
+ pixi.js:
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ solid-js:
+ optional: true
+ svelte:
+ optional: true
+ vue:
+ optional: true
+
sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -8468,6 +8653,11 @@ packages:
svix@1.90.0:
resolution: {integrity: sha512-ljkZuyy2+IBEoESkIpn8sLM+sxJHQcPxlZFxU+nVDhltNfUMisMBzWX/UR8SjEnzoI28ZjCzMbmYAPwSTucoMw==}
+ swrv@1.2.0:
+ resolution: {integrity: sha512-lH/g4UcNyj+7lzK4eRGT4C68Q4EhQ6JtM9otPRIASfhhzfLWtbZPHcMuhuba7S9YVYuxkMUGImwMyGpfbkH07A==}
+ peerDependencies:
+ vue: '>=3.2.26 < 4'
+
table@6.9.0:
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
engines: {node: '>=10.0.0'}
@@ -8531,6 +8721,9 @@ packages:
three@0.135.0:
resolution: {integrity: sha512-kuEpuuxRzLv0MDsXai9huCxOSQPZ4vje6y0gn80SRmQvgz6/+rI0NAvCRAw56zYaWKMGMfqKWsxF9Qa2Z9xymQ==}
+ three@0.183.2:
+ resolution: {integrity: sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==}
+
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
engines: {node: '>=12.22'}
@@ -8556,6 +8749,10 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinyglobby@0.2.16:
+ resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
+ engines: {node: '>=12.0.0'}
+
tinyqueue@2.0.3:
resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==}
@@ -9151,8 +9348,8 @@ packages:
vue-component-type-helpers@2.2.12:
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
- vue-component-type-helpers@3.2.6:
- resolution: {integrity: sha512-O02tnvIfOQVmnvoWwuSydwRoHjZVt8UEBR+2p4rT35p8GAy5VTlWP8o5qXfJR/GWCN0nVZoYWsVUvx2jwgdBmQ==}
+ vue-component-type-helpers@3.2.7:
+ resolution: {integrity: sha512-+gPp5YGmhfsj1IN+xUo7y0fb4clfnOiiUA39y07yW1VzCRjzVgwLbtmdWlghh7mXrPsEaYc7rrIir/HT6C8vYQ==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -9441,6 +9638,12 @@ snapshots:
'@phc/format': 1.0.0
'@poppinss/utils': 6.10.1
+ '@ai-sdk/anthropic@3.0.68(zod@4.3.6)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.8
+ '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6)
+ zod: 4.3.6
+
'@ai-sdk/gateway@3.0.104(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.8
@@ -9448,6 +9651,20 @@ snapshots:
'@vercel/oidc': 3.2.0
zod: 4.3.6
+ '@ai-sdk/gateway@3.0.91(zod@4.3.6)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.8
+ '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6)
+ '@vercel/oidc': 3.1.0
+ zod: 4.3.6
+
+ '@ai-sdk/gateway@3.0.94(zod@4.3.6)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.8
+ '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6)
+ '@vercel/oidc': 3.1.0
+ zod: 4.3.6
+
'@ai-sdk/mcp@1.0.36(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.8
@@ -9466,6 +9683,15 @@ snapshots:
dependencies:
json-schema: 0.4.0
+ '@ai-sdk/vue@3.0.154(vue@3.5.32(typescript@6.0.3))(zod@4.3.6)':
+ dependencies:
+ '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6)
+ ai: 6.0.154(zod@4.3.6)
+ swrv: 1.2.0(vue@3.5.32(typescript@6.0.3))
+ vue: 3.5.32(typescript@6.0.3)
+ transitivePeerDependencies:
+ - zod
+
'@alloc/quick-lru@5.2.0': {}
'@antfu/install-pkg@1.1.0':
@@ -9678,6 +9904,37 @@ snapshots:
'@colordx/core@5.0.3': {}
+ '@comark/markdown-it@0.3.3(@types/markdown-it@14.1.2)(markdown-it@14.1.1)':
+ dependencies:
+ '@types/markdown-it': 14.1.2
+ js-yaml: 4.1.1
+ markdown-it: 14.1.1
+
+ '@comark/nuxt@0.2.1(@types/markdown-it@14.1.2)(magicast@0.5.2)(markdown-it@14.1.1)(nuxt@4.4.2(ec9ba8feacf9ea1d35ac88469523465a))(shiki@4.0.2)(vue@3.5.32(typescript@6.0.3))':
+ dependencies:
+ '@comark/vue': 0.2.1(@types/markdown-it@14.1.2)(markdown-it@14.1.1)(shiki@4.0.2)(vue@3.5.32(typescript@6.0.3))
+ '@nuxt/kit': 4.4.2(magicast@0.5.2)
+ comark: 0.2.1(@types/markdown-it@14.1.2)(markdown-it@14.1.1)(shiki@4.0.2)
+ nuxt: 4.4.2(ec9ba8feacf9ea1d35ac88469523465a)
+ transitivePeerDependencies:
+ - '@types/markdown-it'
+ - beautiful-mermaid
+ - katex
+ - magicast
+ - markdown-it
+ - shiki
+ - vue
+
+ '@comark/vue@0.2.1(@types/markdown-it@14.1.2)(markdown-it@14.1.1)(shiki@4.0.2)(vue@3.5.32(typescript@6.0.3))':
+ dependencies:
+ comark: 0.2.1(@types/markdown-it@14.1.2)(markdown-it@14.1.1)(shiki@4.0.2)
+ vue: 3.5.32(typescript@6.0.3)
+ optionalDependencies:
+ shiki: 4.0.2
+ transitivePeerDependencies:
+ - '@types/markdown-it'
+ - markdown-it
+
'@drizzle-team/brocli@0.10.2': {}
'@dxup/nuxt@0.4.0(magicast@0.5.2)(typescript@6.0.3)':
@@ -10197,7 +10454,7 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
- '@iconify-json/simple-icons@1.2.78':
+ '@iconify-json/simple-icons@1.2.79':
dependencies:
'@iconify/types': 2.0.0
@@ -10777,7 +11034,7 @@ snapshots:
ofetch: 1.5.1
pathe: 2.0.3
sirv: 3.0.2
- tinyglobby: 0.2.15
+ tinyglobby: 0.2.16
ufo: 1.6.3
unifont: 0.7.4
unplugin: 3.0.0
@@ -10813,7 +11070,7 @@ snapshots:
defu: 6.1.7
devalue: 5.7.0
h3: 1.15.11
- html-validate: 10.13.0(vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(happy-dom@20.9.0)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
+ html-validate: 10.13.1(vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(happy-dom@20.9.0)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
knitwork: 1.3.0
magic-string: 0.30.21
nitropack: 2.13.3(@libsql/client@0.17.2)(@vercel/functions@3.4.3)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(rolldown@1.0.0-beta.57(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(srvx@0.11.15)
@@ -10886,7 +11143,7 @@ snapshots:
pathe: 2.0.3
picomatch: 4.0.4
std-env: 3.10.0
- tinyglobby: 0.2.15
+ tinyglobby: 0.2.16
transitivePeerDependencies:
- magicast
- vite
@@ -11158,12 +11415,10 @@ snapshots:
- typescript
- vite
- '@nuxt/ui@4.6.1(@nuxt/content@3.13.0(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(magicast@0.5.2)(valibot@1.3.1(typescript@6.0.3)))(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(@vercel/functions@3.4.3)(change-case@5.4.4)(db0@0.3.4(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@6.0.3)(valibot@1.3.1(typescript@6.0.3))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@6.0.3))(yjs@13.6.30)(zod@4.3.6)':
+ '@nuxt/ui@https://pkg.pr.new/@nuxt/ui@8d08ef3(7f3fd92f21d5ecaed314c37ee5a01ec7)':
dependencies:
'@floating-ui/dom': 1.7.6
'@iconify/vue': 5.0.0(vue@3.5.32(typescript@6.0.3))
- '@internationalized/date': 3.12.0
- '@internationalized/number': 3.6.5
'@nuxt/fonts': 0.14.0(@vercel/functions@3.4.3)(db0@0.3.4(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0)))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@6.0.3))
'@nuxt/kit': 4.4.2(magicast@0.5.2)
@@ -11173,24 +11428,24 @@ snapshots:
'@tailwindcss/postcss': 4.2.2
'@tailwindcss/vite': 4.2.2(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@tanstack/vue-table': 8.21.3(vue@3.5.32(typescript@6.0.3))
- '@tanstack/vue-virtual': 3.13.23(vue@3.5.32(typescript@6.0.3))
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/extension-bubble-menu': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-code': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-collaboration': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)
- '@tiptap/extension-drag-handle': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))
- '@tiptap/extension-drag-handle-vue-3': 3.22.2(@tiptap/extension-drag-handle@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)))(@tiptap/pm@3.22.2)(@tiptap/vue-3@3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
- '@tiptap/extension-floating-menu': 3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-horizontal-rule': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-image': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-mention': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/suggestion@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-node-range': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-placeholder': 3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/markdown': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
- '@tiptap/starter-kit': 3.22.2
- '@tiptap/suggestion': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/vue-3': 3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(vue@3.5.32(typescript@6.0.3))
+ '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@6.0.3))
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/extension-bubble-menu': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-code': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-collaboration': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)
+ '@tiptap/extension-drag-handle': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))
+ '@tiptap/extension-drag-handle-vue-3': 3.22.4(@tiptap/extension-drag-handle@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)))(@tiptap/pm@3.22.4)(@tiptap/vue-3@3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
+ '@tiptap/extension-floating-menu': 3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-horizontal-rule': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-image': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-mention': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/suggestion@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-node-range': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-placeholder': 3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/markdown': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
+ '@tiptap/starter-kit': 3.22.4
+ '@tiptap/suggestion': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/vue-3': 3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(vue@3.5.32(typescript@6.0.3))
'@unhead/vue': 2.1.13(vue@3.5.32(typescript@6.0.3))
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@6.0.3))
'@vueuse/integrations': 14.2.1(change-case@5.4.4)(fuse.js@7.3.0)(vue@3.5.32(typescript@6.0.3))
@@ -11206,27 +11461,29 @@ snapshots:
embla-carousel-vue: 8.6.0(vue@3.5.32(typescript@6.0.3))
embla-carousel-wheel-gestures: 8.1.0(embla-carousel@8.6.0)
fuse.js: 7.3.0
- hookable: 6.1.0
+ hookable: 6.1.1
knitwork: 1.3.0
magic-string: 0.30.21
mlly: 1.8.2
- motion-v: 2.2.0(@vueuse/core@14.2.1(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
+ motion-v: 2.2.1(@vueuse/core@14.2.1(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
ohash: 2.0.11
pathe: 2.0.3
- reka-ui: 2.9.3(vue@3.5.32(typescript@6.0.3))
+ reka-ui: 2.9.6(vue@3.5.32(typescript@6.0.3))
scule: 1.3.0
tailwind-merge: 3.5.0
tailwind-variants: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.2)
tailwindcss: 4.2.2
- tinyglobby: 0.2.15
+ tinyglobby: 0.2.16
typescript: 6.0.3
ufo: 1.6.3
unplugin: 3.0.0
unplugin-auto-import: 21.0.0(@nuxt/kit@4.4.2(magicast@0.5.2))(@vueuse/core@14.2.1(vue@3.5.32(typescript@6.0.3)))
unplugin-vue-components: 32.0.0(@nuxt/kit@4.4.2(magicast@0.5.2))(vue@3.5.32(typescript@6.0.3))
- vaul-vue: 0.4.1(reka-ui@2.9.3(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
- vue-component-type-helpers: 3.2.6
+ vaul-vue: 0.4.1(reka-ui@2.9.6(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))
+ vue-component-type-helpers: 3.2.7
optionalDependencies:
+ '@internationalized/date': 3.12.0
+ '@internationalized/number': 3.6.5
'@nuxt/content': 3.13.0(@libsql/client@0.17.2)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(magicast@0.5.2)(valibot@1.3.1(typescript@6.0.3))
valibot: 1.3.1(typescript@6.0.3)
zod: 4.3.6
@@ -11902,8 +12159,6 @@ snapshots:
dependencies:
quansync: 1.0.0
- '@remirror/core-constants@3.0.0': {}
-
'@remusao/guess-url-type@2.1.0': {}
'@remusao/small@2.1.0': {}
@@ -12427,16 +12682,16 @@ snapshots:
'@tanstack/table-core@8.21.3': {}
- '@tanstack/virtual-core@3.13.23': {}
+ '@tanstack/virtual-core@3.14.0': {}
'@tanstack/vue-table@8.21.3(vue@3.5.32(typescript@6.0.3))':
dependencies:
'@tanstack/table-core': 8.21.3
vue: 3.5.32(typescript@6.0.3)
- '@tanstack/vue-virtual@3.13.23(vue@3.5.32(typescript@6.0.3))':
+ '@tanstack/vue-virtual@3.13.24(vue@3.5.32(typescript@6.0.3))':
dependencies:
- '@tanstack/virtual-core': 3.13.23
+ '@tanstack/virtual-core': 3.14.0
vue: 3.5.32(typescript@6.0.3)
'@testing-library/dom@9.3.4':
@@ -12459,226 +12714,220 @@ snapshots:
optionalDependencies:
'@vue/compiler-sfc': 3.5.32
- '@tiptap/core@3.22.2(@tiptap/pm@3.22.2)':
+ '@tiptap/core@3.22.4(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/pm': 3.22.2
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-blockquote@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-blockquote@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-bold@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-bold@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-bubble-menu@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-bubble-menu@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
'@floating-ui/dom': 1.7.6
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-bullet-list@3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-bullet-list@3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extension-list': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extension-list': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-code-block@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-code-block@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-code@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-code@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)':
+ '@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
'@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)
yjs: 13.6.30
- '@tiptap/extension-document@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-document@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-drag-handle-vue-3@3.22.2(@tiptap/extension-drag-handle@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)))(@tiptap/pm@3.22.2)(@tiptap/vue-3@3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))':
+ '@tiptap/extension-drag-handle-vue-3@3.22.4(@tiptap/extension-drag-handle@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)))(@tiptap/pm@3.22.4)(@tiptap/vue-3@3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3))':
dependencies:
- '@tiptap/extension-drag-handle': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))
- '@tiptap/pm': 3.22.2
- '@tiptap/vue-3': 3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(vue@3.5.32(typescript@6.0.3))
+ '@tiptap/extension-drag-handle': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))
+ '@tiptap/pm': 3.22.4
+ '@tiptap/vue-3': 3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(vue@3.5.32(typescript@6.0.3))
vue: 3.5.32(typescript@6.0.3)
- '@tiptap/extension-drag-handle@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/extension-collaboration@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))':
+ '@tiptap/extension-drag-handle@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/extension-collaboration@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30))(@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))':
dependencies:
'@floating-ui/dom': 1.7.6
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/extension-collaboration': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)
- '@tiptap/extension-node-range': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/extension-collaboration': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)
+ '@tiptap/extension-node-range': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
'@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)
- '@tiptap/extension-dropcursor@3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-dropcursor@3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extensions': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extensions': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-floating-menu@3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-floating-menu@3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
'@floating-ui/dom': 1.7.6
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-gapcursor@3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-gapcursor@3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extensions': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extensions': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-hard-break@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-hard-break@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-heading@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-heading@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-horizontal-rule@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-horizontal-rule@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-image@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-image@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-italic@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-italic@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-link@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-link@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
linkifyjs: 4.3.2
- '@tiptap/extension-list-item@3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-list-item@3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extension-list': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extension-list': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-list-keymap@3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-list-keymap@3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extension-list': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extension-list': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-mention@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(@tiptap/suggestion@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-mention@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@tiptap/suggestion@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
- '@tiptap/suggestion': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
+ '@tiptap/suggestion': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-node-range@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extension-node-range@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/extension-ordered-list@3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-ordered-list@3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extension-list': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extension-list': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-paragraph@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-paragraph@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-placeholder@3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-placeholder@3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/extensions': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extensions': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
- '@tiptap/extension-strike@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-strike@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-text@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-text@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extension-underline@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))':
+ '@tiptap/extension-underline@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
- '@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
- '@tiptap/markdown@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
+ '@tiptap/markdown@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
marked: 17.0.6
- '@tiptap/pm@3.22.2':
+ '@tiptap/pm@3.22.4':
dependencies:
prosemirror-changeset: 2.4.0
- prosemirror-collab: 1.3.1
prosemirror-commands: 1.7.1
prosemirror-dropcursor: 1.8.2
prosemirror-gapcursor: 1.4.1
prosemirror-history: 1.5.0
- prosemirror-inputrules: 1.5.1
prosemirror-keymap: 1.2.3
- prosemirror-markdown: 1.13.4
- prosemirror-menu: 1.3.0
prosemirror-model: 1.25.4
- prosemirror-schema-basic: 1.2.4
prosemirror-schema-list: 1.5.1
prosemirror-state: 1.4.4
prosemirror-tables: 1.8.5
- prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
- '@tiptap/starter-kit@3.22.2':
- dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/extension-blockquote': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-bold': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-bullet-list': 3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-code': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-code-block': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-document': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-dropcursor': 3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-gapcursor': 3.22.2(@tiptap/extensions@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-hard-break': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-heading': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-horizontal-rule': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-italic': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-link': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-list': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-list-item': 3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-list-keymap': 3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-ordered-list': 3.22.2(@tiptap/extension-list@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2))
- '@tiptap/extension-paragraph': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-strike': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-text': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extension-underline': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))
- '@tiptap/extensions': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
-
- '@tiptap/suggestion@3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)':
- dependencies:
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
-
- '@tiptap/vue-3@3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)(vue@3.5.32(typescript@6.0.3))':
+ '@tiptap/starter-kit@3.22.4':
+ dependencies:
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/extension-blockquote': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-bold': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-bullet-list': 3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-code': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-code-block': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-document': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-dropcursor': 3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-gapcursor': 3.22.4(@tiptap/extensions@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-hard-break': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-heading': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-horizontal-rule': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-italic': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-link': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-list': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-list-item': 3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-list-keymap': 3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-ordered-list': 3.22.4(@tiptap/extension-list@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4))
+ '@tiptap/extension-paragraph': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-strike': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-text': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extension-underline': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))
+ '@tiptap/extensions': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
+
+ '@tiptap/suggestion@3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)':
+ dependencies:
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
+
+ '@tiptap/vue-3@3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(vue@3.5.32(typescript@6.0.3))':
dependencies:
'@floating-ui/dom': 1.7.6
- '@tiptap/core': 3.22.2(@tiptap/pm@3.22.2)
- '@tiptap/pm': 3.22.2
+ '@tiptap/core': 3.22.4(@tiptap/pm@3.22.4)
+ '@tiptap/pm': 3.22.4
vue: 3.5.32(typescript@6.0.3)
optionalDependencies:
- '@tiptap/extension-bubble-menu': 3.22.2(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
- '@tiptap/extension-floating-menu': 3.22.2(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.2(@tiptap/pm@3.22.2))(@tiptap/pm@3.22.2)
+ '@tiptap/extension-bubble-menu': 3.22.4(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
+ '@tiptap/extension-floating-menu': 3.22.4(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)
'@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)':
dependencies:
@@ -12924,7 +13173,7 @@ snapshots:
'@types/sax@1.2.7':
dependencies:
- '@types/node': 25.5.2
+ '@types/node': 24.12.2
'@types/semver@7.7.1': {}
@@ -13097,27 +13346,27 @@ snapshots:
unhead: 2.1.13
vue: 3.5.32(typescript@6.0.3)
- '@unocss/core@66.6.8': {}
+ '@unocss/core@66.6.7': {}
- '@unocss/extractor-arbitrary-variants@66.6.8':
+ '@unocss/extractor-arbitrary-variants@66.6.7':
dependencies:
- '@unocss/core': 66.6.8
+ '@unocss/core': 66.6.7
- '@unocss/preset-mini@66.6.8':
+ '@unocss/preset-mini@66.6.7':
dependencies:
- '@unocss/core': 66.6.8
- '@unocss/extractor-arbitrary-variants': 66.6.8
- '@unocss/rule-utils': 66.6.8
+ '@unocss/core': 66.6.7
+ '@unocss/extractor-arbitrary-variants': 66.6.7
+ '@unocss/rule-utils': 66.6.7
- '@unocss/preset-wind3@66.6.8':
+ '@unocss/preset-wind3@66.6.7':
dependencies:
- '@unocss/core': 66.6.8
- '@unocss/preset-mini': 66.6.8
- '@unocss/rule-utils': 66.6.8
+ '@unocss/core': 66.6.7
+ '@unocss/preset-mini': 66.6.7
+ '@unocss/rule-utils': 66.6.7
- '@unocss/rule-utils@66.6.8':
+ '@unocss/rule-utils@66.6.7':
dependencies:
- '@unocss/core': 66.6.8
+ '@unocss/core': 66.6.7
magic-string: 0.30.21
'@unovis/dagre-layout@0.8.8-2':
@@ -13296,6 +13545,8 @@ snapshots:
- rollup
- supports-color
+ '@vercel/oidc@3.1.0': {}
+
'@vercel/oidc@3.2.0': {}
'@vercel/speed-insights@2.0.0(nuxt@4.4.2(ec9ba8feacf9ea1d35ac88469523465a))(vue@3.5.32(typescript@6.0.3))':
@@ -13338,10 +13589,19 @@ snapshots:
optionalDependencies:
vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
+ '@vitest/pretty-format@4.1.3':
+ dependencies:
+ tinyrainbow: 3.1.0
+
'@vitest/pretty-format@4.1.4':
dependencies:
tinyrainbow: 3.1.0
+ '@vitest/runner@4.1.3':
+ dependencies:
+ '@vitest/utils': 4.1.3
+ pathe: 2.0.3
+
'@vitest/runner@4.1.4':
dependencies:
'@vitest/utils': 4.1.4
@@ -13356,6 +13616,12 @@ snapshots:
'@vitest/spy@4.1.4': {}
+ '@vitest/utils@4.1.3':
+ dependencies:
+ '@vitest/pretty-format': 4.1.3
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.1.0
+
'@vitest/utils@4.1.4':
dependencies:
'@vitest/pretty-format': 4.1.4
@@ -13588,6 +13854,14 @@ snapshots:
agent-base@7.1.4: {}
+ ai@6.0.154(zod@4.3.6):
+ dependencies:
+ '@ai-sdk/gateway': 3.0.94(zod@4.3.6)
+ '@ai-sdk/provider': 3.0.8
+ '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6)
+ '@opentelemetry/api': 1.9.0
+ zod: 4.3.6
+
ai@6.0.168(zod@4.3.6):
dependencies:
'@ai-sdk/gateway': 3.0.104(zod@4.3.6)
@@ -14012,6 +14286,19 @@ snapshots:
colortranslator@5.0.0: {}
+ comark@0.2.1(@types/markdown-it@14.1.2)(markdown-it@14.1.1)(shiki@4.0.2):
+ dependencies:
+ '@comark/markdown-it': 0.3.3(@types/markdown-it@14.1.2)(markdown-it@14.1.1)
+ entities: 8.0.0
+ htmlparser2: 12.0.0
+ js-yaml: 4.1.1
+ markdown-exit: 1.0.0-beta.9
+ optionalDependencies:
+ shiki: 4.0.2
+ transitivePeerDependencies:
+ - '@types/markdown-it'
+ - markdown-it
+
comma-separated-tokens@2.0.3: {}
commander@10.0.1: {}
@@ -14106,8 +14393,6 @@ snapshots:
crc-32: 1.2.2
readable-stream: 4.7.0
- crelt@1.0.6: {}
-
croner@10.0.1: {}
cross-fetch@4.1.0:
@@ -14524,18 +14809,36 @@ snapshots:
domhandler: 5.0.3
entities: 4.5.0
+ dom-serializer@3.0.0:
+ dependencies:
+ domelementtype: 3.0.0
+ domhandler: 6.0.1
+ entities: 8.0.0
+
domelementtype@2.3.0: {}
+ domelementtype@3.0.0: {}
+
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
+ domhandler@6.0.1:
+ dependencies:
+ domelementtype: 3.0.0
+
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
+ domutils@4.0.2:
+ dependencies:
+ dom-serializer: 3.0.0
+ domelementtype: 3.0.0
+ domhandler: 6.0.1
+
dot-prop@10.1.0:
dependencies:
type-fest: 5.5.0
@@ -14674,6 +14977,8 @@ snapshots:
entities@7.0.1: {}
+ entities@8.0.0: {}
+
env-paths@2.2.1: {}
error-ex@1.3.4:
@@ -15016,8 +15321,8 @@ snapshots:
'@fastify/websocket': 11.2.0
'@stricli/auto-complete': 1.2.6
'@stricli/core': 1.2.6
- '@vitest/runner': 4.1.4
- '@vitest/utils': 4.1.4
+ '@vitest/runner': 4.1.3
+ '@vitest/utils': 4.1.3
dotenv: 16.6.1
fastify: 5.8.4
file-type: 19.6.0
@@ -15049,6 +15354,18 @@ snapshots:
dependencies:
eventsource-parser: 3.0.6
+ evlog@2.12.0(@nuxt/kit@4.4.2(magicast@0.5.2))(ai@6.0.168(zod@4.3.6))(express@5.2.1)(fastify@5.8.4)(h3@1.15.11)(hono@4.12.12)(nitropack@2.13.3(@libsql/client@0.17.2)(@vercel/functions@3.4.3)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(rolldown@1.0.0-beta.57(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(srvx@0.11.15))(ofetch@1.5.1)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
+ optionalDependencies:
+ '@nuxt/kit': 4.4.2(magicast@0.5.2)
+ ai: 6.0.168(zod@4.3.6)
+ express: 5.2.1
+ fastify: 5.8.4
+ h3: 1.15.11
+ hono: 4.12.12
+ nitropack: 2.13.3(@libsql/client@0.17.2)(@vercel/functions@3.4.3)(better-sqlite3@12.9.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260405.1)(@libsql/client@0.17.2)(@opentelemetry/api@1.9.0)(better-sqlite3@12.9.0))(rolldown@1.0.0-beta.57(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(srvx@0.11.15)
+ ofetch: 1.5.1
+ vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
+
execa@8.0.1:
dependencies:
cross-spawn: 7.0.6
@@ -15730,9 +16047,11 @@ snapshots:
hookable@6.1.0: {}
+ hookable@6.1.1: {}
+
html-entities@2.6.0: {}
- html-validate@10.13.0(vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(happy-dom@20.9.0)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
+ html-validate@10.13.1(vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(happy-dom@20.9.0)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
dependencies:
'@html-validate/stylish': 5.2.0
'@sidvind/better-ajv-errors': 5.0.0(ajv@8.18.0)
@@ -15762,6 +16081,13 @@ snapshots:
html-whitespace-sensitive-tag-names@3.0.1: {}
+ htmlparser2@12.0.0:
+ dependencies:
+ domelementtype: 3.0.0
+ domhandler: 6.0.1
+ domutils: 4.0.2
+ entities: 8.0.0
+
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@@ -16396,6 +16722,16 @@ snapshots:
tinyqueue: 2.0.3
vt-pbf: 3.1.3
+ markdown-exit@1.0.0-beta.9:
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+ entities: 7.0.1
+ linkify-it: 5.0.0
+ mdurl: 2.0.0
+ punycode.js: 2.3.1
+ uc.micro: 2.1.0
+
markdown-it@14.1.1:
dependencies:
argparse: 2.0.1
@@ -16810,7 +17146,7 @@ snapshots:
- react
- react-dom
- motion-v@2.2.0(@vueuse/core@14.2.1(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3)):
+ motion-v@2.2.1(@vueuse/core@14.2.1(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3)):
dependencies:
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@6.0.3))
framer-motion: 12.38.0
@@ -17079,8 +17415,8 @@ snapshots:
'@resvg/resvg-js': 2.6.2
'@resvg/resvg-wasm': 2.6.2
'@unhead/vue': 2.1.13(vue@3.5.32(typescript@6.0.3))
- '@unocss/core': 66.6.8
- '@unocss/preset-wind3': 66.6.8
+ '@unocss/core': 66.6.7
+ '@unocss/preset-wind3': 66.6.7
chrome-launcher: 1.2.1
consola: 3.4.2
defu: 6.1.7
@@ -17902,10 +18238,6 @@ snapshots:
dependencies:
prosemirror-transform: 1.12.0
- prosemirror-collab@1.3.1:
- dependencies:
- prosemirror-state: 1.4.4
-
prosemirror-commands@1.7.1:
dependencies:
prosemirror-model: 1.25.4
@@ -17932,37 +18264,15 @@ snapshots:
prosemirror-view: 1.41.8
rope-sequence: 1.3.4
- prosemirror-inputrules@1.5.1:
- dependencies:
- prosemirror-state: 1.4.4
- prosemirror-transform: 1.12.0
-
prosemirror-keymap@1.2.3:
dependencies:
prosemirror-state: 1.4.4
w3c-keyname: 2.2.8
- prosemirror-markdown@1.13.4:
- dependencies:
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.1
- prosemirror-model: 1.25.4
-
- prosemirror-menu@1.3.0:
- dependencies:
- crelt: 1.0.6
- prosemirror-commands: 1.7.1
- prosemirror-history: 1.5.0
- prosemirror-state: 1.4.4
-
prosemirror-model@1.25.4:
dependencies:
orderedmap: 2.1.1
- prosemirror-schema-basic@1.2.4:
- dependencies:
- prosemirror-model: 1.25.4
-
prosemirror-schema-list@1.5.1:
dependencies:
prosemirror-model: 1.25.4
@@ -17983,14 +18293,6 @@ snapshots:
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
- prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8):
- dependencies:
- '@remirror/core-constants': 3.0.0
- escape-string-regexp: 4.0.0
- prosemirror-model: 1.25.4
- prosemirror-state: 1.4.4
- prosemirror-view: 1.41.8
-
prosemirror-transform@1.12.0:
dependencies:
prosemirror-model: 1.25.4
@@ -18230,13 +18532,13 @@ snapshots:
'@types/hast': 3.0.4
unist-util-visit: 5.1.0
- reka-ui@2.9.3(vue@3.5.32(typescript@6.0.3)):
+ reka-ui@2.9.6(vue@3.5.32(typescript@6.0.3)):
dependencies:
'@floating-ui/dom': 1.7.6
'@floating-ui/vue': 1.1.11(vue@3.5.32(typescript@6.0.3))
'@internationalized/date': 3.12.0
'@internationalized/number': 3.6.5
- '@tanstack/vue-virtual': 3.13.23(vue@3.5.32(typescript@6.0.3))
+ '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@6.0.3))
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@6.0.3))
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@6.0.3))
aria-hidden: 1.2.6
@@ -18583,6 +18885,12 @@ snapshots:
safe-buffer: 5.2.1
to-buffer: 1.2.2
+ shaders@2.5.93(vue@3.5.32(typescript@6.0.3)):
+ dependencies:
+ three: 0.183.2
+ optionalDependencies:
+ vue: 3.5.32(typescript@6.0.3)
+
sharp@0.34.5:
dependencies:
'@img/colour': 1.1.0
@@ -18915,6 +19223,10 @@ snapshots:
standardwebhooks: 1.0.0
uuid: 10.0.0
+ swrv@1.2.0(vue@3.5.32(typescript@6.0.3)):
+ dependencies:
+ vue: 3.5.32(typescript@6.0.3)
+
table@6.9.0:
dependencies:
ajv: 8.18.0
@@ -19009,6 +19321,8 @@ snapshots:
three@0.135.0: {}
+ three@0.183.2: {}
+
throttle-debounce@5.0.2: {}
tiny-inflate@1.0.3: {}
@@ -19026,6 +19340,11 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tinyglobby@0.2.16:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
tinyqueue@2.0.3: {}
tinyrainbow@3.1.0: {}
@@ -19095,7 +19414,7 @@ snapshots:
cac: 6.7.14
defu: 6.1.7
empathic: 2.0.0
- hookable: 6.1.0
+ hookable: 6.0.1
import-without-cache: 0.2.5
obug: 2.1.1
picomatch: 4.0.4
@@ -19342,7 +19661,7 @@ snapshots:
mlly: 1.8.2
obug: 2.1.1
picomatch: 4.0.4
- tinyglobby: 0.2.15
+ tinyglobby: 0.2.16
unplugin: 3.0.0
unplugin-utils: 0.3.1
vue: 3.5.32(typescript@6.0.3)
@@ -19458,10 +19777,10 @@ snapshots:
vary@1.1.2: {}
- vaul-vue@0.4.1(reka-ui@2.9.3(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3)):
+ vaul-vue@0.4.1(reka-ui@2.9.6(vue@3.5.32(typescript@6.0.3)))(vue@3.5.32(typescript@6.0.3)):
dependencies:
'@vueuse/core': 10.11.1(vue@3.5.32(typescript@6.0.3))
- reka-ui: 2.9.3(vue@3.5.32(typescript@6.0.3))
+ reka-ui: 2.9.6(vue@3.5.32(typescript@6.0.3))
vue: 3.5.32(typescript@6.0.3)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -19653,7 +19972,7 @@ snapshots:
vue-component-type-helpers@2.2.12: {}
- vue-component-type-helpers@3.2.6: {}
+ vue-component-type-helpers@3.2.7: {}
vue-demi@0.14.10(vue@3.5.32(typescript@6.0.3)):
dependencies:
diff --git a/public/nuxt-chat-og.jpg b/public/nuxt-chat-og.jpg
new file mode 100644
index 000000000..a43955c47
Binary files /dev/null and b/public/nuxt-chat-og.jpg differ
diff --git a/server/api/agent.post.ts b/server/api/agent.post.ts
new file mode 100644
index 000000000..3beb6ebd8
--- /dev/null
+++ b/server/api/agent.post.ts
@@ -0,0 +1,223 @@
+import { streamText, convertToModelMessages, createUIMessageStream, createUIMessageStreamResponse, safeValidateUIMessages } from 'ai'
+import type { ToolSet, UIMessage } from 'ai'
+import { createMCPClient } from '@ai-sdk/mcp'
+import { anthropic } from '@ai-sdk/anthropic'
+import { createAILogger, createEvlogIntegration } from 'evlog/ai'
+import type { AILogger } from 'evlog/ai'
+import { sql } from 'drizzle-orm'
+import { getAgentFingerprint } from '../utils/agent-fingerprint'
+import { showModuleTool } from '../utils/tools/show-module'
+import { createShowTemplateTool } from '../utils/tools/show-template'
+import { createShowBlogPostTool } from '../utils/tools/show-blog-post'
+import { createShowHostingTool } from '../utils/tools/show-hosting'
+import { openPlaygroundTool } from '../utils/tools/open-playground'
+import { createSearchGitHubIssuesTool } from '../utils/tools/search-github-issues'
+import { reportIssueTool } from '../utils/tools/report-issue'
+
+const MCP_PATH = '/mcp'
+const MODEL = 'anthropic/claude-sonnet-4.6'
+const MAX_STEPS = 10
+
+function stopWhenResponseComplete({ steps }: { steps: { text?: string, toolCalls?: unknown[] }[] }): boolean {
+ const lastStep = steps.at(-1)
+ if (!lastStep) return false
+
+ const hasText = Boolean(lastStep.text && lastStep.text.trim().length > 0)
+ const hasNoToolCalls = !lastStep.toolCalls || lastStep.toolCalls.length === 0
+
+ if (hasText && hasNoToolCalls) return true
+
+ return steps.length >= MAX_STEPS
+}
+
+const baseSystemPrompt = `You are **the Nuxt Agent**, Nuxt's documentation agent on nuxt.com. You help users navigate the official documentation, blog, modules catalog, and guides.
+
+**Identity:** You are the Nuxt Agent — not a generic chatbot. Be confident, precise, and grounded in retrieved content. Avoid casual first person ("I think…"). Attribute capabilities to Nuxt, not to yourself.
+
+**Current page context:** When the request includes a "Current page" line at the top of this prompt, that's the page the user has open in the browser. Treat it as a strong hint about what they're asking about, especially for vague questions like "explain this", "summarize", "tldr", "what does this do?". Map the path to the right tool:
+- \`/docs/…\` → \`get-documentation-page\` with that exact path
+- \`/blog/…\` → \`get-blog-post\` with that exact path
+- \`/deploy/…\` → \`get-deploy-provider\` with that exact path
+- \`/modules/\` → \`show_module\` with that slug
+- \`/changelog/…\` → use the GitHub changelog tools
+Do NOT call \`list-*\` first when the page is given — call the get tool directly. If the question is unrelated to the current page, ignore it and answer normally.
+
+**Modules:** Never invent npm package names. Use \`show_module\` to display modules (it includes all needed info — do NOT also call \`get-module\` for the same module). NuxtHub's module is \`@nuxthub/core\`, not \`@nuxt/hub\`.
+
+**TOKEN EFFICIENCY (CRITICAL — follow strictly):**
+- For \`get-documentation-page\`: pass the \`sections\` parameter with the relevant h2 titles when you only need part of a long page. Omit it when the user wants an overview/tldr/summary of the whole page.
+- For \`get-blog-post\` and \`get-deploy-provider\`: do NOT use sections — these pages are short, fetch them once in full.
+- **Never call the same tool twice with the same path** in a single turn. If the first call returned content, work with it — do not refetch.
+- If you already know the doc path, call \`get-documentation-page\` directly — skip \`list-documentation-pages\`.
+- Prefer \`show_module\` over \`get-module\` (smaller response, richer UI).
+
+**Debugging / error questions:**
+- When the user shares an error message or stack trace, use \`search_github_issues\` first — it searches across nuxt, nuxt-modules, and nuxt-content orgs.
+- If a matching closed issue exists, link to it and summarize the fix/workaround.
+- If open, link to the issue and mention any workarounds from the body.
+- Only fall back to \`web_search\` if no relevant GitHub Issue is found.
+
+**Tools:**
+- \`list-documentation-pages\` — discover pages by topic (use before \`get-documentation-page\` if path unknown)
+- \`get-documentation-page\` — read a doc page. Pass \`sections\` with the relevant h2 titles for partial reads; omit for full-page overviews.
+- \`get-blog-post\` — read a blog post (full content, no sections).
+- \`get-deploy-provider\` — read a deploy provider page (full content, no sections).
+- \`search_github_issues\` — search GitHub Issues across the Nuxt ecosystem. Use for errors, bugs, and debugging questions.
+- \`show_module\` — display a module card (preferred for module questions)
+- \`show_template\` — display template cards (accepts array of slugs). For vague requests, show official templates first: nuxt-ui-dashboard, nuxt-ui-saas, nuxt-ui-landing, nuxt-ui-chat, nuxt-ui-docs, nuxt-ui-portfolio
+- \`show_blog_post\` — display a blog post card
+- \`show_hosting\` — display a hosting provider card
+- \`open_playground\` — generate a StackBlitz link
+- \`report_issue\` — call when you cannot resolve the user's question after exhausting all available tools, or when the user expresses frustration. Provide a short title and 1-3 sentence summary of what was tried and why it failed
+- ALWAYS respond with text after tool calls — never end with just tool calls
+
+**Web search:** Only use when the user **explicitly** asks about recent events or real-time data beyond the Nuxt docs, or if \`search_github_issues\` returned no results. Never search proactively.
+
+**Formatting:**
+- NEVER use markdown headings (#, ##, ###)
+- Use **bold** for emphasis, bullet points for lists
+- Use markdown links from tool result URLs
+- Be concise and direct — actionable guidance, not information dumps`
+
+const PAGE_PATH_PATTERN = /^\/[a-zA-Z0-9._\-/]*$/
+
+function buildSystemPrompt(pagePath: string | null): string {
+ if (!pagePath) return baseSystemPrompt
+ return `Current page: ${pagePath}\n\n${baseSystemPrompt}`
+}
+
+function computeEstimatedCost(state: AILogger['_state']): number {
+ if (!state.costMap) return 0
+ const model = state.models.at(-1)
+ if (!model) return 0
+ const cost = state.costMap[model]
+ if (!cost) return 0
+ return (state.usage.inputTokens * cost.input + state.usage.outputTokens * cost.output) / 1_000_000
+}
+
+export default defineEventHandler(async (event) => {
+ const raw = await readBody(event) as { messages?: unknown } | null
+ if (!raw || !Array.isArray(raw.messages)) {
+ throw createError({ statusCode: 400, statusMessage: 'Invalid request body' })
+ }
+
+ const validated = await safeValidateUIMessages({ messages: raw.messages })
+ if (validated.success === false) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: validated.error.message || 'Invalid messages'
+ })
+ }
+
+ const messages = validated.data
+
+ await consumeAgentRateLimit(event)
+ const chatId = getHeader(event, 'x-chat-id')
+ const rawPagePath = getHeader(event, 'x-page-path')?.trim() ?? null
+ const pagePath = rawPagePath && PAGE_PATH_PATTERN.test(rawPagePath) && rawPagePath.length <= 256
+ ? rawPagePath
+ : null
+ const log = useLogger(event)
+ const ai = createAILogger(log, {
+ toolInputs: true,
+ cost: { 'claude-sonnet-4-6': { input: 3, output: 15 } }
+ })
+
+ const abortController = new AbortController()
+ event.node.req.on('close', () => abortController.abort())
+
+ const mcpUrl = import.meta.dev
+ ? `http://localhost:3000${MCP_PATH}`
+ : `${getRequestURL(event).origin}${MCP_PATH}`
+
+ const httpClient = await createMCPClient({
+ transport: { type: 'http', url: mcpUrl }
+ })
+ const mcpTools = await httpClient.tools()
+
+ const closeMcp = () => event.waitUntil(httpClient.close())
+
+ const saveChat = async (finalizedMessages: UIMessage[]) => {
+ if (!chatId) return
+ const fingerprint = await getAgentFingerprint(event)
+ const now = new Date()
+ const state = ai._state
+ const model = state.models.at(-1) ?? null
+ const provider = state.lastProvider ?? null
+ const { inputTokens, outputTokens } = state.usage
+ const estimatedCost = computeEstimatedCost(state)
+ const durationMs = state.totalDurationMs ?? 0
+
+ await db.insert(schema.agentChats).values({
+ id: chatId,
+ messages: finalizedMessages,
+ fingerprint,
+ model,
+ provider,
+ inputTokens,
+ outputTokens,
+ estimatedCost,
+ durationMs,
+ requestCount: 1,
+ createdAt: now,
+ updatedAt: now
+ }).onConflictDoUpdate({
+ target: schema.agentChats.id,
+ set: {
+ messages: finalizedMessages,
+ updatedAt: now,
+ model,
+ provider,
+ inputTokens: sql`${schema.agentChats.inputTokens} + ${inputTokens}`,
+ outputTokens: sql`${schema.agentChats.outputTokens} + ${outputTokens}`,
+ estimatedCost: sql`${schema.agentChats.estimatedCost} + ${estimatedCost}`,
+ durationMs: sql`${schema.agentChats.durationMs} + ${durationMs}`,
+ requestCount: sql`${schema.agentChats.requestCount} + 1`
+ }
+ })
+ }
+
+ const stream = createUIMessageStream({
+ execute: async ({ writer }) => {
+ const result = streamText({
+ model: ai.wrap(MODEL),
+ maxOutputTokens: 4000,
+ maxRetries: 2,
+ abortSignal: abortController.signal,
+ stopWhen: stopWhenResponseComplete,
+ system: buildSystemPrompt(pagePath),
+ messages: await convertToModelMessages(messages),
+ tools: {
+ ...mcpTools as ToolSet,
+ web_search: anthropic.tools.webSearch_20250305(),
+ search_github_issues: createSearchGitHubIssuesTool(event),
+ show_module: showModuleTool,
+ show_template: createShowTemplateTool(event),
+ show_blog_post: createShowBlogPostTool(event),
+ show_hosting: createShowHostingTool(event),
+ open_playground: openPlaygroundTool,
+ report_issue: reportIssueTool
+ },
+ experimental_telemetry: {
+ isEnabled: true,
+ integrations: [createEvlogIntegration(ai)]
+ },
+ onFinish: () => {
+ closeMcp()
+ },
+ onAbort: closeMcp,
+ onError: closeMcp
+ })
+
+ writer.merge(result.toUIMessageStream({
+ sendSources: true,
+ originalMessages: messages,
+ onFinish: ({ messages: finalizedMessages }) => {
+ event.waitUntil(saveChat(finalizedMessages))
+ }
+ }))
+ }
+ })
+
+ return createUIMessageStreamResponse({ stream })
+})
diff --git a/server/api/agent/cleanup.get.ts b/server/api/agent/cleanup.get.ts
new file mode 100644
index 000000000..d4f1848b3
--- /dev/null
+++ b/server/api/agent/cleanup.get.ts
@@ -0,0 +1,21 @@
+import { lt } from 'drizzle-orm'
+
+const RETENTION_DAYS = 30
+
+export default defineEventHandler(async (event) => {
+ const secret = useRuntimeConfig(event).cronSecret
+ const authHeader = getHeader(event, 'authorization')
+
+ if (!secret || authHeader !== `Bearer ${secret}`) {
+ throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
+ }
+
+ const threshold = new Date()
+ threshold.setDate(threshold.getDate() - RETENTION_DAYS)
+
+ const deleted = await db.delete(schema.agentChats)
+ .where(lt(schema.agentChats.updatedAt, threshold))
+ .returning({ id: schema.agentChats.id })
+
+ return { deleted: deleted.length, threshold: threshold.toISOString() }
+})
diff --git a/server/api/agent/feedback.post.ts b/server/api/agent/feedback.post.ts
new file mode 100644
index 000000000..9b18600a0
--- /dev/null
+++ b/server/api/agent/feedback.post.ts
@@ -0,0 +1,132 @@
+import { z } from 'zod'
+import { eq } from 'drizzle-orm'
+import { getAgentFingerprint } from '../../utils/agent-fingerprint'
+
+const LINEAR_API = 'https://api.linear.app/graphql'
+const LINEAR_TEAM_ID = 'f79ad145-d4eb-4bff-88b9-c344f006a777'
+const LINEAR_PROJECT_ID = '11a6000e-6c95-445e-85f1-a7de5c372bcd'
+const MAX_TRANSCRIPT_CHARS = 3000
+
+const bodySchema = z.object({
+ chatId: z.string().min(1),
+ title: z.string().min(1).max(80),
+ summary: z.string().min(1),
+ userFeedback: z.string().max(2000).optional()
+})
+
+type MessagePart = { type: string, text?: string }
+type StoredMessage = { id: string, role: string, parts: MessagePart[] }
+
+function extractText(parts: MessagePart[]): string {
+ return parts
+ .filter(p => p.type === 'text' && p.text)
+ .map(p => p.text!.trim())
+ .join(' ')
+}
+
+function buildTranscript(messages: StoredMessage[]): string {
+ const lines: string[] = []
+ for (const msg of messages) {
+ const role = msg.role === 'user' ? 'User' : 'Agent'
+ const text = extractText(msg.parts)
+ if (text) lines.push(`**${role}:** ${text}`)
+ }
+ const full = lines.join('\n\n')
+ if (full.length <= MAX_TRANSCRIPT_CHARS) return full
+ return full.slice(0, MAX_TRANSCRIPT_CHARS) + '\n\n_…conversation truncated_'
+}
+
+function buildIssueBody(params: {
+ summary: string
+ userFeedback?: string
+ transcript: string
+ chatId: string
+ createdAt: Date
+}): string {
+ const sections: string[] = []
+
+ if (params.userFeedback?.trim()) {
+ sections.push(`## User feedback\n\n${params.userFeedback.trim()}`)
+ }
+
+ sections.push(`## Summary\n\n${params.summary}`)
+
+ const meta = [
+ `**Chat ID:** \`${params.chatId}\``,
+ `**Date:** ${params.createdAt.toISOString()}`
+ ].join('\n')
+
+ sections.push(`## Metadata\n\n${meta}`)
+
+ if (params.transcript) {
+ sections.push(`## Conversation transcript\n\n${params.transcript}`)
+ }
+
+ return sections.join('\n\n---\n\n')
+}
+
+export default defineEventHandler(async (event) => {
+ const { chatId, title, summary, userFeedback } = await readValidatedBody(event, bodySchema.parse)
+
+ const fingerprint = await getAgentFingerprint(event)
+
+ const [chat] = await db
+ .select({
+ id: schema.agentChats.id,
+ fingerprint: schema.agentChats.fingerprint,
+ messages: schema.agentChats.messages,
+ createdAt: schema.agentChats.createdAt
+ })
+ .from(schema.agentChats)
+ .where(eq(schema.agentChats.id, chatId))
+ .limit(1)
+
+ if (!chat || chat.fingerprint !== fingerprint) {
+ throw createError({ statusCode: 403, statusMessage: 'Forbidden' })
+ }
+
+ const apiKey = useRuntimeConfig(event).linear.apiKey
+ if (!apiKey) {
+ throw createError({ statusCode: 503, statusMessage: 'Linear integration not configured' })
+ }
+
+ const transcript = buildTranscript(chat.messages as StoredMessage[])
+ const description = buildIssueBody({ summary, userFeedback, transcript, chatId, createdAt: chat.createdAt })
+
+ const mutation = `
+ mutation IssueCreate($input: IssueCreateInput!) {
+ issueCreate(input: $input) {
+ success
+ issue { id url }
+ }
+ }
+ `
+
+ const response = await $fetch<{
+ data: { issueCreate: { success: boolean, issue: { id: string, url: string } } }
+ }>(LINEAR_API, {
+ method: 'POST',
+ headers: {
+ 'Authorization': apiKey,
+ 'Content-Type': 'application/json'
+ },
+ body: {
+ query: mutation,
+ variables: {
+ input: {
+ teamId: LINEAR_TEAM_ID,
+ projectId: LINEAR_PROJECT_ID,
+ title: `[Agent] ${title}`,
+ description
+ }
+ }
+ }
+ })
+
+ const issue = response.data?.issueCreate?.issue
+ if (!issue?.url) {
+ throw createError({ statusCode: 500, statusMessage: 'Failed to create Linear issue' })
+ }
+
+ return { url: issue.url }
+})
diff --git a/server/api/agent/usage.get.ts b/server/api/agent/usage.get.ts
new file mode 100644
index 000000000..b3fff191f
--- /dev/null
+++ b/server/api/agent/usage.get.ts
@@ -0,0 +1,3 @@
+export default defineEventHandler(async (event) => {
+ return checkAgentRateLimit(event)
+})
diff --git a/server/api/agent/vote.post.ts b/server/api/agent/vote.post.ts
new file mode 100644
index 000000000..5f918cceb
--- /dev/null
+++ b/server/api/agent/vote.post.ts
@@ -0,0 +1,44 @@
+import { z } from 'zod'
+import { and, eq } from 'drizzle-orm'
+import { getAgentFingerprint } from '../../utils/agent-fingerprint'
+
+const voteSchema = z.object({
+ chatId: z.string().min(1),
+ messageId: z.string().min(1),
+ isUpvoted: z.boolean().optional()
+})
+
+export default defineEventHandler(async (event) => {
+ const { chatId, messageId, isUpvoted } = await readValidatedBody(event, voteSchema.parse)
+
+ const fingerprint = await getAgentFingerprint(event)
+ const [chat] = await db.select({ id: schema.agentChats.id, fingerprint: schema.agentChats.fingerprint })
+ .from(schema.agentChats)
+ .where(eq(schema.agentChats.id, chatId))
+ .limit(1)
+
+ if (!chat || chat.fingerprint !== fingerprint) {
+ throw createError({ statusCode: 403, statusMessage: 'Forbidden' })
+ }
+
+ if (isUpvoted === undefined) {
+ await db.delete(schema.agentVotes).where(
+ and(
+ eq(schema.agentVotes.chatId, chatId),
+ eq(schema.agentVotes.messageId, messageId)
+ )
+ )
+ } else {
+ await db.insert(schema.agentVotes).values({
+ chatId,
+ messageId,
+ isUpvoted,
+ createdAt: new Date()
+ }).onConflictDoUpdate({
+ target: [schema.agentVotes.chatId, schema.agentVotes.messageId],
+ set: { isUpvoted }
+ })
+ }
+
+ return { chatId, messageId, isUpvoted }
+})
diff --git a/server/db/migrations/sqlite/0001_great_spot.sql b/server/db/migrations/sqlite/0001_great_spot.sql
new file mode 100644
index 000000000..cb16e6af1
--- /dev/null
+++ b/server/db/migrations/sqlite/0001_great_spot.sql
@@ -0,0 +1,32 @@
+CREATE TABLE `agent_chats` (
+ `id` text PRIMARY KEY NOT NULL,
+ `messages` text NOT NULL,
+ `fingerprint` text NOT NULL,
+ `model` text,
+ `provider` text,
+ `input_tokens` integer DEFAULT 0 NOT NULL,
+ `output_tokens` integer DEFAULT 0 NOT NULL,
+ `estimated_cost` real DEFAULT 0 NOT NULL,
+ `duration_ms` integer DEFAULT 0 NOT NULL,
+ `request_count` integer DEFAULT 0 NOT NULL,
+ `createdAt` integer NOT NULL,
+ `updatedAt` integer NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE `agent_votes` (
+ `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `chat_id` text NOT NULL,
+ `message_id` text NOT NULL,
+ `is_upvoted` integer NOT NULL,
+ `createdAt` integer NOT NULL,
+ FOREIGN KEY (`chat_id`) REFERENCES `agent_chats`(`id`) ON DELETE CASCADE
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `agent_vote_chat_msg_idx` ON `agent_votes` (`chat_id`,`message_id`);
+--> statement-breakpoint
+CREATE INDEX `agent_chats_fingerprint_idx` ON `agent_chats` (`fingerprint`);
+--> statement-breakpoint
+CREATE TABLE `agent_daily_usage` (
+ `day_key` text PRIMARY KEY NOT NULL,
+ `count` integer NOT NULL
+);
\ No newline at end of file
diff --git a/server/db/migrations/sqlite/0002_agent_daily_usage.sql b/server/db/migrations/sqlite/0002_agent_daily_usage.sql
new file mode 100644
index 000000000..0edbcede5
--- /dev/null
+++ b/server/db/migrations/sqlite/0002_agent_daily_usage.sql
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `agent_daily_usage` (
+ `day_key` text PRIMARY KEY NOT NULL,
+ `count` integer NOT NULL
+);
+--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS `agent_chats_fingerprint_idx` ON `agent_chats` (`fingerprint`);
\ No newline at end of file
diff --git a/server/db/migrations/sqlite/meta/0001_snapshot.json b/server/db/migrations/sqlite/meta/0001_snapshot.json
new file mode 100644
index 000000000..10a3db0b6
--- /dev/null
+++ b/server/db/migrations/sqlite/meta/0001_snapshot.json
@@ -0,0 +1,306 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "31933b0a-9c21-4fa2-9861-99db0e8ffcd1",
+ "prevId": "18f499b5-b447-4ec4-b175-6e8cce5b06ea",
+ "tables": {
+ "agent_chats": {
+ "name": "agent_chats",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "messages": {
+ "name": "messages",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fingerprint": {
+ "name": "fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "output_tokens": {
+ "name": "output_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "estimated_cost": {
+ "name": "estimated_cost",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "agent_chats_fingerprint_idx": {
+ "name": "agent_chats_fingerprint_idx",
+ "columns": [
+ "fingerprint"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "agent_daily_usage": {
+ "name": "agent_daily_usage",
+ "columns": {
+ "day_key": {
+ "name": "day_key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "agent_votes": {
+ "name": "agent_votes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "chat_id": {
+ "name": "chat_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_upvoted": {
+ "name": "is_upvoted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "agent_vote_chat_msg_idx": {
+ "name": "agent_vote_chat_msg_idx",
+ "columns": [
+ "chat_id",
+ "message_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "agent_votes_chat_id_agent_chats_id_fk": {
+ "name": "agent_votes_chat_id_agent_chats_id_fk",
+ "tableFrom": "agent_votes",
+ "tableTo": "agent_chats",
+ "columnsFrom": [
+ "chat_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "feedback": {
+ "name": "feedback",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "rating": {
+ "name": "rating",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "feedback": {
+ "name": "feedback",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "stem": {
+ "name": "stem",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fingerprint": {
+ "name": "fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "path_fingerprint_idx": {
+ "name": "path_fingerprint_idx",
+ "columns": [
+ "path",
+ "fingerprint"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/server/db/migrations/sqlite/meta/0002_snapshot.json b/server/db/migrations/sqlite/meta/0002_snapshot.json
new file mode 100644
index 000000000..f50941795
--- /dev/null
+++ b/server/db/migrations/sqlite/meta/0002_snapshot.json
@@ -0,0 +1,306 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "a8264235-7bd0-49b9-afc8-3b2dbbcac5ec",
+ "prevId": "31933b0a-9c21-4fa2-9861-99db0e8ffcd1",
+ "tables": {
+ "agent_chats": {
+ "name": "agent_chats",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "messages": {
+ "name": "messages",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fingerprint": {
+ "name": "fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "output_tokens": {
+ "name": "output_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "estimated_cost": {
+ "name": "estimated_cost",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 0
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "agent_chats_fingerprint_idx": {
+ "name": "agent_chats_fingerprint_idx",
+ "columns": [
+ "fingerprint"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "agent_daily_usage": {
+ "name": "agent_daily_usage",
+ "columns": {
+ "day_key": {
+ "name": "day_key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "agent_votes": {
+ "name": "agent_votes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "chat_id": {
+ "name": "chat_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_upvoted": {
+ "name": "is_upvoted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "agent_vote_chat_msg_idx": {
+ "name": "agent_vote_chat_msg_idx",
+ "columns": [
+ "chat_id",
+ "message_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "agent_votes_chat_id_agent_chats_id_fk": {
+ "name": "agent_votes_chat_id_agent_chats_id_fk",
+ "tableFrom": "agent_votes",
+ "tableTo": "agent_chats",
+ "columnsFrom": [
+ "chat_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "feedback": {
+ "name": "feedback",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "rating": {
+ "name": "rating",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "feedback": {
+ "name": "feedback",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "stem": {
+ "name": "stem",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fingerprint": {
+ "name": "fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "path_fingerprint_idx": {
+ "name": "path_fingerprint_idx",
+ "columns": [
+ "path",
+ "fingerprint"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/server/db/migrations/sqlite/meta/_journal.json b/server/db/migrations/sqlite/meta/_journal.json
index 68c4ff89b..baa9e05e6 100644
--- a/server/db/migrations/sqlite/meta/_journal.json
+++ b/server/db/migrations/sqlite/meta/_journal.json
@@ -8,6 +8,20 @@
"when": 1762358790381,
"tag": "0000_sudden_christian_walker",
"breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "6",
+ "when": 1776264773812,
+ "tag": "0001_great_spot",
+ "breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "6",
+ "when": 1776600000000,
+ "tag": "0002_agent_daily_usage",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/server/db/schema.ts b/server/db/schema.ts
index 375dd0f7a..973f13343 100644
--- a/server/db/schema.ts
+++ b/server/db/schema.ts
@@ -1,4 +1,4 @@
-import { sqliteTable, text, integer, uniqueIndex } from 'drizzle-orm/sqlite-core'
+import { sqliteTable, text, integer, real, uniqueIndex, index } from 'drizzle-orm/sqlite-core'
export const feedback = sqliteTable('feedback', {
id: integer('id').primaryKey({ autoIncrement: true }),
@@ -12,3 +12,31 @@ export const feedback = sqliteTable('feedback', {
createdAt: integer({ mode: 'timestamp' }).notNull(),
updatedAt: integer({ mode: 'timestamp' }).notNull()
}, table => [uniqueIndex('path_fingerprint_idx').on(table.path, table.fingerprint)])
+
+export const agentChats = sqliteTable('agent_chats', {
+ id: text('id').primaryKey(),
+ messages: text('messages', { mode: 'json' }).notNull().$type<{ id: string, role: string, parts: unknown[] }[]>(),
+ fingerprint: text('fingerprint').notNull(),
+ model: text('model'),
+ provider: text('provider'),
+ inputTokens: integer('input_tokens').notNull().default(0),
+ outputTokens: integer('output_tokens').notNull().default(0),
+ estimatedCost: real('estimated_cost').notNull().default(0),
+ durationMs: integer('duration_ms').notNull().default(0),
+ requestCount: integer('request_count').notNull().default(0),
+ createdAt: integer({ mode: 'timestamp' }).notNull(),
+ updatedAt: integer({ mode: 'timestamp' }).notNull()
+}, table => [index('agent_chats_fingerprint_idx').on(table.fingerprint)])
+
+export const agentDailyUsage = sqliteTable('agent_daily_usage', {
+ dayKey: text('day_key').primaryKey(),
+ count: integer('count').notNull()
+})
+
+export const agentVotes = sqliteTable('agent_votes', {
+ id: integer('id').primaryKey({ autoIncrement: true }),
+ chatId: text('chat_id').notNull().references(() => agentChats.id, { onDelete: 'cascade' }),
+ messageId: text('message_id').notNull(),
+ isUpvoted: integer('is_upvoted', { mode: 'boolean' }).notNull(),
+ createdAt: integer({ mode: 'timestamp' }).notNull()
+}, table => [uniqueIndex('agent_vote_chat_msg_idx').on(table.chatId, table.messageId)])
diff --git a/server/mcp/prompts/deployment-guide.ts b/server/mcp/prompts/deployment-guide.ts
index cc80ea96f..14d5b8f52 100644
--- a/server/mcp/prompts/deployment-guide.ts
+++ b/server/mcp/prompts/deployment-guide.ts
@@ -24,14 +24,9 @@ export default defineMcpPrompt({
p.title.toLowerCase().includes(provider.toLowerCase())
)
- let providerContent: string | null = null
- if (matchingProvider) {
- try {
- providerContent = await $fetch(`/raw${matchingProvider.path}.md`)
- } catch {
- providerContent = null
- }
- }
+ const providerContent = matchingProvider
+ ? await fetchPageMarkdown(event, 'deploy', matchingProvider.path)
+ : null
return {
messages: [
diff --git a/server/mcp/tools/get-blog-post.ts b/server/mcp/tools/get-blog-post.ts
index 9ea47e529..46930fabb 100644
--- a/server/mcp/tools/get-blog-post.ts
+++ b/server/mcp/tools/get-blog-post.ts
@@ -1,35 +1,30 @@
import { z } from 'zod'
export default defineMcpTool({
- description: `Retrieves the full content and details of a specific Nuxt blog post.
+ description: `Retrieves the full content of a specific Nuxt blog post.
WHEN TO USE: Use this tool when you know the EXACT path to a blog post. Common scenarios:
- User asks for a specific post: "Get the blog post about Nuxt 4" → /blog/v4
- You found a relevant post from list_blog_posts and want the full content
-- You know the post slug from context
+- The current page context is a /blog/... path
-WHEN NOT TO USE: If you don't know the exact path and need to search/discover, use list_blog_posts first.
+WHEN NOT TO USE: If you don't know the exact path and need to search/discover, use list_blog_posts first. Never call this tool twice for the same path — one call returns the full post.
EXAMPLES: "/blog/v4", "/blog/nuxt3", "/blog/nuxt-on-the-edge"`,
inputSchema: {
- path: z.string().describe('The path to the blog post (e.g., /blog/v4)'),
- sections: z.array(z.string()).optional().describe('Specific h2 section titles to return. If omitted, returns full content.')
+ path: z.string().describe('The path to the blog post (e.g., /blog/v4)')
},
cache: '1h',
- async handler({ path, sections }) {
- try {
- const fullContent = await $fetch(`/raw${path}.md`)
+ async handler({ path }) {
+ const event = useEvent()
+ const fullContent = await fetchPageMarkdown(event, 'blog', path)
- let content = fullContent
- if (sections?.length) {
- content = extractSections(fullContent, sections)
- }
+ if (!fullContent) {
+ return errorResult(`Blog post not found: ${path}`)
+ }
- return {
- content: [{ type: 'text' as const, text: content }]
- }
- } catch (error) {
- return errorResult(`Blog post not found: ${error}`)
+ return {
+ content: [{ type: 'text' as const, text: fullContent }]
}
}
})
diff --git a/server/mcp/tools/get-changelog.ts b/server/mcp/tools/get-changelog.ts
index e4f817311..ccd257f87 100644
--- a/server/mcp/tools/get-changelog.ts
+++ b/server/mcp/tools/get-changelog.ts
@@ -15,7 +15,7 @@ Covers repositories: nuxt/nuxt, nuxt/ui, nuxt/content, nuxt/image, nuxt/fonts, n
OUTPUT: Returns releases with title, repo, tag, date, and raw markdown body. Optionally filter by repository name.`,
inputSchema: {
repo: z.string().optional().describe('Filter by repository (e.g., "nuxt/ui", "nuxt/nuxt"). If omitted, returns releases from all repos.'),
- limit: z.number().min(1).max(50).default(20).describe('Number of releases to return (default: 20, max: 50)')
+ limit: z.number().min(1).max(20).default(5).describe('Number of releases to return (default: 5, max: 20)')
},
annotations: {
readOnlyHint: true
@@ -32,13 +32,11 @@ OUTPUT: Returns releases with title, repo, tag, date, and raw markdown body. Opt
return errorResult(repo ? `No releases found for repository "${repo}"` : 'No releases found')
}
- return jsonResult(filtered.slice(0, limit).map(r => ({
- title: r.title,
- repo: r.repo,
- tag: r.tag,
- date: r.date,
- url: r.url,
- markdown: r.markdown
- })))
+ return jsonResult(filtered.slice(0, limit).map((r) => {
+ const md = r.markdown && r.markdown.length > 4_000
+ ? r.markdown.slice(0, 4_000) + '\n\n[Release notes truncated. Visit the full release page for details.]'
+ : r.markdown
+ return { title: r.title, repo: r.repo, tag: r.tag, date: r.date, url: r.url, markdown: md }
+ }))
}
})
diff --git a/server/mcp/tools/get-deploy-provider.ts b/server/mcp/tools/get-deploy-provider.ts
index 20b6f9723..9126e4265 100644
--- a/server/mcp/tools/get-deploy-provider.ts
+++ b/server/mcp/tools/get-deploy-provider.ts
@@ -17,19 +17,19 @@ EXAMPLES: "/deploy/vercel", "/deploy/cloudflare", "/deploy/netlify", "/deploy/aw
},
cache: '1h',
async handler({ path, sections }) {
- try {
- const fullContent = await $fetch(`/raw${path}.md`)
+ const event = useEvent()
+ const fullContent = await fetchPageMarkdown(event, 'deploy', path)
- let content = fullContent
- if (sections?.length) {
- content = extractSections(fullContent, sections)
- }
+ if (!fullContent) {
+ return errorResult(`Deploy provider not found: ${path}`)
+ }
+
+ const content = sections?.length
+ ? extractSections(fullContent, sections)
+ : fullContent
- return {
- content: [{ type: 'text' as const, text: content }]
- }
- } catch (error) {
- return errorResult(`Deploy provider not found: ${error}`)
+ return {
+ content: [{ type: 'text' as const, text: content }]
}
}
})
diff --git a/server/mcp/tools/get-documentation-page.ts b/server/mcp/tools/get-documentation-page.ts
index 0270ce647..5bbf66e7a 100644
--- a/server/mcp/tools/get-documentation-page.ts
+++ b/server/mcp/tools/get-documentation-page.ts
@@ -35,19 +35,25 @@ Common Issues:
},
cache: '30m',
async handler({ path, sections }) {
- try {
- const fullContent = await $fetch(`/raw${path}.md`)
-
- let content = fullContent
- if (sections?.length) {
- content = extractSections(fullContent, sections)
- }
-
- return {
- content: [{ type: 'text' as const, text: content }]
- }
- } catch (error) {
- return errorResult(`Documentation page not found: ${error}`)
+ const event = useEvent()
+ const docsVersion = path.includes('/docs/5.x') ? 'docsv5' : path.includes('/docs/4.x') ? 'docsv4' : 'docsv3'
+ const fullContent = await fetchPageMarkdown(event, docsVersion, path)
+
+ if (!fullContent) {
+ return errorResult(`Documentation page not found: ${path}`)
+ }
+
+ let content = sections?.length
+ ? extractSections(fullContent, sections)
+ : fullContent
+
+ const MAX_CHARS = 12_000
+ if (content.length > MAX_CHARS) {
+ content = content.slice(0, MAX_CHARS) + '\n\n[Content truncated. Use the sections parameter to request specific h2 sections.]'
+ }
+
+ return {
+ content: [{ type: 'text' as const, text: content }]
}
}
})
diff --git a/server/mcp/tools/get-getting-started-guide.ts b/server/mcp/tools/get-getting-started-guide.ts
index 061808022..6e7415a37 100644
--- a/server/mcp/tools/get-getting-started-guide.ts
+++ b/server/mcp/tools/get-getting-started-guide.ts
@@ -9,21 +9,21 @@ export default defineMcpTool({
},
cache: '30m',
async handler({ version, sections }) {
+ const event = useEvent()
const path = `/docs/${version}/getting-started/introduction`
+ const docsVersion = version === '4.x' ? 'docsv4' : 'docsv3'
+ const fullContent = await fetchPageMarkdown(event, docsVersion, path)
- try {
- const fullContent = await $fetch(`/raw${path}.md`)
+ if (!fullContent) {
+ return errorResult(`Getting started guide not found: ${path}`)
+ }
- let content = fullContent
- if (sections?.length) {
- content = extractSections(fullContent, sections)
- }
+ const content = sections?.length
+ ? extractSections(fullContent, sections)
+ : fullContent
- return {
- content: [{ type: 'text' as const, text: content }]
- }
- } catch (error) {
- return errorResult(`Getting started guide not found: ${error}`)
+ return {
+ content: [{ type: 'text' as const, text: content }]
}
}
})
diff --git a/server/mcp/tools/get-module.ts b/server/mcp/tools/get-module.ts
index f8c19019a..5ef20f8fb 100644
--- a/server/mcp/tools/get-module.ts
+++ b/server/mcp/tools/get-module.ts
@@ -27,23 +27,26 @@ EXAMPLES:
try {
const module = await $fetch(`https://api.nuxt.com/modules/${slug}`)
+ let readme: string | undefined
+ if (module.readme) {
+ const raw = JSON.stringify(module.readme)
+ readme = raw.length > 8_000
+ ? raw.slice(0, 8_000) + '… [README truncated]'
+ : raw
+ }
+
return jsonResult({
name: module.name,
description: module.description,
npm: module.npm,
repo: module.repo,
- github: module.github,
website: module.website,
learn_more: module.learn_more,
category: module.category,
type: module.type,
- sponsor: module.sponsor,
- icon: module.icon,
compatibility: module.compatibility,
- stats: module.stats,
- maintainers: module.maintainers,
- contributors: module.contributors,
- readme: module.readme,
+ stats: module.stats ? { downloads: module.stats.downloads, stars: module.stats.stars } : undefined,
+ readme,
url: `https://nuxt.com/modules/${module.name}`
})
} catch (error: unknown) {
diff --git a/server/mcp/tools/list-documentation-pages.ts b/server/mcp/tools/list-documentation-pages.ts
index 2a88ac3db..7e643f65a 100644
--- a/server/mcp/tools/list-documentation-pages.ts
+++ b/server/mcp/tools/list-documentation-pages.ts
@@ -2,67 +2,47 @@ import { z } from 'zod'
import { queryCollection } from '@nuxt/content/server'
export default defineMcpTool({
- description: `Lists all available Nuxt documentation pages with their categories and basic information.
+ description: `Lists Nuxt documentation pages, optionally filtered by search term.
-WHEN TO USE: Use this tool when you need to EXPLORE or SEARCH for documentation about a topic but don't know the exact page path. For example: "Find documentation about hydration errors", "What pages cover rendering modes?", "Search for migration guides".
-
-WHEN NOT TO USE: If you already know the specific page path (e.g., "/docs/4.x/getting-started/introduction"), use get_documentation_page directly instead.
-
-WORKFLOW: This tool returns page titles, descriptions, and paths. After finding relevant pages, use get_documentation_page to retrieve the full content.`,
+WHEN TO USE: When you need to find documentation about a topic but don't know the exact page path.
+WHEN NOT TO USE: If you already know the page path, use get_documentation_page directly.
+TIPS: Always pass a search term to narrow results — avoids dumping the entire catalog.`,
inputSchema: {
- version: z.enum(['3.x', '4.x', '5.x', 'all']).optional().default('4.x').describe('Documentation version to fetch')
+ version: z.enum(['3.x', '4.x', '5.x', 'all']).optional().default('4.x').describe('Documentation version to fetch'),
+ search: z.string().optional().describe('Filter pages by keyword (matches title, path, and description). Strongly recommended to avoid large results.')
},
cache: '1h',
- async handler({ version }) {
+ async handler({ version, search }) {
const event = useEvent()
- let allDocs = []
+ let allDocs: { title: string, path: string, description: string }[] = []
- if (version === '3.x') {
- allDocs = await queryCollection(event, 'docsv3')
- .select('title', 'path', 'description')
- .all()
+ const collections = version === 'all'
+ ? ['docsv3', 'docsv4', 'docsv5'] as const
+ : [version === '3.x' ? 'docsv3' : version === '5.x' ? 'docsv5' : 'docsv4'] as const
- if (!allDocs) {
- return errorResult('Documentation pages collection not found')
- }
- } else if (version === '4.x') {
- allDocs = await queryCollection(event, 'docsv4')
+ for (const col of collections) {
+ const docs = await queryCollection(event, col)
.select('title', 'path', 'description')
.all()
-
- if (!allDocs) {
- return errorResult('Documentation pages collection not found')
- }
- } else if (version === '5.x') {
- allDocs = await queryCollection(event, 'docsv5')
- .select('title', 'path', 'description')
- .all()
-
- if (!allDocs) {
- return errorResult('Documentation pages collection not found')
- }
- } else {
- // TODO: include docsv5 in 'all' when Nuxt 5 is released
- const docsV3 = await queryCollection(event, 'docsv3')
- .select('title', 'path', 'description')
- .all()
-
- const docsV4 = await queryCollection(event, 'docsv4')
- .select('title', 'path', 'description')
- .all()
-
- if (!docsV3 || !docsV4) {
+ if (!docs) {
+ if (version === 'all') continue
return errorResult('Documentation pages collection not found')
}
+ allDocs.push(...docs)
+ }
- allDocs = [...docsV3, ...docsV4]
+ if (search) {
+ const terms = search.toLowerCase().split(/\s+/)
+ allDocs = allDocs.filter((doc) => {
+ const haystack = `${doc.title ?? ''} ${doc.path ?? ''} ${doc.description ?? ''}`.toLowerCase()
+ return terms.every(t => haystack.includes(t))
+ })
}
return jsonResult(allDocs.map(doc => ({
title: doc.title,
path: doc.path,
- description: doc.description,
- version: doc.path.includes('/docs/5.x') ? '5.x' : doc.path.includes('/docs/4.x') ? '4.x' : '3.x',
+ ...(search ? { description: doc.description } : {}),
url: `https://nuxt.com${doc.path}`
})))
}
diff --git a/server/mcp/tools/list-modules.ts b/server/mcp/tools/list-modules.ts
index 4d408d38f..8dfdd4d39 100644
--- a/server/mcp/tools/list-modules.ts
+++ b/server/mcp/tools/list-modules.ts
@@ -41,7 +41,7 @@ OUTPUT: Returns list of modules with name, description, category, stats. Use get
const searchLower = search.toLowerCase()
modules = modules.filter(module =>
module.name.toLowerCase().includes(searchLower)
- || module.description.toLowerCase().includes(searchLower)
+ || module.description?.toLowerCase().includes(searchLower)
|| module.npm.toLowerCase().includes(searchLower)
)
}
@@ -87,8 +87,10 @@ OUTPUT: Returns list of modules with name, description, category, stats. Use get
}
})
+ const totalMatches = modules.length
+
return jsonResult({
- modules: modules.map(module => ({
+ modules: modules.slice(0, 20).map(module => ({
name: module.name,
description: module.description,
npm: module.npm,
@@ -97,16 +99,12 @@ OUTPUT: Returns list of modules with name, description, category, stats. Use get
website: module.website,
learn_more: module.learn_more,
category: module.category,
- type: module.type,
- sponsor: module.sponsor,
- icon: module.icon,
- compatibility: module.compatibility,
- stats: module.stats,
- maintainers: module.maintainers,
+ downloads: module.stats?.downloads,
+ stars: module.stats?.stars,
url: `https://nuxt.com/modules/${module.name}`
})),
stats: response.stats,
- total: modules.length
+ total: totalMatches
})
}
})
diff --git a/server/utils/agent-fingerprint.ts b/server/utils/agent-fingerprint.ts
new file mode 100644
index 000000000..8f0845e97
--- /dev/null
+++ b/server/utils/agent-fingerprint.ts
@@ -0,0 +1,10 @@
+import type { H3Event } from 'h3'
+
+export async function getAgentFingerprint(event: H3Event): Promise {
+ const ip = event.context.cf?.ip || 'unknown'
+ const userAgent = getHeader(event, 'user-agent') || 'unknown'
+ const domain = getHeader(event, 'host') || 'localhost'
+ const data = `${domain}+${ip}+${userAgent}`
+ const buffer = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(data))
+ return [...new Uint8Array(buffer)].map(b => b.toString(16).padStart(2, '0')).join('')
+}
diff --git a/server/utils/mcp.ts b/server/utils/mcp.ts
index 02b622933..37092d5ba 100644
--- a/server/utils/mcp.ts
+++ b/server/utils/mcp.ts
@@ -1,6 +1,56 @@
+import type { H3Event } from 'h3'
+import { stringify } from 'minimark/stringify'
+import type { MinimarkNode } from 'minimark'
+import { queryCollection } from '@nuxt/content/server'
+
+type CollectionName = Parameters[1]
+
+/**
+ * Fetches a page from a known content collection and renders it as markdown.
+ *
+ * Mirrors the output of `/raw/.md` (provided by `@nuxt/content`'s
+ * `nuxt-llms` feature) — minimark-stringified body with the title/description
+ * prepended and resource links appended — but skips its collection-iteration
+ * lookup, which can throw or return 404 for `type: 'page'` collections that
+ * aren't keyed by the requested path.
+ */
+export async function fetchPageMarkdown(
+ event: H3Event,
+ collection: CollectionName,
+ path: string
+): Promise {
+ const page = await queryCollection(event, collection).path(path).first() as
+ | { title?: string, description?: string, body?: { value?: MinimarkNode[] }, links?: unknown[], meta?: { links?: unknown[] } }
+ | null
+
+ if (!page?.body?.value) return null
+
+ const value: MinimarkNode[] = [...page.body.value]
+
+ if ((value[0] as unknown[] | undefined)?.[0] !== 'h1') {
+ if (page.description) value.unshift(['blockquote', {}, page.description])
+ if (page.title) value.unshift(['h1', {}, page.title])
+ }
+
+ const links = page.links || page.meta?.links
+ if (Array.isArray(links) && links.length > 0) {
+ const items: MinimarkNode[] = links
+ .filter((link): link is { label: string, to: string } => Boolean((link as { label?: string }).label && (link as { to?: string }).to))
+ .map(link => ['li', {}, ['a', { href: link.to }, link.label]])
+ if (items.length > 0) {
+ value.push(['hr', {}])
+ value.push(['ul', {}, ...items])
+ }
+ }
+
+ return stringify({ type: 'minimark', value }, { format: 'markdown/html' })
+}
+
/**
* Extract specific h2 sections from markdown content.
* Always includes the title (h1) and description (first blockquote).
+ * If no requested section matches, returns the full markdown to avoid
+ * round-tripping (the model would otherwise refetch with no `sections`).
*/
export function extractSections(markdown: string, sectionTitles: string[]): string {
const lines = markdown.split('\n')
@@ -23,12 +73,14 @@ export function extractSections(markdown: string, sectionTitles: string[]): stri
let currentSection: string | null = null
let sectionContent: string[] = []
+ let matchedAny = false
for (const line of lines) {
if (line.startsWith('## ')) {
if (currentSection && normalizedTitles.includes(currentSection.toLowerCase())) {
result.push(...sectionContent)
result.push('')
+ matchedAny = true
}
currentSection = line.replace('## ', '').trim()
sectionContent = [line]
@@ -42,7 +94,10 @@ export function extractSections(markdown: string, sectionTitles: string[]): stri
if (currentSection && normalizedTitles.includes(currentSection.toLowerCase())) {
result.push(...sectionContent)
+ matchedAny = true
}
+ if (!matchedAny) return markdown
+
return result.join('\n').trim()
}
diff --git a/server/utils/rate-limit.ts b/server/utils/rate-limit.ts
new file mode 100644
index 000000000..982b03820
--- /dev/null
+++ b/server/utils/rate-limit.ts
@@ -0,0 +1,45 @@
+import type { H3Event } from 'h3'
+import { eq, sql } from 'drizzle-orm'
+
+const DAILY_LIMIT = 20
+
+function todayKey(ip: string): string {
+ const date = new Date().toISOString().slice(0, 10)
+ return `rate:agent:${ip}:${date}`
+}
+
+function resolveIP(event: H3Event): string {
+ return getRequestIP(event, { xForwardedFor: true }) || 'unknown'
+}
+
+export async function checkAgentRateLimit(event: H3Event): Promise<{ used: number, remaining: number, limit: number }> {
+ const ip = resolveIP(event)
+ const key = todayKey(ip)
+ const rows = await db.select().from(schema.agentDailyUsage).where(eq(schema.agentDailyUsage.dayKey, key)).limit(1)
+ const used = rows[0]?.count ?? 0
+ const remaining = Math.max(0, DAILY_LIMIT - used)
+
+ return { used, remaining, limit: DAILY_LIMIT }
+}
+
+export async function consumeAgentRateLimit(event: H3Event): Promise<{ used: number, remaining: number, limit: number }> {
+ const ip = resolveIP(event)
+ const key = todayKey(ip)
+
+ return await db.transaction(async (tx: typeof db) => {
+ await tx.insert(schema.agentDailyUsage).values({ dayKey: key, count: 1 })
+ .onConflictDoUpdate({
+ target: schema.agentDailyUsage.dayKey,
+ set: { count: sql`${schema.agentDailyUsage.count} + 1` }
+ })
+ const [row] = await tx.select().from(schema.agentDailyUsage).where(eq(schema.agentDailyUsage.dayKey, key))
+ const used = row!.count
+ if (used > DAILY_LIMIT) {
+ throw createError({
+ statusCode: 429,
+ message: `You've reached the daily limit of ${DAILY_LIMIT} messages. Try again tomorrow.`
+ })
+ }
+ return { used, remaining: DAILY_LIMIT - used, limit: DAILY_LIMIT }
+ })
+}
diff --git a/server/utils/tools/open-playground.ts b/server/utils/tools/open-playground.ts
new file mode 100644
index 000000000..5fef6edb2
--- /dev/null
+++ b/server/utils/tools/open-playground.ts
@@ -0,0 +1,31 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+
+export const openPlaygroundTool = tool({
+ description: 'Generate a StackBlitz playground link for a Nuxt example or GitHub repository. Use when the user wants to try code live, see a working example, or experiment with a Nuxt feature in the browser.',
+ inputSchema: z.object({
+ repo: z.string().regex(/^[\w.-]+\/[\w.-]+$/).describe('GitHub repository in "owner/repo" format (e.g., "nuxt/starter", "nuxt-ui-templates/dashboard")'),
+ branch: z.string().default('main').describe('Branch name'),
+ dir: z.string().default('').describe('Subdirectory path within the repo'),
+ file: z.string().default('app.vue').describe('Default file to open'),
+ title: z.string().optional().describe('Display title for the playground')
+ }),
+ execute: async ({ repo, branch, dir, file, title }) => {
+ const [owner, name] = repo.split('/') as [string, string]
+ const encodedBranch = encodeURIComponent(branch)
+ const encodedDir = dir
+ ? dir.split('/').filter(Boolean).map(encodeURIComponent).join('/')
+ : ''
+ const dirPath = encodedDir ? `/tree/${encodedBranch}/${encodedDir}` : `/tree/${encodedBranch}`
+ const url = `https://stackblitz.com/github/${encodeURIComponent(owner)}/${encodeURIComponent(name)}${dirPath}?file=${encodeURIComponent(file)}`
+
+ return {
+ url,
+ repo,
+ branch,
+ dir,
+ file,
+ title: title || name || repo
+ }
+ }
+})
diff --git a/server/utils/tools/report-issue.ts b/server/utils/tools/report-issue.ts
new file mode 100644
index 000000000..83c165453
--- /dev/null
+++ b/server/utils/tools/report-issue.ts
@@ -0,0 +1,11 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+
+export const reportIssueTool = tool({
+ description: 'Use this when you cannot resolve the user\'s problem after exhausting all available tools, or when the user explicitly signals frustration or a bad experience. This surfaces an inline feedback card so the user can report the issue with full conversation context attached automatically.',
+ inputSchema: z.object({
+ title: z.string().max(80).describe('Short issue title describing the problem (max 80 chars)'),
+ summary: z.string().describe('1-3 sentence summary of what was attempted and why it could not be resolved')
+ }),
+ execute: async ({ title, summary }) => ({ title, summary })
+})
diff --git a/server/utils/tools/search-github-issues.ts b/server/utils/tools/search-github-issues.ts
new file mode 100644
index 000000000..d4862c98a
--- /dev/null
+++ b/server/utils/tools/search-github-issues.ts
@@ -0,0 +1,83 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+import type { H3Event } from 'h3'
+
+const NUXT_ORGS = ['nuxt', 'nuxt-modules', 'nuxt-content']
+
+interface GitHubSearchResult {
+ total_count: number
+ items: Array<{
+ title: string
+ number: number
+ state: string
+ html_url: string
+ created_at: string
+ body: string | null
+ comments: number
+ labels: Array<{ name: string }>
+ repository_url: string
+ pull_request?: unknown
+ }>
+}
+
+export function createSearchGitHubIssuesTool(event: H3Event) {
+ return tool({
+ description: 'Search GitHub Issues across the Nuxt ecosystem (nuxt, nuxt-modules, nuxt-content orgs). Use when the user shares an error message, stack trace, or debugging question. Returns matching issues with status, labels, and body excerpts. Much faster and cheaper than web search for Nuxt-specific bugs.',
+ inputSchema: z.object({
+ query: z.string().describe('Error message, keyword, or search term'),
+ repo: z.string().optional().describe('Scope to a specific repo (e.g. "nuxt/nuxt", "nuxt/ui"). Omit to search all Nuxt orgs.'),
+ state: z.enum(['open', 'closed', 'all']).optional().describe('Filter by issue state')
+ }),
+ execute: async ({ query, repo, state = 'all' }) => {
+ const token = useRuntimeConfig(event).github.token
+
+ let q = query
+ if (repo) {
+ q += ` repo:${repo}`
+ } else {
+ q += ` ${NUXT_ORGS.map(org => `org:${org}`).join(' ')}`
+ }
+ if (state !== 'all') {
+ q += ` state:${state}`
+ }
+ q += ' is:issue'
+
+ const url = `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&per_page=8&sort=relevance`
+
+ const headers: Record = {
+ 'Accept': 'application/vnd.github.v3+json',
+ 'User-Agent': 'nuxt-agent'
+ }
+ if (token) {
+ headers['Authorization'] = `token ${token}`
+ }
+
+ try {
+ const data = await $fetch(url, { headers, timeout: 5000 })
+
+ if (!data.items?.length) {
+ return { total: 0, issues: [], message: 'No matching issues found.' }
+ }
+
+ return {
+ total: data.total_count,
+ issues: data.items.map(issue => ({
+ title: issue.title,
+ number: issue.number,
+ repo: issue.repository_url.replace('https://api.github.com/repos/', ''),
+ state: issue.state,
+ labels: issue.labels.map(l => l.name),
+ url: issue.html_url,
+ date: issue.created_at.slice(0, 10),
+ comments: issue.comments,
+ body: issue.body
+ ? issue.body.slice(0, 500) + (issue.body.length > 500 ? '…' : '')
+ : null
+ }))
+ }
+ } catch (error) {
+ return { error: `GitHub search failed: ${(error as Error).message}` }
+ }
+ }
+ })
+}
diff --git a/server/utils/tools/show-blog-post.ts b/server/utils/tools/show-blog-post.ts
new file mode 100644
index 000000000..fd93be7ea
--- /dev/null
+++ b/server/utils/tools/show-blog-post.ts
@@ -0,0 +1,44 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+import { queryCollection } from '@nuxt/content/server'
+import type { H3Event } from 'h3'
+
+export function createShowBlogPostTool(event: H3Event) {
+ return tool({
+ description: 'Display a rich blog post card with image, title, date, and author. Use when the user asks about Nuxt blog posts, release announcements, tutorials, or when referencing a specific blog article.',
+ inputSchema: z.object({
+ title: z.string().describe('The blog post title or search keyword (e.g., "v4", "Nuxt 3.15", "TypeScript")')
+ }),
+ execute: async ({ title }) => {
+ const posts = await queryCollection(event, 'blog')
+ .where('extension', '=', 'md')
+ .order('date', 'DESC')
+ .all()
+
+ const post = posts.find(p =>
+ p.path !== '/blog'
+ && (
+ p.title?.toLowerCase().includes(title.toLowerCase())
+ || p.path?.toLowerCase().includes(title.toLowerCase())
+ )
+ )
+
+ if (!post) {
+ return { error: `Blog post matching "${title}" not found` }
+ }
+
+ return {
+ title: post.title,
+ description: post.description,
+ path: post.path,
+ date: post.date,
+ image: post.image,
+ category: post.category,
+ authors: post.authors?.map(a => ({
+ name: a.name,
+ avatar: a.avatar?.src
+ }))
+ }
+ }
+ })
+}
diff --git a/server/utils/tools/show-hosting.ts b/server/utils/tools/show-hosting.ts
new file mode 100644
index 000000000..3e904bc44
--- /dev/null
+++ b/server/utils/tools/show-hosting.ts
@@ -0,0 +1,40 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+import { queryCollection } from '@nuxt/content/server'
+import type { H3Event } from 'h3'
+
+export function createShowHostingTool(event: H3Event) {
+ return tool({
+ description: 'Display a hosting/deployment provider card with logo, description, and deploy links. Use when the user asks about deploying a Nuxt app, hosting options, or a specific provider (Vercel, Netlify, Cloudflare, etc.).',
+ inputSchema: z.object({
+ name: z.string().trim().min(1).describe('The hosting provider name (e.g., "vercel", "netlify", "cloudflare")')
+ }),
+ execute: async ({ name }) => {
+ const needle = name.trim().toLowerCase()
+ const providers = await queryCollection(event, 'deploy').all()
+ const provider = providers.find(p =>
+ p.path !== '/deploy'
+ && (
+ p.title?.toLowerCase() === needle
+ || p.path?.toLowerCase().endsWith(`/${needle}`)
+ || p.title?.toLowerCase().includes(needle)
+ )
+ )
+
+ if (!provider) {
+ return { error: `Hosting provider "${name}" not found` }
+ }
+
+ return {
+ title: provider.title,
+ description: provider.description,
+ path: provider.path,
+ logoSrc: provider.logoSrc,
+ logoIcon: provider.logoIcon,
+ category: provider.category,
+ nitroPreset: provider.nitroPreset,
+ website: provider.website
+ }
+ }
+ })
+}
diff --git a/server/utils/tools/show-module.ts b/server/utils/tools/show-module.ts
new file mode 100644
index 000000000..afedc0e36
--- /dev/null
+++ b/server/utils/tools/show-module.ts
@@ -0,0 +1,77 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+import type { UIToolInvocation } from 'ai'
+import { FetchError } from 'ofetch'
+
+export type ShowModuleUIToolInvocation = UIToolInvocation
+
+const MODULE_API = 'https://api.nuxt.com/modules'
+
+/** Try alternate slugs (e.g. NuxtHub → `hub`). */
+function slugCandidates(raw: string): string[] {
+ const t = raw.trim()
+ const lower = t.toLowerCase()
+ const hyphenated = lower.replace(/\s+/g, '-')
+ const set = new Set([t, lower, hyphenated])
+
+ if (lower === '@nuxthub/core' || (lower.endsWith('/core') && lower.includes('nuxthub'))) {
+ set.add('hub')
+ }
+ if (
+ ['nuxthub', 'nuxt-hub', 'nuxt hub'].includes(lower)
+ || (lower.includes('nuxt') && lower.includes('hub') && !lower.includes('devtools'))
+ ) {
+ set.add('hub')
+ }
+
+ return [...set].filter(Boolean)
+}
+
+async function fetchModule(slug: string): Promise | null> {
+ const url = `${MODULE_API}/${encodeURIComponent(slug)}`
+ try {
+ const data = await $fetch>(url)
+ return data.error === true ? null : data
+ } catch (e) {
+ if (e instanceof FetchError && e.statusCode === 404) return null
+ throw e
+ }
+}
+
+export const showModuleTool = tool({
+ description: 'Display a Nuxt module card with install command. Use this tool when the user asks about installing, using, or recommending a specific Nuxt module. The card shows the module icon, description, stats, and a copy-able install command. Prefer catalog slugs when known (e.g. "hub" for NuxtHub / @nuxthub/core, "pinia" for Pinia).',
+ inputSchema: z.object({
+ name: z.string().describe('Module slug (e.g. "pinia", "i18n", "hub" for NuxtHub)')
+ }),
+ execute: async ({ name }) => {
+ let data: Record | null = null
+
+ for (const slug of slugCandidates(name)) {
+ data = await fetchModule(slug)
+ if (data) break
+ }
+
+ if (!data) {
+ return { error: `Module "${name}" not found` }
+ }
+
+ const catalogName = data.name
+ if (typeof catalogName !== 'string' || !catalogName.trim()) {
+ return { error: `Module "${name}" returned an invalid response` }
+ }
+
+ const stats = data.stats as Record | undefined
+
+ return {
+ name: catalogName,
+ npm: data.npm as string,
+ description: data.description as string,
+ icon: data.icon as string,
+ category: data.category as string,
+ repo: data.repo as string,
+ website: data.website as string,
+ downloads: stats?.downloads as number | undefined,
+ stars: stats?.stars as number | undefined
+ }
+ }
+})
diff --git a/server/utils/tools/show-template.ts b/server/utils/tools/show-template.ts
new file mode 100644
index 000000000..80f7e17a4
--- /dev/null
+++ b/server/utils/tools/show-template.ts
@@ -0,0 +1,44 @@
+import { tool } from 'ai'
+import { z } from 'zod'
+import { queryCollection } from '@nuxt/content/server'
+import type { H3Event } from 'h3'
+
+export function createShowTemplateTool(event: H3Event) {
+ return tool({
+ description: 'Display one or more Nuxt starter template cards with preview image, description, and action buttons. Use when the user asks about starter templates, project scaffolding, or wants to create a new Nuxt project. Pass multiple names to show several templates at once.',
+ inputSchema: z.object({
+ names: z.array(z.string().trim().min(1)).min(1).describe('Template names or slugs to display (e.g., ["ui", "content", "starter", "movies"])')
+ }),
+ execute: async ({ names }) => {
+ const allTemplates = await queryCollection(event, 'templates').all()
+
+ const results = names.map((rawName) => {
+ const name = rawName.trim().toLowerCase()
+ const template = allTemplates.find(t =>
+ t.slug.toLowerCase() === name
+ || t.name.toLowerCase() === name
+ || t.slug.toLowerCase().includes(name)
+ || t.name.toLowerCase().includes(name)
+ )
+
+ if (!template) return null
+
+ return {
+ name: template.name,
+ slug: template.slug,
+ description: template.description,
+ repo: template.repo,
+ demo: template.demo,
+ badge: template.badge,
+ purchase: template.purchase
+ }
+ }).filter(Boolean)
+
+ if (!results.length) {
+ return { error: `No templates found matching: ${names.join(', ')}` }
+ }
+
+ return { templates: results }
+ }
+ })
+}
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 000000000..a830cace7
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,8 @@
+{
+ "crons": [
+ {
+ "path": "/api/agent/cleanup",
+ "schedule": "0 3 * * *"
+ }
+ ]
+}