@8wave/ai-elements 0.115.0-beta.2 → 0.115.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/_chunks/{PkStreamingMarkdown-BKxA6Ntl.js → PkStreamingMarkdown-B7SRIqnV.js} +2 -2
  2. package/dist/_chunks/{PkStreamingMarkdown-BKxA6Ntl.js.map → PkStreamingMarkdown-B7SRIqnV.js.map} +1 -1
  3. package/dist/_chunks/{PkToolRequestOAuthConnection-CgQePKKb.js → PkToolRequestOAuthConnection-CbZn1f14.js} +31 -25
  4. package/dist/_chunks/PkToolRequestOAuthConnection-CbZn1f14.js.map +1 -0
  5. package/dist/_chunks/{PkToolShowArtifact-CHxeuKEU.js → PkToolShowArtifact-CRQySuGC.js} +2 -2
  6. package/dist/_chunks/{PkToolShowArtifact-CHxeuKEU.js.map → PkToolShowArtifact-CRQySuGC.js.map} +1 -1
  7. package/dist/_chunks/{PkToolShowCalendarEvent-D0jVbqWt.js → PkToolShowCalendarEvent-Cj7yRj-0.js} +2 -2
  8. package/dist/_chunks/{PkToolShowCalendarEvent-D0jVbqWt.js.map → PkToolShowCalendarEvent-Cj7yRj-0.js.map} +1 -1
  9. package/dist/_chunks/{PkToolShowContactForm-DqVLVwqJ.js → PkToolShowContactForm-DGmzPa3d.js} +2 -2
  10. package/dist/_chunks/{PkToolShowContactForm-DqVLVwqJ.js.map → PkToolShowContactForm-DGmzPa3d.js.map} +1 -1
  11. package/dist/_chunks/{PkToolShowForm-YJQPYfEe.js → PkToolShowForm-76LQdZHH.js} +2 -2
  12. package/dist/_chunks/{PkToolShowForm-YJQPYfEe.js.map → PkToolShowForm-76LQdZHH.js.map} +1 -1
  13. package/dist/_chunks/{PkToolShowImageGallery-1mq-_cnp.js → PkToolShowImageGallery-BmpcrdIL.js} +2 -2
  14. package/dist/_chunks/{PkToolShowImageGallery-1mq-_cnp.js.map → PkToolShowImageGallery-BmpcrdIL.js.map} +1 -1
  15. package/dist/_chunks/{PkToolShowSources-VFoI0YS8.js → PkToolShowSources-13IBQve7.js} +3 -3
  16. package/dist/_chunks/{PkToolShowSources-VFoI0YS8.js.map → PkToolShowSources-13IBQve7.js.map} +1 -1
  17. package/dist/_chunks/{PkToolShowWebPages-BgXlRdmH.js → PkToolShowWebPages-CCSRAlz5.js} +2 -2
  18. package/dist/_chunks/{PkToolShowWebPages-BgXlRdmH.js.map → PkToolShowWebPages-CCSRAlz5.js.map} +1 -1
  19. package/dist/_chunks/{markdown-BL7WANiX.js → markdown-tS_2A0k9.js} +39 -11
  20. package/dist/_chunks/markdown-tS_2A0k9.js.map +1 -0
  21. package/dist/_chunks/{url-validation-uLMZ5-AU.js → url-validation-9DstIfq2.js} +9 -2
  22. package/dist/_chunks/{url-validation-uLMZ5-AU.js.map → url-validation-9DstIfq2.js.map} +1 -1
  23. package/dist/ai-elements.es.js +1697 -1578
  24. package/dist/ai-elements.es.js.map +1 -1
  25. package/dist-vue/PkChatbot.js +1 -1
  26. package/dist-vue/PkChatbotEmptyState.js +1 -1
  27. package/dist-vue/PkChatbotInput.js +1 -1
  28. package/dist-vue/PkChatbotMessages.js +1 -1
  29. package/dist-vue/PkChatbotSteps.js +1 -1
  30. package/dist-vue/PkChatbotViewChat.js +1 -1
  31. package/dist-vue/PkChatbotViewConversations.js +1 -1
  32. package/dist-vue/PkChatbotViewFiles.js +1 -1
  33. package/dist-vue/PkChatbotViewProfile.js +1 -1
  34. package/dist-vue/_chunks/{Document-nCWwJC2h.js → Document-BmfGStvM.js} +40 -12
  35. package/dist-vue/_chunks/Document-BmfGStvM.js.map +1 -0
  36. package/dist-vue/_chunks/{PkChatbot-CdtYJ1Lf.js → PkChatbot-DKqKEsUn.js} +7 -7
  37. package/dist-vue/_chunks/{PkChatbot-CdtYJ1Lf.js.map → PkChatbot-DKqKEsUn.js.map} +1 -1
  38. package/dist-vue/_chunks/PkChatbotAuth-jzoNJkmH.js.map +1 -1
  39. package/dist-vue/_chunks/{PkChatbotEmptyState-Dicjr_Fv.js → PkChatbotEmptyState-BPPP0-0U.js} +2 -2
  40. package/dist-vue/_chunks/{PkChatbotEmptyState-Dicjr_Fv.js.map → PkChatbotEmptyState-BPPP0-0U.js.map} +1 -1
  41. package/dist-vue/_chunks/PkChatbotFilePreview-CwiVQDF2.js.map +1 -1
  42. package/dist-vue/_chunks/{PkChatbotInput-DcLmOqA1.js → PkChatbotInput-BHcxLnWU.js} +2 -2
  43. package/dist-vue/_chunks/PkChatbotInput-BHcxLnWU.js.map +1 -0
  44. package/dist-vue/_chunks/PkChatbotMessageFeedback-BYmrVaCE.js.map +1 -1
  45. package/dist-vue/_chunks/{PkChatbotMessages-R1EsTJpM.js → PkChatbotMessages-CaHFzdIC.js} +122 -113
  46. package/dist-vue/_chunks/PkChatbotMessages-CaHFzdIC.js.map +1 -0
  47. package/dist-vue/_chunks/{PkChatbotSteps-CPPjwTJN.js → PkChatbotSteps-CXiFF3Z-.js} +2 -2
  48. package/dist-vue/_chunks/{PkChatbotSteps-CPPjwTJN.js.map → PkChatbotSteps-CXiFF3Z-.js.map} +1 -1
  49. package/dist-vue/_chunks/{PkChatbotViewChat-CbqrHEC6.js → PkChatbotViewChat-D5HtjK3u.js} +9 -7
  50. package/dist-vue/_chunks/{PkChatbotViewChat-CbqrHEC6.js.map → PkChatbotViewChat-D5HtjK3u.js.map} +1 -1
  51. package/dist-vue/_chunks/{PkChatbotViewConversations-CHpN4vhm.js → PkChatbotViewConversations-C5O-IocA.js} +2 -2
  52. package/dist-vue/_chunks/{PkChatbotViewConversations-CHpN4vhm.js.map → PkChatbotViewConversations-C5O-IocA.js.map} +1 -1
  53. package/dist-vue/_chunks/{PkChatbotViewFiles-Bk9-K6Hw.js → PkChatbotViewFiles-W8DDmtTi.js} +3 -3
  54. package/dist-vue/_chunks/{PkChatbotViewFiles-Bk9-K6Hw.js.map → PkChatbotViewFiles-W8DDmtTi.js.map} +1 -1
  55. package/dist-vue/_chunks/{PkChatbotViewProfile-CosaHA-g.js → PkChatbotViewProfile--upofqcF.js} +2 -2
  56. package/dist-vue/_chunks/{PkChatbotViewProfile-CosaHA-g.js.map → PkChatbotViewProfile--upofqcF.js.map} +1 -1
  57. package/dist-vue/_chunks/{PkStreamingMarkdown-DYy79C1P.js → PkStreamingMarkdown-it3X7T75.js} +2 -2
  58. package/dist-vue/_chunks/{PkStreamingMarkdown-DYy79C1P.js.map → PkStreamingMarkdown-it3X7T75.js.map} +1 -1
  59. package/dist-vue/_chunks/PkToolRequestOAuthConnection-BiaSF6R_.js +124 -0
  60. package/dist-vue/_chunks/PkToolRequestOAuthConnection-BiaSF6R_.js.map +1 -0
  61. package/dist-vue/_chunks/{PkToolShowArtifact-DUOnFdfa.js → PkToolShowArtifact-BFyFYjHI.js} +2 -2
  62. package/dist-vue/_chunks/{PkToolShowArtifact-DUOnFdfa.js.map → PkToolShowArtifact-BFyFYjHI.js.map} +1 -1
  63. package/dist-vue/_chunks/{PkToolShowCalendarEvent-BsBdsbV0.js → PkToolShowCalendarEvent-yttlTHiX.js} +2 -2
  64. package/dist-vue/_chunks/{PkToolShowCalendarEvent-BsBdsbV0.js.map → PkToolShowCalendarEvent-yttlTHiX.js.map} +1 -1
  65. package/dist-vue/_chunks/{PkToolShowContactForm-1TFKvG_s.js → PkToolShowContactForm-YpaC_31s.js} +2 -2
  66. package/dist-vue/_chunks/{PkToolShowContactForm-1TFKvG_s.js.map → PkToolShowContactForm-YpaC_31s.js.map} +1 -1
  67. package/dist-vue/_chunks/{PkToolShowDiagram-Dmtqu3th.js → PkToolShowDiagram-Bdxjd0BJ.js} +2 -2
  68. package/dist-vue/_chunks/{PkToolShowDiagram-Dmtqu3th.js.map → PkToolShowDiagram-Bdxjd0BJ.js.map} +1 -1
  69. package/dist-vue/_chunks/{PkToolShowEmail-BDsBoETQ.js → PkToolShowEmail-CNkFP7qC.js} +2 -2
  70. package/dist-vue/_chunks/{PkToolShowEmail-BDsBoETQ.js.map → PkToolShowEmail-CNkFP7qC.js.map} +1 -1
  71. package/dist-vue/_chunks/{PkToolShowForm-DwFKxMJd.js → PkToolShowForm-FKJNKXPq.js} +2 -2
  72. package/dist-vue/_chunks/{PkToolShowForm-DwFKxMJd.js.map → PkToolShowForm-FKJNKXPq.js.map} +1 -1
  73. package/dist-vue/_chunks/{PkToolShowImageGallery-DudQzBx4.js → PkToolShowImageGallery-DmhZLnBk.js} +2 -2
  74. package/dist-vue/_chunks/{PkToolShowImageGallery-DudQzBx4.js.map → PkToolShowImageGallery-DmhZLnBk.js.map} +1 -1
  75. package/dist-vue/_chunks/{PkToolShowMessage-Dau3agUy.js → PkToolShowMessage-C9xFAi1s.js} +2 -2
  76. package/dist-vue/_chunks/{PkToolShowMessage-Dau3agUy.js.map → PkToolShowMessage-C9xFAi1s.js.map} +1 -1
  77. package/dist-vue/_chunks/{PkToolShowProductList-CsupMsuy.js → PkToolShowProductList-k1ckf1Ko.js} +2 -2
  78. package/dist-vue/_chunks/{PkToolShowProductList-CsupMsuy.js.map → PkToolShowProductList-k1ckf1Ko.js.map} +1 -1
  79. package/dist-vue/_chunks/{PkToolShowSources-TzkNgwnr.js → PkToolShowSources-DE0RO8z9.js} +4 -4
  80. package/dist-vue/_chunks/{PkToolShowSources-TzkNgwnr.js.map → PkToolShowSources-DE0RO8z9.js.map} +1 -1
  81. package/dist-vue/_chunks/{PkToolShowWebPages-D4E3Xzg1.js → PkToolShowWebPages-v-6DGCNM.js} +3 -3
  82. package/dist-vue/_chunks/{PkToolShowWebPages-D4E3Xzg1.js.map → PkToolShowWebPages-v-6DGCNM.js.map} +1 -1
  83. package/dist-vue/_chunks/{PkUrl-Cu3qKl4G.js → PkUrl-C5UXwNxy.js} +2 -2
  84. package/dist-vue/_chunks/{PkUrl-Cu3qKl4G.js.map → PkUrl-C5UXwNxy.js.map} +1 -1
  85. package/dist-vue/_chunks/{createChatbotApiClient-BlWs282E.js → createChatbotApiClient-L7wpQUUA.js} +110 -110
  86. package/dist-vue/_chunks/createChatbotApiClient-L7wpQUUA.js.map +1 -0
  87. package/dist-vue/_chunks/mcpOAuthReport-DG84H11Y.js +9 -0
  88. package/dist-vue/_chunks/mcpOAuthReport-DG84H11Y.js.map +1 -0
  89. package/dist-vue/_chunks/{url-validation-uLMZ5-AU.js → url-validation-9DstIfq2.js} +9 -2
  90. package/dist-vue/_chunks/{url-validation-uLMZ5-AU.js.map → url-validation-9DstIfq2.js.map} +1 -1
  91. package/dist-vue/_chunks/{useChatbotStore-CMo47fnS.js → useChatbotStore-Ctr5gDIr.js} +2 -2
  92. package/dist-vue/_chunks/{useChatbotStore-CMo47fnS.js.map → useChatbotStore-Ctr5gDIr.js.map} +1 -1
  93. package/dist-vue/api.js +1 -1
  94. package/dist-vue/composables.js +118 -10
  95. package/dist-vue/composables.js.map +1 -1
  96. package/dist-vue/index.js +2976 -2972
  97. package/dist-vue/index.js.map +1 -1
  98. package/dist-vue/locales.js +4 -0
  99. package/dist-vue/packages/auth/src/active-membership.d.ts +36 -0
  100. package/dist-vue/packages/auth/src/add-member-to-organization.d.ts +5 -1
  101. package/dist-vue/packages/auth/src/passkey-policy-client-plugin.d.ts +1 -1
  102. package/dist-vue/packages/auth/src/social-redirect-plugin.d.ts +13 -1
  103. package/dist-vue/packages/auth/src/two-factor-policy-client-plugin.d.ts +3 -3
  104. package/dist-vue/packages/auth/src/two-factor-policy-plugin.d.ts +1 -1
  105. package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +5 -0
  106. package/dist-vue/packages/components/src/chat/PkToolRequestOAuthConnection.d.ts +5 -0
  107. package/dist-vue/packages/components/src/chat/index.d.ts +1 -0
  108. package/dist-vue/packages/components/src/chat/mcpOAuthReport.d.ts +11 -0
  109. package/dist-vue/packages/models/src/schema/AgentApiTool.d.ts +5 -8
  110. package/dist-vue/packages/models/src/schema/ConversationAnalysis.d.ts +6 -1
  111. package/dist-vue/packages/models/src/schema/Document.d.ts +478 -0
  112. package/dist-vue/packages/models/src/schema/Header.d.ts +39 -0
  113. package/dist-vue/packages/models/src/schema/McpServer.d.ts +76 -11
  114. package/dist-vue/packages/models/src/schema/Notification.d.ts +2 -0
  115. package/dist-vue/packages/models/src/schema/Organization.d.ts +23 -6
  116. package/dist-vue/packages/models/src/schema/SignInSecurity.d.ts +1 -1
  117. package/dist-vue/style.css +1 -1
  118. package/package.json +1 -1
  119. package/dist/_chunks/PkToolRequestOAuthConnection-CgQePKKb.js.map +0 -1
  120. package/dist/_chunks/markdown-BL7WANiX.js.map +0 -1
  121. package/dist-vue/_chunks/Document-nCWwJC2h.js.map +0 -1
  122. package/dist-vue/_chunks/PkChatbotInput-DcLmOqA1.js.map +0 -1
  123. package/dist-vue/_chunks/PkChatbotMessages-R1EsTJpM.js.map +0 -1
  124. package/dist-vue/_chunks/PkToolRequestOAuthConnection-a2LegFoE.js +0 -122
  125. package/dist-vue/_chunks/PkToolRequestOAuthConnection-a2LegFoE.js.map +0 -1
  126. package/dist-vue/_chunks/createChatbotApiClient-BlWs282E.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8wave/ai-elements",
3
3
  "private": false,
4
- "version": "0.115.0-beta.2",
4
+ "version": "0.115.0-beta.30",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite --port 8082 --open --host",
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolRequestOAuthConnection-CgQePKKb.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue","../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdown-BL7WANiX.js","names":[],"sources":["../../../../packages/models/src/schema/constants.ts","../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Document.ts","../../../../packages/utils/src/markdown.ts"],"sourcesContent":["/**\n * The models an agent of this platform may be pointed at.\n *\n * The gateway listing DESCRIBES what exists, this list PERMITS. They are two\n * different questions, and conflating them broke production once: on\n * 2026-07-23 two models entered the catalog before the installed\n * `@ai-sdk/gateway` knew them, and their tool calls failed under the generic\n * handling. Exposing every model the day the listing shows it would reproduce\n * that by construction.\n *\n * Adding one is a line here, after the checklist in\n * `docs/agents/llm-integration.md`: it must be `type: language` in the\n * listing, produce text and carry the `tool-use` tag. Everything else about\n * it, the commercial name, the price, the context window, the two privacy\n * flags, comes from the listing and is never written down twice.\n *\n * A test checks every id against a saved copy of the listing, which is what\n * replaces the by-hand verification that missed a model on 2026-09-18.\n */\nexport const GATEWAY_MODELS = [\n // OpenAI\n 'openai/gpt-5-nano',\n 'openai/gpt-5-mini',\n 'openai/gpt-5.1-thinking',\n 'openai/gpt-5.2',\n 'openai/gpt-5.4-nano',\n 'openai/gpt-5.4-mini',\n 'openai/gpt-5.4',\n 'openai/gpt-5.4-pro',\n 'openai/gpt-5.5',\n 'openai/gpt-5.5-pro',\n 'openai/gpt-5.6-luna',\n 'openai/gpt-5.6-terra',\n 'openai/gpt-5.6-sol',\n 'openai/gpt-5.6-luna-fast',\n 'openai/gpt-5.6-terra-fast',\n 'openai/gpt-5.6-sol-fast',\n 'openai/gpt-6-astra',\n 'openai/gpt-6-astra-fast',\n 'openai/gpt-6-sol',\n 'openai/gpt-6-sol-fast',\n 'openai/gpt-6-luna',\n 'openai/gpt-6-luna-fast',\n 'openai/gpt-6.1-sol',\n 'openai/gpt-6.1-sol-fast',\n // Google Gemini\n 'google/gemini-2.5-flash-lite',\n 'google/gemini-2.5-flash',\n 'google/gemini-2.5-pro',\n 'google/gemini-3-flash',\n 'google/gemini-3.1-pro-preview',\n 'google/gemini-3.1-flash-lite',\n 'google/gemini-3.5-flash',\n 'google/gemini-3.5-flash-lite',\n 'google/gemini-3.6-flash',\n 'google/gemini-3.7-flash',\n 'google/gemini-3.8-flash',\n // Anthropic Claude\n 'anthropic/claude-haiku-4.5',\n 'anthropic/claude-sonnet-4.6',\n 'anthropic/claude-sonnet-5',\n 'anthropic/claude-sonnet-5.5',\n 'anthropic/claude-opus-4.6',\n 'anthropic/claude-opus-4.7',\n 'anthropic/claude-opus-4.8',\n 'anthropic/claude-opus-5',\n 'anthropic/claude-opus-5-fast',\n 'anthropic/claude-opus-5.5',\n 'anthropic/claude-opus-5.5-fast',\n 'anthropic/claude-fable-5',\n 'anthropic/claude-fable-5.1',\n // Deepseek\n 'deepseek/deepseek-v3.2',\n 'deepseek/deepseek-v4-flash-0731',\n 'deepseek/deepseek-v4.1-flash',\n 'deepseek/deepseek-v4-pro-0813',\n // Alibaba\n 'alibaba/qwen3.6-plus',\n 'alibaba/qwen3.7-max',\n 'alibaba/qwen3.7-plus',\n 'alibaba/qwen3.7-flash',\n 'alibaba/qwen3.8-flash',\n 'alibaba/qwen3.8-max',\n 'alibaba/qwen3.8-27b',\n 'alibaba/qwen3.8-max-0902',\n 'alibaba/qwen3.8-omni-flash',\n 'alibaba/qwen3.8-max-prime',\n // SpaceXAI\n 'spacexai/grok-4.3',\n 'spacexai/grok-4.6',\n 'spacexai/grok-4.7',\n // z.ai\n 'zai/glm-5.2',\n 'zai/glm-5.2-fast',\n 'zai/glm-5.3',\n 'zai/glm-5.3-flash',\n 'zai/glm-5.3-fast',\n 'zai/glm-5.3-flashx',\n // Meta\n 'meta/muse-spark-1.1',\n 'meta/muse-spark-1.2',\n 'meta/muse-spark-1.3',\n // Moonshot AI\n 'moonshotai/kimi-k3',\n 'moonshotai/kimi-k3-fast',\n // Mistral\n 'mistral/ministral-14b',\n 'mistral/mistral-small',\n 'mistral/mistral-medium-3.5',\n 'mistral/mistral-large-3',\n // Xiaomi\n 'xiaomi/mimo-v2.6-flash',\n 'xiaomi/mimo-v2.6-pro',\n 'xiaomi/mimo-v2.6-pro-ultraspeed',\n] as const satisfies readonly string[]\n\n/**\n * A model id: an entry of {@link GATEWAY_MODELS}, or a `byok/` id served by an\n * organization's own endpoint. A plain string on purpose, because the second\n * half cannot be enumerated.\n */\nexport type ModelId = string\n\n/**\n * Prices are stored per token, the unit the gateway listing publishes, and\n * shown per million, the unit every price page uses. One copy, so the page,\n * its dialogs and the picker never disagree on the factor.\n */\nexport const PER_MILLION_TOKENS = 1_000_000\n\n/**\n * The models the platform falls back to when an organization has expressed no\n * preference. The only model ids written in the code besides the curated list.\n */\nexport const PLATFORM_MODEL_DEFAULTS = {\n /** New agents, and the runtime fallback of an agent with no model. */\n agentModel: 'openai/gpt-5.6-terra',\n /** Question reformulation, JSON to schema, memory reconciliation. */\n utilityModel: 'google/gemini-3-flash',\n} as const\n\n/**\n * Models used to vectorize text, never to answer. Kept apart from\n * {@link GATEWAY_MODELS} so an embedding id cannot reach the chat plumbing,\n * and out of the catalog for the same reason: they are not selectable.\n */\nexport enum EmbeddingModel {\n textEmbedding3Small = 'openai/text-embedding-3-small',\n textEmbedding3Large = 'openai/text-embedding-3-large',\n geminiEmbedding2 = 'google/gemini-embedding-2',\n}\n\nexport const EMBED_MODEL = EmbeddingModel.geminiEmbedding2\n\n/**\n * Model that answers typed questions (yes/no, choice, score) with calibrated\n * probabilities instead of text, through the gateway's `experimental_evaluate`\n * (TypeSafe Jev). Out of the catalog like the embeddings: it decides for the\n * platform, it never answers the user. Its gateway listing reports no ZDR,\n * yet a ZDR request is served, routed to TypeSafe alone (measured\n * 2026-09-28, scripts/spikeJevEvaluation.ts).\n */\nexport const EVALUATION_MODEL = 'typesafe-ai/jev'\n\n/**\n * Supported languages for documents and revised answers\n */\nexport enum SupportedLanguage {\n it = 'it',\n en = 'en',\n es = 'es',\n fr = 'fr',\n de = 'de',\n}\n\n/**\n * Language display names (i18n keys)\n */\nexport const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {\n [SupportedLanguage.it]: 'language.italian',\n [SupportedLanguage.en]: 'language.english',\n [SupportedLanguage.es]: 'language.spanish',\n [SupportedLanguage.fr]: 'language.french',\n [SupportedLanguage.de]: 'language.german',\n}\n\n/**\n * Content extraction strategies for URL scraping\n */\nexport enum ExtractionStrategy {\n /** Use Mozilla Readability for content extraction (fast, free, no API calls) */\n READABILITY = 'readability',\n /** Use AI-based extraction (better for complex layouts, requires API calls) */\n AI = 'ai',\n}\n\n/**\n * Browser engine options for URL scraping\n */\nexport enum BrowserEngine {\n /** Use simple HTTP fetch (fast, lightweight, no JavaScript support) */\n FETCH = 'fetch',\n /** Use Puppeteer with headless Chrome (full browser, JavaScript support) */\n PUPPETEER = 'puppeteer',\n /** Delegate browser rendering to an external pdf-image-generator service */\n REMOTE = 'remote',\n}\n\n/**\n * Which report family has to account for a credit charge.\n *\n * The hourly reconciliation trusts `cost_reports` (the gateway's own billing,\n * copied in house) as the source of truth for what an organization spent. A\n * charge whose supplier is not the gateway has no counterpart there, so it must\n * be told apart or the reconciliation forgives it: that is what happened to the\n * voice minutes until 2026-08-31.\n *\n * Declared here, written on the ledger row at charge time, never inferred from\n * `source` downstream: the next non-gateway supplier must be a compile-time\n * decision, not a string nobody remembered to add to a list.\n */\nexport enum BillingSource {\n /** Billed by the AI gateway, so it lands in `cost_reports`. */\n GATEWAY = 'gateway',\n /** Billed by another supplier (voice minutes, OCR): the ledger is the only record. */\n INTERNAL = 'internal',\n}\n\n/**\n * Kinds of row on the credit ledger.\n *\n * It used to be a free string with exactly one value ever written, `debit`.\n * Adding a second one is not free: the hourly reconciliation sums debits to\n * rebuild the real-time estimate, and the analytics endpoint sums the ledger to\n * chart spend, so a new type that nobody filters out is a number silently\n * doubled.\n */\nexport enum CreditTransactionType {\n /** A charge for something consumed, written in real time. */\n DEBIT = 'debit',\n /** The hourly settlement against the gateway report: the audit trail of a balance move. */\n RECONCILIATION = 'reconciliation',\n}\n\n/**\n * Prefix that marks a model served by the organization's own OpenAI compatible\n * endpoint rather than by the gateway.\n *\n * It is not a gateway brand, which is the point: every switch on the provider\n * (reasoning options, the Google thought signature repair, the per-provider\n * timeouts, the native web search tools) falls into its default branch without\n * any of them having to learn about BYOK.\n */\nexport const BYOK_MODEL_PREFIX = 'byok/'\n\nexport const isByokModel = (id?: string | null): boolean =>\n Boolean(id?.startsWith(BYOK_MODEL_PREFIX))\n\n/**\n * The id as the endpoint wants it, with the prefix removed. The remainder may\n * itself contain slashes (`byok/meta-llama/Llama-3.3-70B`), so this cuts the\n * prefix and does not split.\n */\nexport const byokModelId = (id: string): string =>\n id.startsWith(BYOK_MODEL_PREFIX) ? id.slice(BYOK_MODEL_PREFIX.length) : id\n","import * as z from 'zod'\nimport { decodeOnly } from '../utils'\n\nexport const HeaderSchema = z.object({\n key: z.string().optional(),\n value: z.string().optional(),\n})\n\nexport const HeadesSchema = decodeOnly(\n z.array(HeaderSchema),\n z.array(z.object({ key: z.string(), value: z.string() })),\n (headers) =>\n headers?.filter(\n (header) => header.key?.trim() && header.value?.trim(),\n ) as Array<{ key: string; value: string }>,\n)\n\nexport type Header = z.infer<typeof HeaderSchema>\nexport type Headers = z.infer<typeof HeadesSchema>\n","import * as z from 'zod'\nimport { zodQs, decodeOnly } from '../utils'\nimport {\n SupportedLanguage,\n ExtractionStrategy,\n BrowserEngine,\n} from './constants'\nimport { MediaSchema } from './Media'\nimport { HeadesSchema } from './Header'\n\n/**\n * Processing status for document indexing and embedding generation\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n// ===== Document Source Types =====\n\n/**\n * Conversion options for document processing\n * Used when creating or reprocessing documents\n */\nexport const ConversionOptionsSchema = z.object({\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z\n .enum(BrowserEngine)\n .optional()\n .default(BrowserEngine.FETCH),\n headers: HeadesSchema.nullish(),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n language: z.string().optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n})\n\nexport type ConversionOptions = z.infer<typeof ConversionOptionsSchema>\n\n// ===== Document Source (metadata.source) =====\n\n/**\n * Who keeps a document's content up to date.\n *\n * `web` is a page the platform re-reads on its own; the others are cloud\n * document sources. Distinct from `documents.source_provider`, which carries\n * only the providers that OWN their documents (the cloud ones) and is null for\n * everything else: a web page discovered by a source stays editable.\n */\nexport enum DocumentSourceProvider {\n Web = 'web',\n SharePoint = 'sharepoint',\n GoogleDrive = 'google-drive',\n}\n\n/**\n * Outcome of the last pass over a document's source.\n *\n * `unchanged` is the one that pays for the whole mechanism: the source was read\n * and produced the same text, so no chunk was rewritten and no embedding was\n * charged. `shrunk` is a refusal, not a failure: the page answered but with far\n * less content than before, so the good version was kept.\n */\nexport enum DocumentRefreshResult {\n Updated = 'updated',\n Unchanged = 'unchanged',\n Failed = 'failed',\n Shrunk = 'shrunk',\n Gone = 'gone',\n}\n\n/**\n * How often a document re-reads its source on its own.\n *\n * `null` is the fourth value and the default: manual, which is what every\n * document is today and what every type other than `url` can ever be.\n */\nexport enum DocumentRefreshInterval {\n Daily = 'daily',\n Weekly = 'weekly',\n Monthly = 'monthly',\n}\n\n/**\n * The Postgres interval each trigger advances by.\n *\n * One table, read by the two places that move a deadline: the service, when\n * somebody sets or changes the trigger, and the timer, when a due document has\n * been queued. Written as literals rather than composed at the call site\n * because they end up inside SQL.\n */\nexport const REFRESH_INTERVAL_SQL: Record<DocumentRefreshInterval, string> = {\n [DocumentRefreshInterval.Daily]: '1 day',\n [DocumentRefreshInterval.Weekly]: '7 days',\n [DocumentRefreshInterval.Monthly]: '1 month',\n}\n\n/**\n * Hours after which a document left `processing` is treated as abandoned.\n *\n * A killed worker leaves that status behind, and nothing else moves it: the row\n * would drop out of the refresh timer's selection for good, because the timer\n * only claims documents that are `success` or `failed`. Past this window the\n * timer takes it anyway, and a manual refresh stops being refused.\n *\n * Generous on purpose: a scrape is bounded by the Functions host timeout, so\n * anything still `processing` after six hours is not running.\n */\nexport const STALE_PROCESSING_HOURS = 6\n\nexport const DocumentLastRefreshSchema = z.object({\n at: z.string(),\n result: z.enum(DocumentRefreshResult),\n httpStatus: z.number().optional(),\n error: z.string().optional(),\n /** Reset by the first good outcome; the notification threshold reads it. */\n consecutiveFailures: z.number().optional(),\n /**\n * Words before and after, written by the shrink guard. They are kept rather\n * than only compared because the guard's thresholds are a starting point:\n * after a month of real pages one can tell whether they cut too much.\n */\n previousWordCount: z.number().optional(),\n newWordCount: z.number().optional(),\n})\n\nexport type DocumentLastRefresh = z.infer<typeof DocumentLastRefreshSchema>\n\n/** Fields every source branch shares, whatever keeps the document up to date. */\nconst commonSourceFields = {\n /**\n * Hash of the text that actually reaches the chunks, so an unchanged source\n * costs no chunking and no embedding. Written by the processor on every\n * pass, which is what lets a document imported before this existed start\n * comparing from its second pass on.\n */\n contentHash: z.string().optional(),\n lastRefresh: DocumentLastRefreshSchema.optional(),\n}\n\nconst WebDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.Web),\n url: z.string(),\n // Written by a successful conversion, and only then: at insert time the\n // page has not been read yet, so a value here would attest a scrape that\n // never happened.\n lastScrapedAt: z.string().optional(),\n /**\n * Hash of the deterministic text handed to the extraction step, before the\n * model. With `extractionStrategy: 'ai'` the model's output differs on every\n * call, so comparing the final markdown would always say \"changed\" and\n * every interval would pay conversion, chunking and embedding. This is what\n * the AI path compares, and it decides whether to call the model at all.\n */\n extractionInputHash: z.string().optional(),\n /**\n * Where the last successful pass actually landed after redirects.\n *\n * The shrink guard compares this with the new final URL, and not with `url`\n * on purpose: plenty of sites always redirect (a locale prefix, a trailing\n * slash, http to https), so comparing against the stored URL would refuse\n * every update of those pages forever. What says a page is gone is its\n * destination changing from the one it has been landing on.\n */\n finalUrl: z.string().optional(),\n /**\n * The web source that discovered this page. It says who found it, not who\n * governs it: the page stays editable like any other. Editing the text by\n * hand takes it off, and the page stops belonging to the source.\n */\n sourceId: z.uuid().optional(),\n /**\n * The last run of that source that found the page listed.\n *\n * Not the last successful read of the page, which is `lastScrapedAt`:\n * this is the marker the removal option reads, named like the cloud\n * field because the same reconciliation reads both. A page without one is\n * never removed.\n */\n lastSyncedAt: z.string().optional(),\n /**\n * The `<lastmod>` the sitemap declared the last time the source read it.\n * A change that is newer than `lastScrapedAt` queues an early refresh.\n */\n lastmod: z.string().optional(),\n ...commonSourceFields,\n})\n\n/** Coordinates a cloud adapter needs to find the item again, plus its content fingerprint. */\nconst cloudSourceFields = {\n /** The `document_sync_sources` row that owns this document. */\n sourceId: z.uuid(),\n remoteId: z.string(),\n webUrl: z.string(),\n path: z.string(),\n /**\n * The provider's own content fingerprint, compared for equality only and\n * never interpreted. It is what lets a sync skip the download entirely.\n */\n fingerprint: z.string(),\n /** Marks the document as seen by a run: the reconciliation after a resync reads it. */\n lastSyncedAt: z.string(),\n /** When the provider says the item last changed. Shown, never compared: the fingerprint decides. */\n lastModified: z.string().optional(),\n /**\n * A newly downloaded blob waiting to be converted. It sits here instead of\n * replacing `metadata.media` so that a conversion failure cannot destroy the\n * version that works: the processor promotes it only on success.\n */\n candidate: z\n .object({\n media: MediaSchema,\n fingerprint: z.string(),\n name: z.string(),\n webUrl: z.string(),\n path: z.string(),\n lastModified: z.string().optional(),\n })\n .optional(),\n ...commonSourceFields,\n}\n\nconst SharePointDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.SharePoint),\n ...cloudSourceFields,\n})\n\nconst GoogleDriveDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.GoogleDrive),\n ...cloudSourceFields,\n})\n\n/**\n * `metadata.source`, discriminated on `provider`.\n *\n * One object cannot hold both shapes: a web page has a URL and a scrape date, a\n * cloud file has remote coordinates and a fingerprint, and making every field\n * optional to fit both is the same as having no schema. Rows written before the\n * discriminator existed carry no `provider`, so they are normalized to the web\n * branch before routing: that is the only shape that could have been written.\n *\n * The normalization runs on parse, and responses are serialized by the\n * parse-based compiler (see `apps/backend/src/utils/responseSerializer.ts`), so\n * a legacy row still comes out of the API intact.\n */\nexport const DocumentSourceSchema = z.preprocess(\n (value) => {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !('provider' in value)\n ) {\n return { provider: DocumentSourceProvider.Web, ...value }\n }\n return value\n },\n z.discriminatedUnion('provider', [\n WebDocumentSourceSchema,\n SharePointDocumentSourceSchema,\n GoogleDriveDocumentSourceSchema,\n ]),\n)\n\nexport type DocumentSource = z.infer<typeof DocumentSourceSchema>\nexport type WebDocumentSource = z.infer<typeof WebDocumentSourceSchema>\n\n/**\n * The page URL of a document's source, when its source is a web page.\n *\n * One place to narrow the union, so the views that show \"where this came from\"\n * do not each grow their own check, and a cloud-synced document returns nothing\n * here instead of a broken link: its address is `webUrl` on the remote item,\n * which is a different thing to show and to say.\n */\nexport function documentSourceUrl(source?: DocumentSource): string | undefined {\n return source && 'url' in source ? source.url : undefined\n}\n\n// ===== Processing queue contract =====\n\n/**\n * Why a document is being processed again. Absent means the ingestion of a new\n * document, which is the only case whose natural key is the document id alone.\n */\nexport type DocumentProcessingKind = 'refresh' | 'reprocess' | 'reindex'\n\n/**\n * The `document-processing` queue message.\n *\n * Lives here because two processes read it: the backend publishes it and the\n * Azure Function consumes it. It used to be declared in each, and the copy in\n * the function app was already a field behind.\n */\nexport interface DocumentProcessingJob {\n documentId: string\n conversionOptions?: ConversionOptions\n /**\n * Set by every caller that processes a document which already exists. It\n * shapes the message id (see `publishProcessingJob`) and, for `refresh`,\n * turns on the refresh mode of the processor.\n */\n kind?: DocumentProcessingKind\n /**\n * Only a scheduled refresh enforces the shrink guard. A person who clicked\n * \"refresh now\" is looking at the result and may well be refreshing a page\n * that really did get shorter, so the guard would only stand in their way.\n */\n guarded?: boolean\n /**\n * The web source whose sitemap asked for this early refresh. Nobody is\n * watching it either, so it enforces the shrink guard; and it is void if\n * the page no longer belongs to that source, which is what editing its text\n * by hand does. Not `guarded`, which also voids a page without a trigger:\n * a source reads its pages whether they have one or not.\n */\n sourceId?: string\n}\n\n/**\n * Metadata structure for document.metadata column\n * Consolidates all metadata from converters and source info\n */\nexport const DocumentMetadataSchema = z\n .object({\n // === Source Info (URL and synced documents) ===\n source: DocumentSourceSchema.optional(),\n\n // === Media Info (File documents only) ===\n media: MediaSchema.optional(),\n\n // === Common Fields (all document types) ===\n title: z.string().optional(),\n author: z.string().optional(),\n description: z.string().optional(),\n summary: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n\n // === PDF Specific ===\n subject: z.string().optional(),\n creator: z.string().optional(), // Original application\n producer: z.string().optional(), // PDF generator software\n creationDate: z.string().optional(),\n modificationDate: z.string().optional(),\n pageCount: decodeOnly(\n z.union([z.number(), z.array(z.unknown())]),\n z.number(),\n (val) => (Array.isArray(val) ? val.length : val),\n ).optional(),\n encrypted: z.boolean().optional(),\n\n // === DOCX Specific ===\n characterCount: z.number().optional(),\n lineCount: z.number().optional(),\n\n // === URL Specific ===\n siteName: z.string().optional(),\n canonicalUrl: z.url().optional(),\n imageUrl: z.url().optional(),\n favicon: z.url().optional(),\n type: z.string().optional(), // og:type (article, website, etc.)\n locale: z.string().optional(),\n tags: z.array(z.string()).optional(),\n publishedTime: z.string().optional(),\n modifiedTime: z.string().optional(),\n\n // === Metrics (all document types) ===\n wordCount: z.number().optional(),\n paragraphCount: z.number().optional(),\n readingTime: z.number().optional(), // in minutes\n\n // === OCR (PDF documents) ===\n ocrUsed: z.boolean().optional(),\n ocrProvider: z.string().optional(),\n ocrModel: z.string().optional(),\n ocrPages: z.string().optional(),\n ocrCost: z.number().optional(),\n ocrError: z.string().optional(),\n\n // === RAG / Duplicates / Sync Info ===\n chunksCount: z.number().optional(),\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n qdrantSynced: z.boolean().optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n })\n .loose() // Allow additional custom fields\n\nexport type DocumentMetadata = z.infer<typeof DocumentMetadataSchema>\n\n/**\n * Document type enum\n *\n * - 'file': Document created from uploaded file (PDF, DOCX)\n * - 'url': Document created from web scraping\n * - 'revised-answer': Document created from revised AI answer\n * - 'plain-text': Document created from plain text input\n */\nexport enum DocumentType {\n File = 'file',\n Url = 'url',\n RevisedAnswer = 'revised-answer',\n PlainText = 'plain-text',\n}\n\n// ===== Schemas =====\n\n/**\n * Full document schema from database\n * Extended with computed field chunksCount\n */\nconst BaseDocumentSchema = z.object({\n id: z.string(),\n externalId: z.string().nullable(),\n name: z.string(),\n content: z.string(),\n type: z.string(),\n language: z.string(),\n metadata: z.record(z.string(), z.unknown()),\n indexStatus: z.string(),\n processedAt: z.coerce.date().nullable(),\n createdAt: z.coerce.date(),\n createdBy: z.string(),\n updatedAt: z.coerce.date(),\n updatedBy: z.string(),\n deleted: z.boolean(),\n deletedAt: z.coerce.date().nullable(),\n deletedBy: z.string().nullable(),\n organizationId: z.string(),\n priority: z.number().int().min(1).max(5).default(3),\n /** The automatic refresh trigger; null is manual, and only `url` can have one. */\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n /**\n * When the trigger next comes due. Null with an interval set means paused:\n * the source answered that the page is gone for good.\n */\n nextRefreshAt: z.coerce.date().nullable().optional(),\n /**\n * Which sync source owns this document, when one does.\n *\n * Null is the ordinary case. A non-null value means a job keeps the\n * document up to date, which is what makes it read only: editing it, moving\n * it or deleting it by hand would be undone at the next run, silently.\n * Written only by providers that own their documents, never by a crawl that\n * merely discovered a page.\n */\n sourceProvider: z.enum(DocumentSourceProvider).nullable().optional(),\n})\n\nexport const DocumentSchema = BaseDocumentSchema.extend({\n type: z.enum(DocumentType),\n indexStatus: z.enum(IndexStatus),\n metadata: DocumentMetadataSchema,\n // Add computed field for chunks count (populated by service layer)\n chunksCount: z.number().optional(),\n // Computed fields for redundancy tracking\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n // Qdrant sync status\n qdrantSynced: z.boolean().optional(),\n // Agent IDs associated with this document (populated in single-item reads)\n agentIds: z.array(z.string()).optional(),\n // Folder ID for this document within an agent context (populated in agent-scoped reads)\n folderId: z.string().nullable().optional(),\n // Full folder path string e.g. \"Parent / Child\" (populated in list reads when agentId is set)\n folderPath: z.string().nullable().optional(),\n})\n\n/**\n * Query parameters for listing documents with pagination, sorting, and filters\n */\nexport const DocumentQuerystringSchema = z.object({\n ...zodQs.pagination(),\n ...zodQs.sort(['id', 'updatedAt', 'createdAt', 'name']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n ...zodQs.fullText(),\n ...zodQs.ids(),\n 'filter[similarity]': z.string().optional(),\n // UUID = documents in that folder; 'null' = documents at root (no folder); absent = all documents\n 'filter[folderId]': z.string().optional(),\n 'filter[hasRedundantChunks]': z.string().optional(),\n // One of the intervals, 'null' for the documents left on manual, 'auto' for\n // any of the three. Same convention as `filter[folderId]`, and handled\n // outside `getFilters` for the same reason: `inArray` cannot say IS NULL.\n 'filter[refreshInterval]': z.string().optional(),\n // A provider, 'null' for the documents nobody syncs, 'any' for the ones a\n // source owns whoever it is. Same convention and the same reason as above.\n 'filter[sourceProvider]': z.string().optional(),\n})\n\n/**\n * Document creation schema for POST /documents endpoint\n *\n * Supports three types:\n * - 'file': Requires media object with uploaded file info\n * - 'url': Requires url string for web scraping\n * - 'revised-answer': Requires content (created from revisedAnswer service)\n */\nexport const DocumentCreateDtoSchema = z\n .object({\n type: z.enum(DocumentType).default(DocumentType.File),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n organizationId: z.string(),\n agentIds: z.array(z.string()).min(1, 'At least one agent is required'),\n folderId: z.uuid().nullable().optional(),\n media: MediaSchema.optional(),\n url: z.url().optional(),\n content: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z.enum(BrowserEngine).optional(),\n headers: HeadesSchema.nullish().default([]),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n priority: z.number().int().min(1).max(5).default(3).optional(),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n /**\n * The web source this page is imported for: the pages ticked in the\n * crawler dialog when it also creates a source. They land in the\n * source's folder as pages the source discovered, so its removal\n * option and its early refresh apply to them from the first run.\n */\n syncSourceId: z.uuid().optional(),\n })\n .superRefine((data, ctx) => {\n if (data.syncSourceId && data.type !== DocumentType.Url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.syncSourceUrlOnly',\n },\n path: ['syncSourceId'],\n })\n }\n if (data.type === DocumentType.File && !data.media) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['media'],\n })\n return\n }\n if (data.type === DocumentType.Url && !data.url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['url'],\n })\n }\n // The database has the same CHECK, but a 400 saying which field is\n // wrong beats a constraint violation surfacing as a 500.\n if (data.refreshInterval && data.type !== DocumentType.Url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.refreshIntervalUrlOnly',\n },\n path: ['refreshInterval'],\n })\n }\n if (\n data.type === DocumentType.PlainText &&\n !(data.name || data.language)\n ) {\n if (!data.name) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['name'],\n })\n }\n if (!data.language) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['language'],\n })\n }\n }\n })\n\n/**\n * Document update schema for PUT /documents/:id endpoint\n *\n * All fields are optional, but at least one must be provided\n */\nexport const DocumentUpdateDtoSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n content: z.string().optional(),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n priority: z.number().int().min(1).max(5).optional(),\n agentIds: z.array(z.string()).min(1).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n})\n\n/** Body of `PATCH /documents/:id/refresh-interval`, twin of the priority route. */\nexport const DocumentRefreshIntervalDtoSchema = z.object({\n refreshInterval: z.enum(DocumentRefreshInterval).nullable(),\n})\n\n/** Body of `POST /documents/bulk-refresh-interval`. */\nexport const DocumentBulkRefreshIntervalDtoSchema = z.object({\n ids: z.array(z.uuid()).min(1).max(100),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable(),\n})\n\n/** Body of `POST /documents/bulk-refresh`, twin of `bulk-reprocess`. */\nexport const DocumentBulkRefreshDtoSchema = z.object({\n ids: z.array(z.uuid()).min(1).max(100),\n})\n\n// ===== Type Exports =====\n\nexport type Document = z.infer<typeof DocumentSchema>\nexport type DocumentQuerystring = z.infer<typeof DocumentQuerystringSchema>\nexport type DocumentCreateDto = z.infer<typeof DocumentCreateDtoSchema>\nexport type DocumentUpdateDto = z.infer<typeof DocumentUpdateDtoSchema>\nexport type DocumentRefreshIntervalDto = z.infer<\n typeof DocumentRefreshIntervalDtoSchema\n>\nexport type DocumentBulkRefreshIntervalDto = z.infer<\n typeof DocumentBulkRefreshIntervalDtoSchema\n>\nexport type DocumentBulkRefreshDto = z.infer<\n typeof DocumentBulkRefreshDtoSchema\n>\n\n// ===== Helper Functions =====\n\nexport const getStatusColor = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'success'\n case IndexStatus.Failed:\n return 'danger'\n case IndexStatus.Processing:\n return 'warning'\n default:\n return 'info'\n }\n}\n\nexport const getStatusIcon = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'ri:check-line'\n case IndexStatus.Failed:\n return 'ri:error-warning-line'\n case IndexStatus.Processing:\n return 'ri:hourglass-line'\n default:\n return 'ri:time-line'\n }\n}\n\nexport const getTypeIcon = (type: DocumentType) => {\n switch (type) {\n case DocumentType.File:\n return 'ri:file-line'\n case DocumentType.Url:\n return 'ri:link'\n case DocumentType.RevisedAnswer:\n return 'ri:feedback-line'\n default:\n return 'ri:input-method-line'\n }\n}\n\nexport const getFileIcon = (extension: string) => {\n const ext = (\n extension.startsWith('.') ? extension.slice(1) : extension\n ).toLowerCase()\n switch (ext) {\n case 'pdf':\n return 'ri:file-pdf-2-line'\n case 'doc':\n case 'docx':\n return 'ri:file-word-line'\n case 'xls':\n case 'xlsx':\n return 'ri:file-excel-line'\n case 'ppt':\n case 'pptx':\n return 'ri:file-ppt-line'\n case 'txt':\n return 'ri:file-text-line'\n case 'mp3':\n case 'wav':\n case 'ogg':\n return 'ri:file-music-line'\n case 'mp4':\n case 'avi':\n case 'mkv':\n return 'ri:file-video-line'\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'gif':\n case 'bmp':\n case 'svg':\n return 'ri:file-image-line'\n case 'zip':\n case 'rar':\n case '7z':\n return 'ri:file-zip-line'\n default:\n return 'ri:file-line'\n }\n}\n\n/** Whether a MIME type is an image. */\nexport const isImageMime = (mimeType?: string | null): boolean =>\n (mimeType ?? '').startsWith('image/')\n\n/** Whether a file MIME type can be previewed inline in the browser (image or\n * PDF) instead of being forced as a download. Shared by every file-list surface\n * so the preview/download decision stays consistent. */\nexport const isInlineViewable = (mimeType?: string | null): boolean =>\n isImageMime(mimeType) || mimeType === 'application/pdf'\n\n/** File-type identity for list rows and previews: icon, accent color\n * (CSS value driving glyph and tile tint) and a short human kind label. */\nexport interface FileKind {\n icon: string\n accent: string\n label: string\n}\n\n/** File-type identity derived from a MIME type. Accents match the chat\n * attachment cards (PkChatbotFilePreview) so files read the same everywhere. */\nexport const fileKindForMime = (mimeType?: string | null): FileKind => {\n const mime = mimeType ?? ''\n if (isImageMime(mime)) {\n return { icon: 'ri:image-line', accent: '#ec4899', label: 'Image' }\n }\n if (mime === 'application/pdf') {\n return { icon: 'ri:file-pdf-2-line', accent: '#e2483d', label: 'PDF' }\n }\n if (mime === 'application/msword' || mime.includes('wordprocessingml')) {\n return { icon: 'ri:file-word-line', accent: '#2b7de9', label: 'Word' }\n }\n if (mime === 'application/vnd.ms-excel' || mime.includes('spreadsheetml')) {\n return {\n icon: 'ri:file-excel-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n }\n }\n if (\n mime === 'application/vnd.ms-powerpoint' ||\n mime.includes('presentationml')\n ) {\n return {\n icon: 'ri:file-ppt-line',\n accent: '#d98324',\n label: 'Presentation',\n }\n }\n if (mime.startsWith('audio/')) {\n return { icon: 'ri:file-music-line', accent: '#8b5cf6', label: 'Audio' }\n }\n if (mime.startsWith('video/')) {\n return { icon: 'ri:file-video-line', accent: '#6366f1', label: 'Video' }\n }\n if (\n mime === 'application/zip' ||\n mime === 'application/x-rar-compressed' ||\n mime === 'application/x-7z-compressed'\n ) {\n return { icon: 'ri:file-zip-line', accent: '#c08a1e', label: 'Archive' }\n }\n if (mime.startsWith('text/')) {\n return { icon: 'ri:file-text-line', accent: '#64748b', label: 'Text' }\n }\n return { icon: 'ri:file-line', accent: '#64748b', label: 'File' }\n}\n\n/** Icon for a file-list row, derived from its MIME type. */\nexport const fileIconForMime = (mimeType?: string | null): string =>\n fileKindForMime(mimeType).icon\n","export const stripMarkdown = (text?: string | null) => {\n if (!text) {\n return undefined\n }\n return text\n .replace(/!\\[.*?\\]\\(.*?\\)/g, '') // images\n .replace(/\\[([^\\]]+)\\]\\(.*?\\)/g, '$1') // links\n .replace(/`{1,3}[^`]*`{1,3}/g, '') // inline code / code blocks\n .replace(/#{1,6}\\s/g, '') // headings\n .replace(/(\\*{1,3}|_{1,3})(.*?)\\1/g, '$2') // bold / italic\n .replace(/~~(.*?)~~/g, '$1') // strikethrough\n .replace(/^\\s*[-*+>]\\s+/gm, '') // lists and blockquotes\n .replace(/\\n+/g, ' ') // newlines\n .trim()\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Document-nCWwJC2h.js","names":[],"sources":["../../../../packages/models/src/schema/constants.ts","../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Document.ts"],"sourcesContent":["/**\n * The models an agent of this platform may be pointed at.\n *\n * The gateway listing DESCRIBES what exists, this list PERMITS. They are two\n * different questions, and conflating them broke production once: on\n * 2026-07-23 two models entered the catalog before the installed\n * `@ai-sdk/gateway` knew them, and their tool calls failed under the generic\n * handling. Exposing every model the day the listing shows it would reproduce\n * that by construction.\n *\n * Adding one is a line here, after the checklist in\n * `docs/agents/llm-integration.md`: it must be `type: language` in the\n * listing, produce text and carry the `tool-use` tag. Everything else about\n * it, the commercial name, the price, the context window, the two privacy\n * flags, comes from the listing and is never written down twice.\n *\n * A test checks every id against a saved copy of the listing, which is what\n * replaces the by-hand verification that missed a model on 2026-09-18.\n */\nexport const GATEWAY_MODELS = [\n // OpenAI\n 'openai/gpt-5-nano',\n 'openai/gpt-5-mini',\n 'openai/gpt-5.1-thinking',\n 'openai/gpt-5.2',\n 'openai/gpt-5.4-nano',\n 'openai/gpt-5.4-mini',\n 'openai/gpt-5.4',\n 'openai/gpt-5.4-pro',\n 'openai/gpt-5.5',\n 'openai/gpt-5.5-pro',\n 'openai/gpt-5.6-luna',\n 'openai/gpt-5.6-terra',\n 'openai/gpt-5.6-sol',\n 'openai/gpt-5.6-luna-fast',\n 'openai/gpt-5.6-terra-fast',\n 'openai/gpt-5.6-sol-fast',\n 'openai/gpt-6-astra',\n 'openai/gpt-6-astra-fast',\n 'openai/gpt-6-sol',\n 'openai/gpt-6-sol-fast',\n 'openai/gpt-6-luna',\n 'openai/gpt-6-luna-fast',\n 'openai/gpt-6.1-sol',\n 'openai/gpt-6.1-sol-fast',\n // Google Gemini\n 'google/gemini-2.5-flash-lite',\n 'google/gemini-2.5-flash',\n 'google/gemini-2.5-pro',\n 'google/gemini-3-flash',\n 'google/gemini-3.1-pro-preview',\n 'google/gemini-3.1-flash-lite',\n 'google/gemini-3.5-flash',\n 'google/gemini-3.5-flash-lite',\n 'google/gemini-3.6-flash',\n 'google/gemini-3.7-flash',\n 'google/gemini-3.8-flash',\n // Anthropic Claude\n 'anthropic/claude-haiku-4.5',\n 'anthropic/claude-sonnet-4.6',\n 'anthropic/claude-sonnet-5',\n 'anthropic/claude-sonnet-5.5',\n 'anthropic/claude-opus-4.6',\n 'anthropic/claude-opus-4.7',\n 'anthropic/claude-opus-4.8',\n 'anthropic/claude-opus-5',\n 'anthropic/claude-opus-5-fast',\n 'anthropic/claude-opus-5.5',\n 'anthropic/claude-opus-5.5-fast',\n 'anthropic/claude-fable-5',\n 'anthropic/claude-fable-5.1',\n // Deepseek\n 'deepseek/deepseek-v3.2',\n 'deepseek/deepseek-v4-flash-0731',\n 'deepseek/deepseek-v4.1-flash',\n 'deepseek/deepseek-v4-pro-0813',\n // Alibaba\n 'alibaba/qwen3.6-plus',\n 'alibaba/qwen3.7-max',\n 'alibaba/qwen3.7-plus',\n 'alibaba/qwen3.7-flash',\n 'alibaba/qwen3.8-flash',\n 'alibaba/qwen3.8-max',\n 'alibaba/qwen3.8-27b',\n 'alibaba/qwen3.8-max-0902',\n 'alibaba/qwen3.8-omni-flash',\n 'alibaba/qwen3.8-max-prime',\n // SpaceXAI\n 'spacexai/grok-4.3',\n 'spacexai/grok-4.6',\n 'spacexai/grok-4.7',\n // z.ai\n 'zai/glm-5.2',\n 'zai/glm-5.2-fast',\n 'zai/glm-5.3',\n 'zai/glm-5.3-flash',\n 'zai/glm-5.3-fast',\n 'zai/glm-5.3-flashx',\n // Meta\n 'meta/muse-spark-1.1',\n 'meta/muse-spark-1.2',\n 'meta/muse-spark-1.3',\n // Moonshot AI\n 'moonshotai/kimi-k3',\n 'moonshotai/kimi-k3-fast',\n // Mistral\n 'mistral/ministral-14b',\n 'mistral/mistral-small',\n 'mistral/mistral-medium-3.5',\n 'mistral/mistral-large-3',\n // Xiaomi\n 'xiaomi/mimo-v2.6-flash',\n 'xiaomi/mimo-v2.6-pro',\n 'xiaomi/mimo-v2.6-pro-ultraspeed',\n] as const satisfies readonly string[]\n\n/**\n * A model id: an entry of {@link GATEWAY_MODELS}, or a `byok/` id served by an\n * organization's own endpoint. A plain string on purpose, because the second\n * half cannot be enumerated.\n */\nexport type ModelId = string\n\n/**\n * Prices are stored per token, the unit the gateway listing publishes, and\n * shown per million, the unit every price page uses. One copy, so the page,\n * its dialogs and the picker never disagree on the factor.\n */\nexport const PER_MILLION_TOKENS = 1_000_000\n\n/**\n * The models the platform falls back to when an organization has expressed no\n * preference. The only model ids written in the code besides the curated list.\n */\nexport const PLATFORM_MODEL_DEFAULTS = {\n /** New agents, and the runtime fallback of an agent with no model. */\n agentModel: 'openai/gpt-5.6-terra',\n /** Question reformulation, JSON to schema, memory reconciliation. */\n utilityModel: 'google/gemini-3-flash',\n} as const\n\n/**\n * Models used to vectorize text, never to answer. Kept apart from\n * {@link GATEWAY_MODELS} so an embedding id cannot reach the chat plumbing,\n * and out of the catalog for the same reason: they are not selectable.\n */\nexport enum EmbeddingModel {\n textEmbedding3Small = 'openai/text-embedding-3-small',\n textEmbedding3Large = 'openai/text-embedding-3-large',\n geminiEmbedding2 = 'google/gemini-embedding-2',\n}\n\nexport const EMBED_MODEL = EmbeddingModel.geminiEmbedding2\n\n/**\n * Model that answers typed questions (yes/no, choice, score) with calibrated\n * probabilities instead of text, through the gateway's `experimental_evaluate`\n * (TypeSafe Jev). Out of the catalog like the embeddings: it decides for the\n * platform, it never answers the user. Its gateway listing reports no ZDR,\n * yet a ZDR request is served, routed to TypeSafe alone (measured\n * 2026-09-28, scripts/spikeJevEvaluation.ts).\n */\nexport const EVALUATION_MODEL = 'typesafe-ai/jev'\n\n/**\n * Supported languages for documents and revised answers\n */\nexport enum SupportedLanguage {\n it = 'it',\n en = 'en',\n es = 'es',\n fr = 'fr',\n de = 'de',\n}\n\n/**\n * Language display names (i18n keys)\n */\nexport const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {\n [SupportedLanguage.it]: 'language.italian',\n [SupportedLanguage.en]: 'language.english',\n [SupportedLanguage.es]: 'language.spanish',\n [SupportedLanguage.fr]: 'language.french',\n [SupportedLanguage.de]: 'language.german',\n}\n\n/**\n * Content extraction strategies for URL scraping\n */\nexport enum ExtractionStrategy {\n /** Use Mozilla Readability for content extraction (fast, free, no API calls) */\n READABILITY = 'readability',\n /** Use AI-based extraction (better for complex layouts, requires API calls) */\n AI = 'ai',\n}\n\n/**\n * Browser engine options for URL scraping\n */\nexport enum BrowserEngine {\n /** Use simple HTTP fetch (fast, lightweight, no JavaScript support) */\n FETCH = 'fetch',\n /** Use Puppeteer with headless Chrome (full browser, JavaScript support) */\n PUPPETEER = 'puppeteer',\n /** Delegate browser rendering to an external pdf-image-generator service */\n REMOTE = 'remote',\n}\n\n/**\n * Which report family has to account for a credit charge.\n *\n * The hourly reconciliation trusts `cost_reports` (the gateway's own billing,\n * copied in house) as the source of truth for what an organization spent. A\n * charge whose supplier is not the gateway has no counterpart there, so it must\n * be told apart or the reconciliation forgives it: that is what happened to the\n * voice minutes until 2026-08-31.\n *\n * Declared here, written on the ledger row at charge time, never inferred from\n * `source` downstream: the next non-gateway supplier must be a compile-time\n * decision, not a string nobody remembered to add to a list.\n */\nexport enum BillingSource {\n /** Billed by the AI gateway, so it lands in `cost_reports`. */\n GATEWAY = 'gateway',\n /** Billed by another supplier (voice minutes, OCR): the ledger is the only record. */\n INTERNAL = 'internal',\n}\n\n/**\n * Kinds of row on the credit ledger.\n *\n * It used to be a free string with exactly one value ever written, `debit`.\n * Adding a second one is not free: the hourly reconciliation sums debits to\n * rebuild the real-time estimate, and the analytics endpoint sums the ledger to\n * chart spend, so a new type that nobody filters out is a number silently\n * doubled.\n */\nexport enum CreditTransactionType {\n /** A charge for something consumed, written in real time. */\n DEBIT = 'debit',\n /** The hourly settlement against the gateway report: the audit trail of a balance move. */\n RECONCILIATION = 'reconciliation',\n}\n\n/**\n * Prefix that marks a model served by the organization's own OpenAI compatible\n * endpoint rather than by the gateway.\n *\n * It is not a gateway brand, which is the point: every switch on the provider\n * (reasoning options, the Google thought signature repair, the per-provider\n * timeouts, the native web search tools) falls into its default branch without\n * any of them having to learn about BYOK.\n */\nexport const BYOK_MODEL_PREFIX = 'byok/'\n\nexport const isByokModel = (id?: string | null): boolean =>\n Boolean(id?.startsWith(BYOK_MODEL_PREFIX))\n\n/**\n * The id as the endpoint wants it, with the prefix removed. The remainder may\n * itself contain slashes (`byok/meta-llama/Llama-3.3-70B`), so this cuts the\n * prefix and does not split.\n */\nexport const byokModelId = (id: string): string =>\n id.startsWith(BYOK_MODEL_PREFIX) ? id.slice(BYOK_MODEL_PREFIX.length) : id\n","import * as z from 'zod'\nimport { decodeOnly } from '../utils'\n\nexport const HeaderSchema = z.object({\n key: z.string().optional(),\n value: z.string().optional(),\n})\n\nexport const HeadesSchema = decodeOnly(\n z.array(HeaderSchema),\n z.array(z.object({ key: z.string(), value: z.string() })),\n (headers) =>\n headers?.filter(\n (header) => header.key?.trim() && header.value?.trim(),\n ) as Array<{ key: string; value: string }>,\n)\n\nexport type Header = z.infer<typeof HeaderSchema>\nexport type Headers = z.infer<typeof HeadesSchema>\n","import * as z from 'zod'\nimport { zodQs, decodeOnly } from '../utils'\nimport {\n SupportedLanguage,\n ExtractionStrategy,\n BrowserEngine,\n} from './constants'\nimport { MediaSchema } from './Media'\nimport { HeadesSchema } from './Header'\n\n/**\n * Processing status for document indexing and embedding generation\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n// ===== Document Source Types =====\n\n/**\n * Conversion options for document processing\n * Used when creating or reprocessing documents\n */\nexport const ConversionOptionsSchema = z.object({\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z\n .enum(BrowserEngine)\n .optional()\n .default(BrowserEngine.FETCH),\n headers: HeadesSchema.nullish(),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n language: z.string().optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n})\n\nexport type ConversionOptions = z.infer<typeof ConversionOptionsSchema>\n\n// ===== Document Source (metadata.source) =====\n\n/**\n * Who keeps a document's content up to date.\n *\n * `web` is a page the platform re-reads on its own; the others are cloud\n * document sources. Distinct from `documents.source_provider`, which carries\n * only the providers that OWN their documents (the cloud ones) and is null for\n * everything else: a web page discovered by a source stays editable.\n */\nexport enum DocumentSourceProvider {\n Web = 'web',\n SharePoint = 'sharepoint',\n GoogleDrive = 'google-drive',\n}\n\n/**\n * Outcome of the last pass over a document's source.\n *\n * `unchanged` is the one that pays for the whole mechanism: the source was read\n * and produced the same text, so no chunk was rewritten and no embedding was\n * charged. `shrunk` is a refusal, not a failure: the page answered but with far\n * less content than before, so the good version was kept.\n */\nexport enum DocumentRefreshResult {\n Updated = 'updated',\n Unchanged = 'unchanged',\n Failed = 'failed',\n Shrunk = 'shrunk',\n Gone = 'gone',\n}\n\n/**\n * How often a document re-reads its source on its own.\n *\n * `null` is the fourth value and the default: manual, which is what every\n * document is today and what every type other than `url` can ever be.\n */\nexport enum DocumentRefreshInterval {\n Daily = 'daily',\n Weekly = 'weekly',\n Monthly = 'monthly',\n}\n\n/**\n * The Postgres interval each trigger advances by.\n *\n * One table, read by the two places that move a deadline: the service, when\n * somebody sets or changes the trigger, and the timer, when a due document has\n * been queued. Written as literals rather than composed at the call site\n * because they end up inside SQL.\n */\nexport const REFRESH_INTERVAL_SQL: Record<DocumentRefreshInterval, string> = {\n [DocumentRefreshInterval.Daily]: '1 day',\n [DocumentRefreshInterval.Weekly]: '7 days',\n [DocumentRefreshInterval.Monthly]: '1 month',\n}\n\n/**\n * Hours after which a document left `processing` is treated as abandoned.\n *\n * A killed worker leaves that status behind, and nothing else moves it: the row\n * would drop out of the refresh timer's selection for good, because the timer\n * only claims documents that are `success` or `failed`. Past this window the\n * timer takes it anyway, and a manual refresh stops being refused.\n *\n * Generous on purpose: a scrape is bounded by the Functions host timeout, so\n * anything still `processing` after six hours is not running.\n */\nexport const STALE_PROCESSING_HOURS = 6\n\nexport const DocumentLastRefreshSchema = z.object({\n at: z.string(),\n result: z.enum(DocumentRefreshResult),\n httpStatus: z.number().optional(),\n error: z.string().optional(),\n /** Reset by the first good outcome; the notification threshold reads it. */\n consecutiveFailures: z.number().optional(),\n /**\n * Words before and after, written by the shrink guard. They are kept rather\n * than only compared because the guard's thresholds are a starting point:\n * after a month of real pages one can tell whether they cut too much.\n */\n previousWordCount: z.number().optional(),\n newWordCount: z.number().optional(),\n})\n\nexport type DocumentLastRefresh = z.infer<typeof DocumentLastRefreshSchema>\n\n/** Fields every source branch shares, whatever keeps the document up to date. */\nconst commonSourceFields = {\n /**\n * Hash of the text that actually reaches the chunks, so an unchanged source\n * costs no chunking and no embedding. Written by the processor on every\n * pass, which is what lets a document imported before this existed start\n * comparing from its second pass on.\n */\n contentHash: z.string().optional(),\n lastRefresh: DocumentLastRefreshSchema.optional(),\n}\n\nconst WebDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.Web),\n url: z.string(),\n // Written by a successful conversion, and only then: at insert time the\n // page has not been read yet, so a value here would attest a scrape that\n // never happened.\n lastScrapedAt: z.string().optional(),\n /**\n * Hash of the deterministic text handed to the extraction step, before the\n * model. With `extractionStrategy: 'ai'` the model's output differs on every\n * call, so comparing the final markdown would always say \"changed\" and\n * every interval would pay conversion, chunking and embedding. This is what\n * the AI path compares, and it decides whether to call the model at all.\n */\n extractionInputHash: z.string().optional(),\n /**\n * Where the last successful pass actually landed after redirects.\n *\n * The shrink guard compares this with the new final URL, and not with `url`\n * on purpose: plenty of sites always redirect (a locale prefix, a trailing\n * slash, http to https), so comparing against the stored URL would refuse\n * every update of those pages forever. What says a page is gone is its\n * destination changing from the one it has been landing on.\n */\n finalUrl: z.string().optional(),\n /**\n * The web source that discovered this page. It says who found it, not who\n * governs it: the page stays editable like any other. Editing the text by\n * hand takes it off, and the page stops belonging to the source.\n */\n sourceId: z.uuid().optional(),\n /**\n * The last run of that source that found the page listed.\n *\n * Not the last successful read of the page, which is `lastScrapedAt`:\n * this is the marker the removal option reads, named like the cloud\n * field because the same reconciliation reads both. A page without one is\n * never removed.\n */\n lastSyncedAt: z.string().optional(),\n /**\n * The `<lastmod>` the sitemap declared the last time the source read it.\n * A change that is newer than `lastScrapedAt` queues an early refresh.\n */\n lastmod: z.string().optional(),\n ...commonSourceFields,\n})\n\n/** Coordinates a cloud adapter needs to find the item again, plus its content fingerprint. */\nconst cloudSourceFields = {\n /** The `document_sync_sources` row that owns this document. */\n sourceId: z.uuid(),\n remoteId: z.string(),\n webUrl: z.string(),\n path: z.string(),\n /**\n * The provider's own content fingerprint, compared for equality only and\n * never interpreted. It is what lets a sync skip the download entirely.\n */\n fingerprint: z.string(),\n /** Marks the document as seen by a run: the reconciliation after a resync reads it. */\n lastSyncedAt: z.string(),\n /** When the provider says the item last changed. Shown, never compared: the fingerprint decides. */\n lastModified: z.string().optional(),\n /**\n * A newly downloaded blob waiting to be converted. It sits here instead of\n * replacing `metadata.media` so that a conversion failure cannot destroy the\n * version that works: the processor promotes it only on success.\n */\n candidate: z\n .object({\n media: MediaSchema,\n fingerprint: z.string(),\n name: z.string(),\n webUrl: z.string(),\n path: z.string(),\n lastModified: z.string().optional(),\n })\n .optional(),\n ...commonSourceFields,\n}\n\nconst SharePointDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.SharePoint),\n ...cloudSourceFields,\n})\n\nconst GoogleDriveDocumentSourceSchema = z.object({\n provider: z.literal(DocumentSourceProvider.GoogleDrive),\n ...cloudSourceFields,\n})\n\n/**\n * `metadata.source`, discriminated on `provider`.\n *\n * One object cannot hold both shapes: a web page has a URL and a scrape date, a\n * cloud file has remote coordinates and a fingerprint, and making every field\n * optional to fit both is the same as having no schema. Rows written before the\n * discriminator existed carry no `provider`, so they are normalized to the web\n * branch before routing: that is the only shape that could have been written.\n *\n * The normalization runs on parse, and responses are serialized by the\n * parse-based compiler (see `apps/backend/src/utils/responseSerializer.ts`), so\n * a legacy row still comes out of the API intact.\n */\nexport const DocumentSourceSchema = z.preprocess(\n (value) => {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !('provider' in value)\n ) {\n return { provider: DocumentSourceProvider.Web, ...value }\n }\n return value\n },\n z.discriminatedUnion('provider', [\n WebDocumentSourceSchema,\n SharePointDocumentSourceSchema,\n GoogleDriveDocumentSourceSchema,\n ]),\n)\n\nexport type DocumentSource = z.infer<typeof DocumentSourceSchema>\nexport type WebDocumentSource = z.infer<typeof WebDocumentSourceSchema>\n\n/**\n * The page URL of a document's source, when its source is a web page.\n *\n * One place to narrow the union, so the views that show \"where this came from\"\n * do not each grow their own check, and a cloud-synced document returns nothing\n * here instead of a broken link: its address is `webUrl` on the remote item,\n * which is a different thing to show and to say.\n */\nexport function documentSourceUrl(source?: DocumentSource): string | undefined {\n return source && 'url' in source ? source.url : undefined\n}\n\n// ===== Processing queue contract =====\n\n/**\n * Why a document is being processed again. Absent means the ingestion of a new\n * document, which is the only case whose natural key is the document id alone.\n */\nexport type DocumentProcessingKind = 'refresh' | 'reprocess' | 'reindex'\n\n/**\n * The `document-processing` queue message.\n *\n * Lives here because two processes read it: the backend publishes it and the\n * Azure Function consumes it. It used to be declared in each, and the copy in\n * the function app was already a field behind.\n */\nexport interface DocumentProcessingJob {\n documentId: string\n conversionOptions?: ConversionOptions\n /**\n * Set by every caller that processes a document which already exists. It\n * shapes the message id (see `publishProcessingJob`) and, for `refresh`,\n * turns on the refresh mode of the processor.\n */\n kind?: DocumentProcessingKind\n /**\n * Only a scheduled refresh enforces the shrink guard. A person who clicked\n * \"refresh now\" is looking at the result and may well be refreshing a page\n * that really did get shorter, so the guard would only stand in their way.\n */\n guarded?: boolean\n /**\n * The web source whose sitemap asked for this early refresh. Nobody is\n * watching it either, so it enforces the shrink guard; and it is void if\n * the page no longer belongs to that source, which is what editing its text\n * by hand does. Not `guarded`, which also voids a page without a trigger:\n * a source reads its pages whether they have one or not.\n */\n sourceId?: string\n}\n\n/**\n * Metadata structure for document.metadata column\n * Consolidates all metadata from converters and source info\n */\nexport const DocumentMetadataSchema = z\n .object({\n // === Source Info (URL and synced documents) ===\n source: DocumentSourceSchema.optional(),\n\n // === Media Info (File documents only) ===\n media: MediaSchema.optional(),\n\n // === Common Fields (all document types) ===\n title: z.string().optional(),\n author: z.string().optional(),\n description: z.string().optional(),\n summary: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n\n // === PDF Specific ===\n subject: z.string().optional(),\n creator: z.string().optional(), // Original application\n producer: z.string().optional(), // PDF generator software\n creationDate: z.string().optional(),\n modificationDate: z.string().optional(),\n pageCount: decodeOnly(\n z.union([z.number(), z.array(z.unknown())]),\n z.number(),\n (val) => (Array.isArray(val) ? val.length : val),\n ).optional(),\n encrypted: z.boolean().optional(),\n\n // === DOCX Specific ===\n characterCount: z.number().optional(),\n lineCount: z.number().optional(),\n\n // === URL Specific ===\n siteName: z.string().optional(),\n canonicalUrl: z.url().optional(),\n imageUrl: z.url().optional(),\n favicon: z.url().optional(),\n type: z.string().optional(), // og:type (article, website, etc.)\n locale: z.string().optional(),\n tags: z.array(z.string()).optional(),\n publishedTime: z.string().optional(),\n modifiedTime: z.string().optional(),\n\n // === Metrics (all document types) ===\n wordCount: z.number().optional(),\n paragraphCount: z.number().optional(),\n readingTime: z.number().optional(), // in minutes\n\n // === OCR (PDF documents) ===\n ocrUsed: z.boolean().optional(),\n ocrProvider: z.string().optional(),\n ocrModel: z.string().optional(),\n ocrPages: z.string().optional(),\n ocrCost: z.number().optional(),\n ocrError: z.string().optional(),\n\n // === RAG / Duplicates / Sync Info ===\n chunksCount: z.number().optional(),\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n qdrantSynced: z.boolean().optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n })\n .loose() // Allow additional custom fields\n\nexport type DocumentMetadata = z.infer<typeof DocumentMetadataSchema>\n\n/**\n * Document type enum\n *\n * - 'file': Document created from uploaded file (PDF, DOCX)\n * - 'url': Document created from web scraping\n * - 'revised-answer': Document created from revised AI answer\n * - 'plain-text': Document created from plain text input\n */\nexport enum DocumentType {\n File = 'file',\n Url = 'url',\n RevisedAnswer = 'revised-answer',\n PlainText = 'plain-text',\n}\n\n// ===== Schemas =====\n\n/**\n * Full document schema from database\n * Extended with computed field chunksCount\n */\nconst BaseDocumentSchema = z.object({\n id: z.string(),\n externalId: z.string().nullable(),\n name: z.string(),\n content: z.string(),\n type: z.string(),\n language: z.string(),\n metadata: z.record(z.string(), z.unknown()),\n indexStatus: z.string(),\n processedAt: z.coerce.date().nullable(),\n createdAt: z.coerce.date(),\n createdBy: z.string(),\n updatedAt: z.coerce.date(),\n updatedBy: z.string(),\n deleted: z.boolean(),\n deletedAt: z.coerce.date().nullable(),\n deletedBy: z.string().nullable(),\n organizationId: z.string(),\n priority: z.number().int().min(1).max(5).default(3),\n /** The automatic refresh trigger; null is manual, and only `url` can have one. */\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n /**\n * When the trigger next comes due. Null with an interval set means paused:\n * the source answered that the page is gone for good.\n */\n nextRefreshAt: z.coerce.date().nullable().optional(),\n /**\n * Which sync source owns this document, when one does.\n *\n * Null is the ordinary case. A non-null value means a job keeps the\n * document up to date, which is what makes it read only: editing it, moving\n * it or deleting it by hand would be undone at the next run, silently.\n * Written only by providers that own their documents, never by a crawl that\n * merely discovered a page.\n */\n sourceProvider: z.enum(DocumentSourceProvider).nullable().optional(),\n})\n\nexport const DocumentSchema = BaseDocumentSchema.extend({\n type: z.enum(DocumentType),\n indexStatus: z.enum(IndexStatus),\n metadata: DocumentMetadataSchema,\n // Add computed field for chunks count (populated by service layer)\n chunksCount: z.number().optional(),\n // Computed fields for redundancy tracking\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n // Qdrant sync status\n qdrantSynced: z.boolean().optional(),\n // Agent IDs associated with this document (populated in single-item reads)\n agentIds: z.array(z.string()).optional(),\n // Folder ID for this document within an agent context (populated in agent-scoped reads)\n folderId: z.string().nullable().optional(),\n // Full folder path string e.g. \"Parent / Child\" (populated in list reads when agentId is set)\n folderPath: z.string().nullable().optional(),\n})\n\n/**\n * Query parameters for listing documents with pagination, sorting, and filters\n */\nexport const DocumentQuerystringSchema = z.object({\n ...zodQs.pagination(),\n ...zodQs.sort(['id', 'updatedAt', 'createdAt', 'name']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n ...zodQs.fullText(),\n ...zodQs.ids(),\n 'filter[similarity]': z.string().optional(),\n // UUID = documents in that folder; 'null' = documents at root (no folder); absent = all documents\n 'filter[folderId]': z.string().optional(),\n 'filter[hasRedundantChunks]': z.string().optional(),\n // One of the intervals, 'null' for the documents left on manual, 'auto' for\n // any of the three. Same convention as `filter[folderId]`, and handled\n // outside `getFilters` for the same reason: `inArray` cannot say IS NULL.\n 'filter[refreshInterval]': z.string().optional(),\n // A provider, 'null' for the documents nobody syncs, 'any' for the ones a\n // source owns whoever it is. Same convention and the same reason as above.\n 'filter[sourceProvider]': z.string().optional(),\n})\n\n/**\n * Document creation schema for POST /documents endpoint\n *\n * Supports three types:\n * - 'file': Requires media object with uploaded file info\n * - 'url': Requires url string for web scraping\n * - 'revised-answer': Requires content (created from revisedAnswer service)\n */\nexport const DocumentCreateDtoSchema = z\n .object({\n type: z.enum(DocumentType).default(DocumentType.File),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n organizationId: z.string(),\n agentIds: z.array(z.string()).min(1, 'At least one agent is required'),\n folderId: z.uuid().nullable().optional(),\n media: MediaSchema.optional(),\n url: z.url().optional(),\n content: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z.enum(BrowserEngine).optional(),\n headers: HeadesSchema.nullish().default([]),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n priority: z.number().int().min(1).max(5).default(3).optional(),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n /**\n * The web source this page is imported for: the pages ticked in the\n * crawler dialog when it also creates a source. They land in the\n * source's folder as pages the source discovered, so its removal\n * option and its early refresh apply to them from the first run.\n */\n syncSourceId: z.uuid().optional(),\n })\n .superRefine((data, ctx) => {\n if (data.syncSourceId && data.type !== DocumentType.Url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.syncSourceUrlOnly',\n },\n path: ['syncSourceId'],\n })\n }\n if (data.type === DocumentType.File && !data.media) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['media'],\n })\n return\n }\n if (data.type === DocumentType.Url && !data.url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['url'],\n })\n }\n // The database has the same CHECK, but a 400 saying which field is\n // wrong beats a constraint violation surfacing as a 500.\n if (data.refreshInterval && data.type !== DocumentType.Url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.refreshIntervalUrlOnly',\n },\n path: ['refreshInterval'],\n })\n }\n if (\n data.type === DocumentType.PlainText &&\n !(data.name || data.language)\n ) {\n if (!data.name) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['name'],\n })\n }\n if (!data.language) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['language'],\n })\n }\n }\n })\n\n/**\n * Document update schema for PUT /documents/:id endpoint\n *\n * All fields are optional, but at least one must be provided\n */\nexport const DocumentUpdateDtoSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n content: z.string().optional(),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n priority: z.number().int().min(1).max(5).optional(),\n agentIds: z.array(z.string()).min(1).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable().optional(),\n})\n\n/** Body of `PATCH /documents/:id/refresh-interval`, twin of the priority route. */\nexport const DocumentRefreshIntervalDtoSchema = z.object({\n refreshInterval: z.enum(DocumentRefreshInterval).nullable(),\n})\n\n/** Body of `POST /documents/bulk-refresh-interval`. */\nexport const DocumentBulkRefreshIntervalDtoSchema = z.object({\n ids: z.array(z.uuid()).min(1).max(100),\n refreshInterval: z.enum(DocumentRefreshInterval).nullable(),\n})\n\n/** Body of `POST /documents/bulk-refresh`, twin of `bulk-reprocess`. */\nexport const DocumentBulkRefreshDtoSchema = z.object({\n ids: z.array(z.uuid()).min(1).max(100),\n})\n\n// ===== Type Exports =====\n\nexport type Document = z.infer<typeof DocumentSchema>\nexport type DocumentQuerystring = z.infer<typeof DocumentQuerystringSchema>\nexport type DocumentCreateDto = z.infer<typeof DocumentCreateDtoSchema>\nexport type DocumentUpdateDto = z.infer<typeof DocumentUpdateDtoSchema>\nexport type DocumentRefreshIntervalDto = z.infer<\n typeof DocumentRefreshIntervalDtoSchema\n>\nexport type DocumentBulkRefreshIntervalDto = z.infer<\n typeof DocumentBulkRefreshIntervalDtoSchema\n>\nexport type DocumentBulkRefreshDto = z.infer<\n typeof DocumentBulkRefreshDtoSchema\n>\n\n// ===== Helper Functions =====\n\nexport const getStatusColor = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'success'\n case IndexStatus.Failed:\n return 'danger'\n case IndexStatus.Processing:\n return 'warning'\n default:\n return 'info'\n }\n}\n\nexport const getStatusIcon = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'ri:check-line'\n case IndexStatus.Failed:\n return 'ri:error-warning-line'\n case IndexStatus.Processing:\n return 'ri:hourglass-line'\n default:\n return 'ri:time-line'\n }\n}\n\nexport const getTypeIcon = (type: DocumentType) => {\n switch (type) {\n case DocumentType.File:\n return 'ri:file-line'\n case DocumentType.Url:\n return 'ri:link'\n case DocumentType.RevisedAnswer:\n return 'ri:feedback-line'\n default:\n return 'ri:input-method-line'\n }\n}\n\nexport const getFileIcon = (extension: string) => {\n const ext = (\n extension.startsWith('.') ? extension.slice(1) : extension\n ).toLowerCase()\n switch (ext) {\n case 'pdf':\n return 'ri:file-pdf-2-line'\n case 'doc':\n case 'docx':\n return 'ri:file-word-line'\n case 'xls':\n case 'xlsx':\n return 'ri:file-excel-line'\n case 'ppt':\n case 'pptx':\n return 'ri:file-ppt-line'\n case 'txt':\n return 'ri:file-text-line'\n case 'mp3':\n case 'wav':\n case 'ogg':\n return 'ri:file-music-line'\n case 'mp4':\n case 'avi':\n case 'mkv':\n return 'ri:file-video-line'\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'gif':\n case 'bmp':\n case 'svg':\n return 'ri:file-image-line'\n case 'zip':\n case 'rar':\n case '7z':\n return 'ri:file-zip-line'\n default:\n return 'ri:file-line'\n }\n}\n\n/** Whether a MIME type is an image. */\nexport const isImageMime = (mimeType?: string | null): boolean =>\n (mimeType ?? '').startsWith('image/')\n\n/** Whether a file MIME type can be previewed inline in the browser (image or\n * PDF) instead of being forced as a download. Shared by every file-list surface\n * so the preview/download decision stays consistent. */\nexport const isInlineViewable = (mimeType?: string | null): boolean =>\n isImageMime(mimeType) || mimeType === 'application/pdf'\n\n/** File-type identity for list rows and previews: icon, accent color\n * (CSS value driving glyph and tile tint) and a short human kind label. */\nexport interface FileKind {\n icon: string\n accent: string\n label: string\n}\n\n/** File-type identity derived from a MIME type. Accents match the chat\n * attachment cards (PkChatbotFilePreview) so files read the same everywhere. */\nexport const fileKindForMime = (mimeType?: string | null): FileKind => {\n const mime = mimeType ?? ''\n if (isImageMime(mime)) {\n return { icon: 'ri:image-line', accent: '#ec4899', label: 'Image' }\n }\n if (mime === 'application/pdf') {\n return { icon: 'ri:file-pdf-2-line', accent: '#e2483d', label: 'PDF' }\n }\n if (mime === 'application/msword' || mime.includes('wordprocessingml')) {\n return { icon: 'ri:file-word-line', accent: '#2b7de9', label: 'Word' }\n }\n if (mime === 'application/vnd.ms-excel' || mime.includes('spreadsheetml')) {\n return {\n icon: 'ri:file-excel-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n }\n }\n if (\n mime === 'application/vnd.ms-powerpoint' ||\n mime.includes('presentationml')\n ) {\n return {\n icon: 'ri:file-ppt-line',\n accent: '#d98324',\n label: 'Presentation',\n }\n }\n if (mime.startsWith('audio/')) {\n return { icon: 'ri:file-music-line', accent: '#8b5cf6', label: 'Audio' }\n }\n if (mime.startsWith('video/')) {\n return { icon: 'ri:file-video-line', accent: '#6366f1', label: 'Video' }\n }\n if (\n mime === 'application/zip' ||\n mime === 'application/x-rar-compressed' ||\n mime === 'application/x-7z-compressed'\n ) {\n return { icon: 'ri:file-zip-line', accent: '#c08a1e', label: 'Archive' }\n }\n if (mime.startsWith('text/')) {\n return { icon: 'ri:file-text-line', accent: '#64748b', label: 'Text' }\n }\n return { icon: 'ri:file-line', accent: '#64748b', label: 'File' }\n}\n\n/** Icon for a file-list row, derived from its MIME type. */\nexport const fileIconForMime = (mimeType?: string | null): string =>\n fileKindForMime(mimeType).icon\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkChatbotInput-DcLmOqA1.js","names":["$t","$slots","$n"],"sources":["../../../../node_modules/.pnpm/autosize@6.0.1/node_modules/autosize/dist/autosize.esm.js","../../../../packages/components/src/chat/PkChatButton.vue","../../../../packages/components/src/chat/PkChatButton.vue","../../../../packages/components/src/chat/PkChatbotInput.vue","../../../../packages/components/src/chat/PkChatbotInput.vue"],"sourcesContent":["var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;\"undefined\"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&\"TEXTAREA\"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){a({testForHeightReduction:\"\"===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener(\"autosize:destroy\",l),t.removeEventListener(\"autosize:update\",s),t.removeEventListener(\"input\",i),window.removeEventListener(\"resize\",s),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener(\"autosize:destroy\",l),t.addEventListener(\"autosize:update\",s),t.addEventListener(\"input\",i),window.addEventListener(\"resize\",s),t.style.overflowX=\"hidden\",t.style.wordWrap=\"break-word\",e.set(t,{destroy:l,update:s}),s()}function a(e){var o,i,l=e.restoreTextAlign,s=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&(\"vertical\"===n.resize?t.style.resize=\"none\":\"both\"===n.resize&&(t.style.resize=\"horizontal\"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior=\"auto\",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=\"\"),i=\"content-box\"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),\"none\"!==n.maxHeight&&i>parseFloat(n.maxHeight)?(\"hidden\"===n.overflowY&&(t.style.overflow=\"scroll\"),i=parseFloat(n.maxHeight)):\"hidden\"!==n.overflowY&&(t.style.overflow=\"hidden\"),t.style.height=i+\"px\",s&&(t.style.textAlign=s),o&&o(),r!==i&&(t.dispatchEvent(new Event(\"autosize:resized\",{bubbles:!0})),r=i),c!==n.overflow&&!s)){var v=n.textAlign;\"hidden\"===n.overflow&&(t.style.textAlign=\"start\"===v?\"end\":\"start\"),a({restoreTextAlign:v,testForHeightReduction:!0})}}function s(){a({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;export default n;\n","<script lang=\"ts\" setup>\n /**\n * The icon button of the chat dock: attach, send and stop in the input,\n * mute and hang up in a live call. One component, because the call bar\n * takes the place of the input and the two must not look like two\n * different pieces of software.\n *\n * Native attributes fall through to the button, so `title`, `disabled` and\n * `aria-pressed` are written by the caller as they would be on a `<button>`.\n */\n withDefaults(\n defineProps<{\n icon: string\n /** Written next to the icon, for an action nobody should have to guess from a glyph. */\n label?: string\n type?: 'button' | 'submit'\n /** `primary` is the filled circle of send and stop, the one action the dock is for. */\n modifiers?: 'danger' | 'primary'\n }>(),\n { label: undefined, type: 'button', modifiers: undefined },\n )\n</script>\n\n<template>\n <button\n class=\"pk-chat-button\"\n :class=\"[\n modifiers ? `pk-chat-button--${modifiers}` : undefined,\n { 'pk-chat-button--labelled': label },\n ]\"\n :type=\"type\">\n <VvIcon :name=\"icon\" class=\"pk-chat-button__icon\" />\n <span v-if=\"label\" class=\"pk-chat-button__label\">{{ label }}</span>\n </button>\n</template>\n\n<style lang=\"scss\">\n .pk-chat-button {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n padding: var(--spacing-4);\n border: none;\n border-radius: var(--rounded);\n background-color: transparent;\n color: var(--color-word-1);\n cursor: pointer;\n transition: var(--transition-colors);\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n\n // The fields' focus ring: a 1px surface-5 line measured 1.3:1 on the\n // surface, under the 3:1 a focus indicator needs\n &:focus-visible {\n outline: var(--spacing-2) solid var(--input-focus-color);\n outline-offset: 0;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n // A toggle has to survive a glance: a crossed-out glyph alone changes\n // too few pixels to notice mid-sentence.\n &[aria-pressed='true'] {\n background-color: var(--color-surface-3);\n color: var(--color-word);\n }\n\n // A wash of the same red rather than a lighter red behind it: the\n // `lighten-5` step lands close enough to the icon to leave it at about\n // 2:1, which is a button that disappears exactly while it is being\n // pointed at. The tint composites over whatever surface is behind, so\n // it holds in the dark theme too.\n &--danger {\n color: var(--color-danger);\n\n &:hover {\n background-color: color-mix(\n in srgb,\n var(--color-danger) 12%,\n transparent\n );\n }\n }\n\n // Same 28px box as the ghost buttons, so the dock keeps its height,\n // with a smaller glyph because a filled shape needs air around it.\n // Its edge is visible, so it sits on the input's padding line just as\n // the text does: a ghost glyph sat 6px further in and made the two\n // sides of the input look unequal. Empty, it turns into a grey pad\n // with the fields' disabled ink: a light arrow on that pad vanished,\n // and with it the circle's edge, which is what squares the sides.\n // The agent's color when the widget has one, like the user's bubble.\n &--primary {\n --pk-chat-button-fill: var(\n --chatbot-main-color,\n var(--color-word-1)\n );\n\n padding: var(--spacing-6);\n border-radius: var(--rounded-full);\n background-color: var(--pk-chat-button-fill);\n color: var(--chatbot-contrast-color, var(--color-surface));\n\n &:hover {\n background-color: color-mix(\n in srgb,\n var(--pk-chat-button-fill) 85%,\n var(--color-surface)\n );\n }\n\n // Off the disc, or a dark ring would sink into a dark fill\n &:focus-visible {\n outline-offset: var(--spacing-2);\n }\n\n &:disabled {\n opacity: 1;\n background-color: var(--color-surface-3);\n color: var(--color-word-3);\n }\n\n .pk-chat-button__icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n }\n\n &--labelled {\n gap: var(--spacing-4);\n padding-inline: var(--spacing-8);\n font-size: var(--text-12);\n }\n\n &__icon {\n display: block;\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n /**\n * The icon button of the chat dock: attach, send and stop in the input,\n * mute and hang up in a live call. One component, because the call bar\n * takes the place of the input and the two must not look like two\n * different pieces of software.\n *\n * Native attributes fall through to the button, so `title`, `disabled` and\n * `aria-pressed` are written by the caller as they would be on a `<button>`.\n */\n withDefaults(\n defineProps<{\n icon: string\n /** Written next to the icon, for an action nobody should have to guess from a glyph. */\n label?: string\n type?: 'button' | 'submit'\n /** `primary` is the filled circle of send and stop, the one action the dock is for. */\n modifiers?: 'danger' | 'primary'\n }>(),\n { label: undefined, type: 'button', modifiers: undefined },\n )\n</script>\n\n<template>\n <button\n class=\"pk-chat-button\"\n :class=\"[\n modifiers ? `pk-chat-button--${modifiers}` : undefined,\n { 'pk-chat-button--labelled': label },\n ]\"\n :type=\"type\">\n <VvIcon :name=\"icon\" class=\"pk-chat-button__icon\" />\n <span v-if=\"label\" class=\"pk-chat-button__label\">{{ label }}</span>\n </button>\n</template>\n\n<style lang=\"scss\">\n .pk-chat-button {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n padding: var(--spacing-4);\n border: none;\n border-radius: var(--rounded);\n background-color: transparent;\n color: var(--color-word-1);\n cursor: pointer;\n transition: var(--transition-colors);\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n\n // The fields' focus ring: a 1px surface-5 line measured 1.3:1 on the\n // surface, under the 3:1 a focus indicator needs\n &:focus-visible {\n outline: var(--spacing-2) solid var(--input-focus-color);\n outline-offset: 0;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n // A toggle has to survive a glance: a crossed-out glyph alone changes\n // too few pixels to notice mid-sentence.\n &[aria-pressed='true'] {\n background-color: var(--color-surface-3);\n color: var(--color-word);\n }\n\n // A wash of the same red rather than a lighter red behind it: the\n // `lighten-5` step lands close enough to the icon to leave it at about\n // 2:1, which is a button that disappears exactly while it is being\n // pointed at. The tint composites over whatever surface is behind, so\n // it holds in the dark theme too.\n &--danger {\n color: var(--color-danger);\n\n &:hover {\n background-color: color-mix(\n in srgb,\n var(--color-danger) 12%,\n transparent\n );\n }\n }\n\n // Same 28px box as the ghost buttons, so the dock keeps its height,\n // with a smaller glyph because a filled shape needs air around it.\n // Its edge is visible, so it sits on the input's padding line just as\n // the text does: a ghost glyph sat 6px further in and made the two\n // sides of the input look unequal. Empty, it turns into a grey pad\n // with the fields' disabled ink: a light arrow on that pad vanished,\n // and with it the circle's edge, which is what squares the sides.\n // The agent's color when the widget has one, like the user's bubble.\n &--primary {\n --pk-chat-button-fill: var(\n --chatbot-main-color,\n var(--color-word-1)\n );\n\n padding: var(--spacing-6);\n border-radius: var(--rounded-full);\n background-color: var(--pk-chat-button-fill);\n color: var(--chatbot-contrast-color, var(--color-surface));\n\n &:hover {\n background-color: color-mix(\n in srgb,\n var(--pk-chat-button-fill) 85%,\n var(--color-surface)\n );\n }\n\n // Off the disc, or a dark ring would sink into a dark fill\n &:focus-visible {\n outline-offset: var(--spacing-2);\n }\n\n &:disabled {\n opacity: 1;\n background-color: var(--color-surface-3);\n color: var(--color-word-3);\n }\n\n .pk-chat-button__icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n }\n\n &--labelled {\n gap: var(--spacing-4);\n padding-inline: var(--spacing-8);\n font-size: var(--text-12);\n }\n\n &__icon {\n display: block;\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n // @ts-expect-error - no types for autosize\n import autosize from 'autosize'\n import {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n } from 'vue'\n import { useDropZone } from '@vueuse/core'\n import type { AgentFileUpload } from 'models'\n import type { PendingAttachment } from 'composables'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChatbotFilePreview from './PkChatbotFilePreview.vue'\n import PkChatButton from './PkChatButton.vue'\n\n // --- Models & Props ---\n\n const modelValue = defineModel<string | undefined>({ default: undefined })\n const pendingAttachments = defineModel<PendingAttachment[]>(\n 'pendingAttachments',\n { default: () => [] },\n )\n\n const emit = defineEmits<{\n submit: []\n 'stop-generation': []\n 'file-select': [file: File]\n 'dismiss-attachment-notice': []\n 'voice-start': []\n }>()\n\n const props = defineProps<{\n placeholder?: string\n dismissableNotice?: string\n status?: 'submitted' | 'streaming' | 'ready' | 'error'\n maxMessageLength?: number\n fileUpload?: AgentFileUpload\n enableDragDrop?: boolean\n attachmentLimitReached?: boolean\n attachmentNotice?: string\n /** Show the talk button: the agent has voice enabled. */\n voiceEnabled?: boolean\n }>()\n\n // --- Template refs ---\n\n const textareaEl = useTemplateRef<HTMLTextAreaElement>('textareaEl')\n const fileInputEl = useTemplateRef<HTMLInputElement>('fileInputEl')\n const cameraInputEl = useTemplateRef<HTMLInputElement>('cameraInputEl')\n const formEl = useTemplateRef<HTMLFormElement>('formEl')\n\n // --- Input buffering (decouples typing from reactive model propagation) ---\n\n const localValue = ref(modelValue.value ?? '')\n let syncTimer: ReturnType<typeof setTimeout> | null = null\n\n const cancelSync = () => {\n if (syncTimer !== null) {\n clearTimeout(syncTimer)\n syncTimer = null\n }\n }\n\n const flushSync = () => {\n cancelSync()\n modelValue.value = localValue.value\n }\n\n const scheduleSync = () => {\n cancelSync()\n syncTimer = setTimeout(() => {\n modelValue.value = localValue.value\n syncTimer = null\n }, 300)\n }\n\n const onTextareaInput = () => {\n localValue.value = textareaEl.value?.value ?? ''\n scheduleSync()\n }\n\n watch(modelValue, (v) => {\n if (v !== localValue.value) {\n localValue.value = v ?? ''\n nextTick(() => {\n if (textareaEl.value) {\n autosize.update(textareaEl.value)\n }\n })\n }\n })\n\n // --- Status computeds ---\n\n const isStreaming = computed(() => props.status === 'streaming')\n const isSubmitted = computed(() => props.status === 'submitted')\n const isInputDisabled = computed(\n () => isStreaming.value || isSubmitted.value,\n )\n\n // --- Validation ---\n\n const counterId = useId()\n const currentLength = computed(() => localValue.value.length)\n\n const isOverLimit = computed(\n () =>\n props.maxMessageLength !== undefined &&\n currentLength.value > props.maxMessageLength,\n )\n\n const isUploadingAnyFile = computed(() =>\n pendingAttachments.value.some((a) => a.state === 'uploading'),\n )\n\n const isSubmitEnabled = computed(() => {\n if (\n isInputDisabled.value ||\n isUploadingAnyFile.value ||\n isOverLimit.value\n ) {\n return false\n }\n return localValue.value.trim().length > 0\n })\n\n // --- File upload ---\n\n const hasCaptureSupport = 'capture' in document.createElement('input')\n\n const acceptMimeTypes = computed(\n () => props.fileUpload?.allowedMimeTypes?.join(',') ?? '',\n )\n\n const acceptImageMimeTypes = computed(() =>\n (props.fileUpload?.allowedMimeTypes ?? [])\n .filter((m) => m.startsWith('image/'))\n .join(','),\n )\n\n const showCameraOption = computed(\n () => hasCaptureSupport && acceptImageMimeTypes.value.length > 0,\n )\n\n const openFilePicker = () => {\n fileInputEl.value?.click()\n }\n\n const openCameraPicker = () => {\n cameraInputEl.value?.click()\n }\n\n // The store owns the attachment limits and drops any overflow; the input\n // just forwards every picked/dropped file.\n const emitFiles = (files: File[]) => {\n for (const file of files) {\n emit('file-select', file)\n }\n }\n\n const onFileChange = (event: Event) => {\n const input = event.target as HTMLInputElement\n emitFiles(Array.from(input.files ?? []))\n input.value = ''\n }\n\n // --- Attachments ---\n\n const removeAttachment = (id: string) => {\n const att = pendingAttachments.value.find((a) => a.id === id)\n if (att) {\n URL.revokeObjectURL(att.localUrl)\n }\n pendingAttachments.value = pendingAttachments.value.filter(\n (a) => a.id !== id,\n )\n }\n\n // --- Drag & drop ---\n\n const isDragDropEnabled = computed(\n () => (props.enableDragDrop ?? true) && !!props.fileUpload?.enabled,\n )\n\n const handleDrop = (files: File[] | null) => {\n if (!isDragDropEnabled.value || !files) {\n return\n }\n emitFiles(files)\n }\n\n const { isOverDropZone } = useDropZone(formEl, {\n dataTypes: computed(() => props.fileUpload?.allowedMimeTypes ?? []),\n onDrop: handleDrop,\n })\n\n // --- Notice queue (single slot above the input, highest priority wins) ---\n\n type InputNotice = {\n id: string\n text?: string\n markdown?: string\n icon?: string\n dismiss?: () => void\n }\n\n const isDismissableNoticeVisible = ref(true)\n const closeDismissableNotice = () => {\n isDismissableNoticeVisible.value = false\n }\n\n const activeNotice = computed<InputNotice | null>(() => {\n // Order = priority. The alert takes precedence over the notice.\n if (props.attachmentNotice) {\n return {\n id: 'limit',\n icon: 'ri:information-line',\n text: props.attachmentNotice,\n dismiss: () => emit('dismiss-attachment-notice'),\n }\n }\n if (props.dismissableNotice && isDismissableNoticeVisible.value) {\n return {\n id: 'dismissable',\n markdown: props.dismissableNotice,\n dismiss: closeDismissableNotice,\n }\n }\n return null\n })\n\n // --- Submit ---\n\n /**\n * Send turns disabled and then gives way to stop, stop gives way to send:\n * a keyboard user who pressed one would be left on the page body. Hand\n * the focus to the textarea, but only after a keyboard press: a tap has no\n * focus to keep, and focusing would bring the phone keyboard back up over\n * the answer.\n */\n const keepKeyboardFocus = () => {\n if (formEl.value?.querySelector('button:focus-visible')) {\n textareaEl.value?.focus()\n }\n }\n\n const stopGeneration = () => {\n keepKeyboardFocus()\n emit('stop-generation')\n }\n\n const handleSubmit = () => {\n if (!isSubmitEnabled.value) {\n return\n }\n keepKeyboardFocus()\n flushSync()\n emit('submit')\n localValue.value = ''\n modelValue.value = ''\n nextTick(() => {\n if (textareaEl.value) {\n textareaEl.value.value = ''\n autosize.update(textareaEl.value)\n }\n })\n }\n\n /**\n * Enter sends, Shift+Enter goes to a new line. The Enter that confirms an\n * IME composition (Japanese, Chinese, Korean) belongs to the composition:\n * Safari reports it with `isComposing` already false, hence the 229.\n */\n const onTextareaEnter = (event: KeyboardEvent) => {\n if (event.shiftKey || event.isComposing || event.keyCode === 229) {\n return\n }\n event.preventDefault()\n handleSubmit()\n }\n\n // --- Lifecycle ---\n\n onMounted(() => {\n if (textareaEl.value) {\n autosize(textareaEl.value)\n }\n })\n\n onBeforeUnmount(() => {\n cancelSync()\n if (textareaEl.value) {\n autosize.destroy(textareaEl.value)\n }\n for (const att of pendingAttachments.value) {\n URL.revokeObjectURL(att.localUrl)\n }\n })\n</script>\n\n<template>\n <div class=\"pk-chatbot-input\">\n <div\n v-if=\"activeNotice\"\n :key=\"activeNotice.id\"\n class=\"pk-chatbot-input__notice\"\n role=\"status\">\n <VvIcon\n v-if=\"activeNotice.icon\"\n :name=\"activeNotice.icon\"\n class=\"pk-chatbot-input__notice-icon\" />\n <PkStreamingMarkdown\n v-if=\"activeNotice.markdown\"\n :markdown=\"activeNotice.markdown\"\n class=\"wysiwyg pk-chatbot-input__notice-text\" />\n <span v-else class=\"pk-chatbot-input__notice-text\">\n {{ activeNotice.text }}\n </span>\n <VvButton\n v-if=\"activeNotice.dismiss\"\n :title=\"$t('action.close')\"\n modifiers=\"action-quiet\"\n icon=\"ri:close-line\"\n class=\"w-18 h-18\"\n @click=\"activeNotice.dismiss()\" />\n </div>\n <form\n ref=\"formEl\"\n class=\"pk-chatbot-input__form\"\n @submit.prevent=\"handleSubmit\">\n <div\n v-if=\"isOverDropZone && isDragDropEnabled\"\n class=\"pk-chatbot-input__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-input__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n <div\n v-if=\"pendingAttachments.length\"\n class=\"pk-chatbot-input__attachments\">\n <PkChatbotFilePreview\n v-for=\"att in pendingAttachments\"\n :key=\"att.id\"\n :media-type=\"att.file.type\"\n :url=\"att.localUrl\"\n :filename=\"att.file.name\"\n :size=\"att.file.size\"\n :loading=\"att.state === 'uploading'\"\n :error=\"att.state === 'error' ? att.error : undefined\"\n :removable=\"att.state !== 'uploading'\"\n compact\n @remove=\"removeAttachment(att.id)\" />\n </div>\n <div class=\"pk-chatbot-input__fieldset\">\n <slot name=\"prepend\" />\n <VvDropdown\n v-if=\"\n fileUpload &&\n fileUpload.enabled &&\n (showCameraOption || $slots['additional-actions'])\n \"\n v-bind=\"{\n placement: 'top-start',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n }\">\n <template #default=\"{ init, toggle, aria }\">\n <!-- The only one still written by hand: the dropdown\n anchors itself to whatever `init` receives, and that\n has to be the element, not a component instance -->\n <button\n :ref=\"(e: any) => init(e)\"\n v-bind=\"aria\"\n class=\"pk-chat-button\"\n type=\"button\"\n :title=\"$t('action.attachFile')\"\n :disabled=\"isInputDisabled\"\n @click=\"toggle\">\n <VvIcon\n name=\"ri:add-line\"\n class=\"pk-chat-button__icon\" />\n </button>\n </template>\n <template #items>\n <VvDropdownAction\n v-if=\"showCameraOption\"\n :disabled=\"\n isInputDisabled || attachmentLimitReached\n \"\n @click=\"openCameraPicker\">\n <VvIcon\n name=\"ri:camera-line\"\n class=\"pk-chatbot-input__dropdown-icon\" />\n {{ $t('action.takePhoto') }}\n </VvDropdownAction>\n <VvDropdownAction\n :disabled=\"\n isInputDisabled || attachmentLimitReached\n \"\n @click=\"openFilePicker\">\n <VvIcon\n name=\"ri:attachment-line\"\n class=\"pk-chatbot-input__dropdown-icon\" />\n {{ $t('action.attachFile') }}\n </VvDropdownAction>\n <slot name=\"additional-actions\" />\n </template>\n </VvDropdown>\n <PkChatButton\n v-else-if=\"fileUpload && fileUpload.enabled\"\n icon=\"ri:add-line\"\n :title=\"$t('action.attachFile')\"\n :disabled=\"isInputDisabled || attachmentLimitReached\"\n @click=\"openFilePicker\" />\n <input\n v-if=\"fileUpload && fileUpload.enabled\"\n ref=\"fileInputEl\"\n type=\"file\"\n hidden\n multiple\n :accept=\"acceptMimeTypes\"\n @change=\"onFileChange\" />\n <input\n v-if=\"fileUpload && fileUpload.enabled && showCameraOption\"\n ref=\"cameraInputEl\"\n type=\"file\"\n capture=\"environment\"\n hidden\n :accept=\"acceptImageMimeTypes\"\n @change=\"onFileChange\" />\n <div class=\"pk-chatbot-input__content\">\n <textarea\n ref=\"textareaEl\"\n :value=\"localValue\"\n class=\"pk-chatbot-input__textarea\"\n name=\"message\"\n rows=\"1\"\n enterkeyhint=\"send\"\n :aria-label=\"$t('label.message')\"\n :aria-describedby=\"\n maxMessageLength ? counterId : undefined\n \"\n :aria-invalid=\"isOverLimit || undefined\"\n :placeholder\n @input=\"onTextareaInput\"\n @keydown.enter=\"onTextareaEnter\"></textarea>\n </div>\n <div\n v-if=\"maxMessageLength\"\n :id=\"counterId\"\n class=\"pk-chatbot-input__counter\"\n :class=\"{ 'pk-chatbot-input__counter--over': isOverLimit }\">\n {{ $n(currentLength, 'integer') }}/<span\n class=\"opacity-60\"\n >{{ $n(maxMessageLength, 'integer') }}</span\n >\n </div>\n <PkChatButton\n v-if=\"voiceEnabled && !isStreaming\"\n icon=\"ri:mic-line\"\n :title=\"$t('action.talk')\"\n :disabled=\"isInputDisabled\"\n @click=\"emit('voice-start')\" />\n <PkChatButton\n v-if=\"isStreaming\"\n icon=\"ri:stop-fill\"\n modifiers=\"primary\"\n :title=\"$t('action.stop')\"\n @click=\"stopGeneration\" />\n <PkChatButton\n v-else\n type=\"submit\"\n modifiers=\"primary\"\n :icon=\"isSubmitted ? 'loading-loop' : 'ri:arrow-up-line'\"\n :title=\"$t('action.submit')\"\n :disabled=\"!isSubmitEnabled\" />\n </div>\n </form>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-input {\n position: relative;\n display: flex;\n flex-direction: column;\n margin-inline: var(--spacing-sm);\n margin-bottom: var(--spacing-sm);\n\n &__notice {\n position: relative;\n padding: var(--spacing-10) var(--spacing-sm);\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-sm);\n background-color: var(--color-surface-1);\n border-top-left-radius: var(--rounded-xl);\n border-top-right-radius: var(--rounded-xl);\n transition-property: var(--transition-property-all);\n transition-duration: var(--duration-300);\n transition-timing-function: var(--ease-in-out);\n font-size: var(--text-12);\n color: var(--color-word-4);\n\n &::after {\n content: '';\n display: block;\n position: absolute;\n left: 0;\n right: 0;\n bottom: calc(-1 * var(--spacing-12));\n height: var(--spacing-12);\n background-color: var(--color-surface-1);\n z-index: 1;\n }\n\n &-icon {\n flex-shrink: 0;\n width: var(--spacing-18);\n height: var(--spacing-18);\n }\n\n &-text {\n flex: 1;\n min-width: 0;\n }\n }\n\n &__form {\n position: relative;\n display: flex;\n z-index: 1;\n flex-direction: column;\n\n // The composer is a field too: same border and focus ring as the\n // form fields, on top of the shadow and the 12px radius that make\n // it the container (12) of its 8px buttons\n border-radius: var(--rounded-xl);\n border: 1px solid var(--input-border-color);\n background-color: var(--color-surface);\n padding: var(--spacing-14);\n box-shadow: var(--shadow-lg);\n font-size: var(\n --text-16\n ); // prevent ios zoom on input focus by using a font size >= 16px\n gap: var(--spacing-xs);\n transition: var(--input-transition);\n\n @include media-breakpoint-up('sm', $breakpoints) {\n font-size: var(--text-14);\n padding: var(--spacing-10);\n }\n\n &:has(:focus-within) {\n border-color: var(--input-focus-color);\n box-shadow: var(--shadow-lg), var(--input-focus-ring);\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: 0;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n border-radius: var(--rounded-xl);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n pointer-events: none;\n }\n\n &__fieldset {\n display: flex;\n align-items: center;\n gap: var(--spacing-xs);\n flex: 1;\n }\n\n &__content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n min-width: 0;\n overflow: hidden;\n }\n\n &__textarea {\n display: block;\n border: none;\n outline: none;\n resize: none;\n width: 100%;\n max-height: 200px;\n color: var(--color-word-1);\n background-color: var(--color-surface);\n\n &::placeholder {\n color: var(--color-word-4);\n }\n }\n\n &__attachments {\n display: flex;\n flex-wrap: wrap;\n // Roomy gap so the uniform-height chips breathe in the row.\n gap: var(--spacing-12);\n padding-bottom: var(--spacing-xs);\n border-bottom: 1px solid var(--color-surface-2);\n }\n\n &__counter {\n background-color: var(--color-surface-1);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-md);\n font-size: var(--text-12);\n color: var(--color-word-3);\n line-height: 1;\n padding-inline: var(--spacing-6);\n padding-block: var(--spacing-4);\n z-index: 1;\n font-variant-numeric: tabular-nums;\n font-family: var(--font-mono);\n font-weight: var(--font-bold);\n\n &--over {\n color: var(--color-danger);\n border-color: var(--color-danger-lighten-5);\n font-weight: var(--font-bold);\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n // @ts-expect-error - no types for autosize\n import autosize from 'autosize'\n import {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n } from 'vue'\n import { useDropZone } from '@vueuse/core'\n import type { AgentFileUpload } from 'models'\n import type { PendingAttachment } from 'composables'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChatbotFilePreview from './PkChatbotFilePreview.vue'\n import PkChatButton from './PkChatButton.vue'\n\n // --- Models & Props ---\n\n const modelValue = defineModel<string | undefined>({ default: undefined })\n const pendingAttachments = defineModel<PendingAttachment[]>(\n 'pendingAttachments',\n { default: () => [] },\n )\n\n const emit = defineEmits<{\n submit: []\n 'stop-generation': []\n 'file-select': [file: File]\n 'dismiss-attachment-notice': []\n 'voice-start': []\n }>()\n\n const props = defineProps<{\n placeholder?: string\n dismissableNotice?: string\n status?: 'submitted' | 'streaming' | 'ready' | 'error'\n maxMessageLength?: number\n fileUpload?: AgentFileUpload\n enableDragDrop?: boolean\n attachmentLimitReached?: boolean\n attachmentNotice?: string\n /** Show the talk button: the agent has voice enabled. */\n voiceEnabled?: boolean\n }>()\n\n // --- Template refs ---\n\n const textareaEl = useTemplateRef<HTMLTextAreaElement>('textareaEl')\n const fileInputEl = useTemplateRef<HTMLInputElement>('fileInputEl')\n const cameraInputEl = useTemplateRef<HTMLInputElement>('cameraInputEl')\n const formEl = useTemplateRef<HTMLFormElement>('formEl')\n\n // --- Input buffering (decouples typing from reactive model propagation) ---\n\n const localValue = ref(modelValue.value ?? '')\n let syncTimer: ReturnType<typeof setTimeout> | null = null\n\n const cancelSync = () => {\n if (syncTimer !== null) {\n clearTimeout(syncTimer)\n syncTimer = null\n }\n }\n\n const flushSync = () => {\n cancelSync()\n modelValue.value = localValue.value\n }\n\n const scheduleSync = () => {\n cancelSync()\n syncTimer = setTimeout(() => {\n modelValue.value = localValue.value\n syncTimer = null\n }, 300)\n }\n\n const onTextareaInput = () => {\n localValue.value = textareaEl.value?.value ?? ''\n scheduleSync()\n }\n\n watch(modelValue, (v) => {\n if (v !== localValue.value) {\n localValue.value = v ?? ''\n nextTick(() => {\n if (textareaEl.value) {\n autosize.update(textareaEl.value)\n }\n })\n }\n })\n\n // --- Status computeds ---\n\n const isStreaming = computed(() => props.status === 'streaming')\n const isSubmitted = computed(() => props.status === 'submitted')\n const isInputDisabled = computed(\n () => isStreaming.value || isSubmitted.value,\n )\n\n // --- Validation ---\n\n const counterId = useId()\n const currentLength = computed(() => localValue.value.length)\n\n const isOverLimit = computed(\n () =>\n props.maxMessageLength !== undefined &&\n currentLength.value > props.maxMessageLength,\n )\n\n const isUploadingAnyFile = computed(() =>\n pendingAttachments.value.some((a) => a.state === 'uploading'),\n )\n\n const isSubmitEnabled = computed(() => {\n if (\n isInputDisabled.value ||\n isUploadingAnyFile.value ||\n isOverLimit.value\n ) {\n return false\n }\n return localValue.value.trim().length > 0\n })\n\n // --- File upload ---\n\n const hasCaptureSupport = 'capture' in document.createElement('input')\n\n const acceptMimeTypes = computed(\n () => props.fileUpload?.allowedMimeTypes?.join(',') ?? '',\n )\n\n const acceptImageMimeTypes = computed(() =>\n (props.fileUpload?.allowedMimeTypes ?? [])\n .filter((m) => m.startsWith('image/'))\n .join(','),\n )\n\n const showCameraOption = computed(\n () => hasCaptureSupport && acceptImageMimeTypes.value.length > 0,\n )\n\n const openFilePicker = () => {\n fileInputEl.value?.click()\n }\n\n const openCameraPicker = () => {\n cameraInputEl.value?.click()\n }\n\n // The store owns the attachment limits and drops any overflow; the input\n // just forwards every picked/dropped file.\n const emitFiles = (files: File[]) => {\n for (const file of files) {\n emit('file-select', file)\n }\n }\n\n const onFileChange = (event: Event) => {\n const input = event.target as HTMLInputElement\n emitFiles(Array.from(input.files ?? []))\n input.value = ''\n }\n\n // --- Attachments ---\n\n const removeAttachment = (id: string) => {\n const att = pendingAttachments.value.find((a) => a.id === id)\n if (att) {\n URL.revokeObjectURL(att.localUrl)\n }\n pendingAttachments.value = pendingAttachments.value.filter(\n (a) => a.id !== id,\n )\n }\n\n // --- Drag & drop ---\n\n const isDragDropEnabled = computed(\n () => (props.enableDragDrop ?? true) && !!props.fileUpload?.enabled,\n )\n\n const handleDrop = (files: File[] | null) => {\n if (!isDragDropEnabled.value || !files) {\n return\n }\n emitFiles(files)\n }\n\n const { isOverDropZone } = useDropZone(formEl, {\n dataTypes: computed(() => props.fileUpload?.allowedMimeTypes ?? []),\n onDrop: handleDrop,\n })\n\n // --- Notice queue (single slot above the input, highest priority wins) ---\n\n type InputNotice = {\n id: string\n text?: string\n markdown?: string\n icon?: string\n dismiss?: () => void\n }\n\n const isDismissableNoticeVisible = ref(true)\n const closeDismissableNotice = () => {\n isDismissableNoticeVisible.value = false\n }\n\n const activeNotice = computed<InputNotice | null>(() => {\n // Order = priority. The alert takes precedence over the notice.\n if (props.attachmentNotice) {\n return {\n id: 'limit',\n icon: 'ri:information-line',\n text: props.attachmentNotice,\n dismiss: () => emit('dismiss-attachment-notice'),\n }\n }\n if (props.dismissableNotice && isDismissableNoticeVisible.value) {\n return {\n id: 'dismissable',\n markdown: props.dismissableNotice,\n dismiss: closeDismissableNotice,\n }\n }\n return null\n })\n\n // --- Submit ---\n\n /**\n * Send turns disabled and then gives way to stop, stop gives way to send:\n * a keyboard user who pressed one would be left on the page body. Hand\n * the focus to the textarea, but only after a keyboard press: a tap has no\n * focus to keep, and focusing would bring the phone keyboard back up over\n * the answer.\n */\n const keepKeyboardFocus = () => {\n if (formEl.value?.querySelector('button:focus-visible')) {\n textareaEl.value?.focus()\n }\n }\n\n const stopGeneration = () => {\n keepKeyboardFocus()\n emit('stop-generation')\n }\n\n const handleSubmit = () => {\n if (!isSubmitEnabled.value) {\n return\n }\n keepKeyboardFocus()\n flushSync()\n emit('submit')\n localValue.value = ''\n modelValue.value = ''\n nextTick(() => {\n if (textareaEl.value) {\n textareaEl.value.value = ''\n autosize.update(textareaEl.value)\n }\n })\n }\n\n /**\n * Enter sends, Shift+Enter goes to a new line. The Enter that confirms an\n * IME composition (Japanese, Chinese, Korean) belongs to the composition:\n * Safari reports it with `isComposing` already false, hence the 229.\n */\n const onTextareaEnter = (event: KeyboardEvent) => {\n if (event.shiftKey || event.isComposing || event.keyCode === 229) {\n return\n }\n event.preventDefault()\n handleSubmit()\n }\n\n // --- Lifecycle ---\n\n onMounted(() => {\n if (textareaEl.value) {\n autosize(textareaEl.value)\n }\n })\n\n onBeforeUnmount(() => {\n cancelSync()\n if (textareaEl.value) {\n autosize.destroy(textareaEl.value)\n }\n for (const att of pendingAttachments.value) {\n URL.revokeObjectURL(att.localUrl)\n }\n })\n</script>\n\n<template>\n <div class=\"pk-chatbot-input\">\n <div\n v-if=\"activeNotice\"\n :key=\"activeNotice.id\"\n class=\"pk-chatbot-input__notice\"\n role=\"status\">\n <VvIcon\n v-if=\"activeNotice.icon\"\n :name=\"activeNotice.icon\"\n class=\"pk-chatbot-input__notice-icon\" />\n <PkStreamingMarkdown\n v-if=\"activeNotice.markdown\"\n :markdown=\"activeNotice.markdown\"\n class=\"wysiwyg pk-chatbot-input__notice-text\" />\n <span v-else class=\"pk-chatbot-input__notice-text\">\n {{ activeNotice.text }}\n </span>\n <VvButton\n v-if=\"activeNotice.dismiss\"\n :title=\"$t('action.close')\"\n modifiers=\"action-quiet\"\n icon=\"ri:close-line\"\n class=\"w-18 h-18\"\n @click=\"activeNotice.dismiss()\" />\n </div>\n <form\n ref=\"formEl\"\n class=\"pk-chatbot-input__form\"\n @submit.prevent=\"handleSubmit\">\n <div\n v-if=\"isOverDropZone && isDragDropEnabled\"\n class=\"pk-chatbot-input__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-input__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n <div\n v-if=\"pendingAttachments.length\"\n class=\"pk-chatbot-input__attachments\">\n <PkChatbotFilePreview\n v-for=\"att in pendingAttachments\"\n :key=\"att.id\"\n :media-type=\"att.file.type\"\n :url=\"att.localUrl\"\n :filename=\"att.file.name\"\n :size=\"att.file.size\"\n :loading=\"att.state === 'uploading'\"\n :error=\"att.state === 'error' ? att.error : undefined\"\n :removable=\"att.state !== 'uploading'\"\n compact\n @remove=\"removeAttachment(att.id)\" />\n </div>\n <div class=\"pk-chatbot-input__fieldset\">\n <slot name=\"prepend\" />\n <VvDropdown\n v-if=\"\n fileUpload &&\n fileUpload.enabled &&\n (showCameraOption || $slots['additional-actions'])\n \"\n v-bind=\"{\n placement: 'top-start',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n }\">\n <template #default=\"{ init, toggle, aria }\">\n <!-- The only one still written by hand: the dropdown\n anchors itself to whatever `init` receives, and that\n has to be the element, not a component instance -->\n <button\n :ref=\"(e: any) => init(e)\"\n v-bind=\"aria\"\n class=\"pk-chat-button\"\n type=\"button\"\n :title=\"$t('action.attachFile')\"\n :disabled=\"isInputDisabled\"\n @click=\"toggle\">\n <VvIcon\n name=\"ri:add-line\"\n class=\"pk-chat-button__icon\" />\n </button>\n </template>\n <template #items>\n <VvDropdownAction\n v-if=\"showCameraOption\"\n :disabled=\"\n isInputDisabled || attachmentLimitReached\n \"\n @click=\"openCameraPicker\">\n <VvIcon\n name=\"ri:camera-line\"\n class=\"pk-chatbot-input__dropdown-icon\" />\n {{ $t('action.takePhoto') }}\n </VvDropdownAction>\n <VvDropdownAction\n :disabled=\"\n isInputDisabled || attachmentLimitReached\n \"\n @click=\"openFilePicker\">\n <VvIcon\n name=\"ri:attachment-line\"\n class=\"pk-chatbot-input__dropdown-icon\" />\n {{ $t('action.attachFile') }}\n </VvDropdownAction>\n <slot name=\"additional-actions\" />\n </template>\n </VvDropdown>\n <PkChatButton\n v-else-if=\"fileUpload && fileUpload.enabled\"\n icon=\"ri:add-line\"\n :title=\"$t('action.attachFile')\"\n :disabled=\"isInputDisabled || attachmentLimitReached\"\n @click=\"openFilePicker\" />\n <input\n v-if=\"fileUpload && fileUpload.enabled\"\n ref=\"fileInputEl\"\n type=\"file\"\n hidden\n multiple\n :accept=\"acceptMimeTypes\"\n @change=\"onFileChange\" />\n <input\n v-if=\"fileUpload && fileUpload.enabled && showCameraOption\"\n ref=\"cameraInputEl\"\n type=\"file\"\n capture=\"environment\"\n hidden\n :accept=\"acceptImageMimeTypes\"\n @change=\"onFileChange\" />\n <div class=\"pk-chatbot-input__content\">\n <textarea\n ref=\"textareaEl\"\n :value=\"localValue\"\n class=\"pk-chatbot-input__textarea\"\n name=\"message\"\n rows=\"1\"\n enterkeyhint=\"send\"\n :aria-label=\"$t('label.message')\"\n :aria-describedby=\"\n maxMessageLength ? counterId : undefined\n \"\n :aria-invalid=\"isOverLimit || undefined\"\n :placeholder\n @input=\"onTextareaInput\"\n @keydown.enter=\"onTextareaEnter\"></textarea>\n </div>\n <div\n v-if=\"maxMessageLength\"\n :id=\"counterId\"\n class=\"pk-chatbot-input__counter\"\n :class=\"{ 'pk-chatbot-input__counter--over': isOverLimit }\">\n {{ $n(currentLength, 'integer') }}/<span\n class=\"opacity-60\"\n >{{ $n(maxMessageLength, 'integer') }}</span\n >\n </div>\n <PkChatButton\n v-if=\"voiceEnabled && !isStreaming\"\n icon=\"ri:mic-line\"\n :title=\"$t('action.talk')\"\n :disabled=\"isInputDisabled\"\n @click=\"emit('voice-start')\" />\n <PkChatButton\n v-if=\"isStreaming\"\n icon=\"ri:stop-fill\"\n modifiers=\"primary\"\n :title=\"$t('action.stop')\"\n @click=\"stopGeneration\" />\n <PkChatButton\n v-else\n type=\"submit\"\n modifiers=\"primary\"\n :icon=\"isSubmitted ? 'loading-loop' : 'ri:arrow-up-line'\"\n :title=\"$t('action.submit')\"\n :disabled=\"!isSubmitEnabled\" />\n </div>\n </form>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-input {\n position: relative;\n display: flex;\n flex-direction: column;\n margin-inline: var(--spacing-sm);\n margin-bottom: var(--spacing-sm);\n\n &__notice {\n position: relative;\n padding: var(--spacing-10) var(--spacing-sm);\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-sm);\n background-color: var(--color-surface-1);\n border-top-left-radius: var(--rounded-xl);\n border-top-right-radius: var(--rounded-xl);\n transition-property: var(--transition-property-all);\n transition-duration: var(--duration-300);\n transition-timing-function: var(--ease-in-out);\n font-size: var(--text-12);\n color: var(--color-word-4);\n\n &::after {\n content: '';\n display: block;\n position: absolute;\n left: 0;\n right: 0;\n bottom: calc(-1 * var(--spacing-12));\n height: var(--spacing-12);\n background-color: var(--color-surface-1);\n z-index: 1;\n }\n\n &-icon {\n flex-shrink: 0;\n width: var(--spacing-18);\n height: var(--spacing-18);\n }\n\n &-text {\n flex: 1;\n min-width: 0;\n }\n }\n\n &__form {\n position: relative;\n display: flex;\n z-index: 1;\n flex-direction: column;\n\n // The composer is a field too: same border and focus ring as the\n // form fields, on top of the shadow and the 12px radius that make\n // it the container (12) of its 8px buttons\n border-radius: var(--rounded-xl);\n border: 1px solid var(--input-border-color);\n background-color: var(--color-surface);\n padding: var(--spacing-14);\n box-shadow: var(--shadow-lg);\n font-size: var(\n --text-16\n ); // prevent ios zoom on input focus by using a font size >= 16px\n gap: var(--spacing-xs);\n transition: var(--input-transition);\n\n @include media-breakpoint-up('sm', $breakpoints) {\n font-size: var(--text-14);\n padding: var(--spacing-10);\n }\n\n &:has(:focus-within) {\n border-color: var(--input-focus-color);\n box-shadow: var(--shadow-lg), var(--input-focus-ring);\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: 0;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n border-radius: var(--rounded-xl);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n pointer-events: none;\n }\n\n &__fieldset {\n display: flex;\n align-items: center;\n gap: var(--spacing-xs);\n flex: 1;\n }\n\n &__content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n min-width: 0;\n overflow: hidden;\n }\n\n &__textarea {\n display: block;\n border: none;\n outline: none;\n resize: none;\n width: 100%;\n max-height: 200px;\n color: var(--color-word-1);\n background-color: var(--color-surface);\n\n &::placeholder {\n color: var(--color-word-4);\n }\n }\n\n &__attachments {\n display: flex;\n flex-wrap: wrap;\n // Roomy gap so the uniform-height chips breathe in the row.\n gap: var(--spacing-12);\n padding-bottom: var(--spacing-xs);\n border-bottom: 1px solid var(--color-surface-2);\n }\n\n &__counter {\n background-color: var(--color-surface-1);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-md);\n font-size: var(--text-12);\n color: var(--color-word-3);\n line-height: 1;\n padding-inline: var(--spacing-6);\n padding-block: var(--spacing-4);\n z-index: 1;\n font-variant-numeric: tabular-nums;\n font-family: var(--font-mono);\n font-weight: var(--font-bold);\n\n &--over {\n color: var(--color-danger);\n border-color: var(--color-danger-lighten-5);\n font-weight: var(--font-bold);\n }\n }\n }\n</style>\n"],"x_google_ignoreList":[0],"mappings":"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"}