@8wave/ai-elements 0.116.0-beta.3 → 0.117.0-beta.10

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 (38) hide show
  1. package/dist/_chunks/Agent-BxyaZeuL.js.map +1 -1
  2. package/dist/_chunks/{PkToolShowSources-BnpzLyxs.js → PkToolShowSources-BgnumOzQ.js} +2 -2
  3. package/dist/_chunks/{PkToolShowSources-BnpzLyxs.js.map → PkToolShowSources-BgnumOzQ.js.map} +1 -1
  4. package/dist/_chunks/{browser-CUX1HiwU.js → browser-BJcsjj9S.js} +7 -3
  5. package/dist/_chunks/{browser-CUX1HiwU.js.map → browser-BJcsjj9S.js.map} +1 -1
  6. package/dist/_chunks/{markdown-CqYYB88s.js → markdown-DCYDJNwF.js} +6 -3
  7. package/dist/_chunks/markdown-DCYDJNwF.js.map +1 -0
  8. package/dist/ai-elements.es.js +36 -12
  9. package/dist/ai-elements.es.js.map +1 -1
  10. package/dist-vue/PkChatbot.js +1 -1
  11. package/dist-vue/PkChatbotMessages.js +1 -1
  12. package/dist-vue/PkChatbotViewChat.js +1 -1
  13. package/dist-vue/PkChatbotViewFiles.js +1 -1
  14. package/dist-vue/_chunks/{Document-DoWHaIlA.js → Document-Ol8nGgij.js} +6 -3
  15. package/dist-vue/_chunks/Document-Ol8nGgij.js.map +1 -0
  16. package/dist-vue/_chunks/Media-Cv7ou2tP.js.map +1 -1
  17. package/dist-vue/_chunks/{PkChatbot-VqMDgwEm.js → PkChatbot-BmpFnkde.js} +3 -3
  18. package/dist-vue/_chunks/{PkChatbot-VqMDgwEm.js.map → PkChatbot-BmpFnkde.js.map} +1 -1
  19. package/dist-vue/_chunks/{PkChatbotMessages-DrlWn01K.js → PkChatbotMessages-CztL_AMB.js} +3 -3
  20. package/dist-vue/_chunks/{PkChatbotMessages-DrlWn01K.js.map → PkChatbotMessages-CztL_AMB.js.map} +1 -1
  21. package/dist-vue/_chunks/{PkChatbotViewChat-DP9PaWaa.js → PkChatbotViewChat-DZbQQNQn.js} +3 -3
  22. package/dist-vue/_chunks/{PkChatbotViewChat-DP9PaWaa.js.map → PkChatbotViewChat-DZbQQNQn.js.map} +1 -1
  23. package/dist-vue/_chunks/{PkChatbotViewFiles-NRidzZgV.js → PkChatbotViewFiles--wM5SVAl.js} +2 -2
  24. package/dist-vue/_chunks/{PkChatbotViewFiles-NRidzZgV.js.map → PkChatbotViewFiles--wM5SVAl.js.map} +1 -1
  25. package/dist-vue/_chunks/{PkToolShowDiagram-KjhjQe3_.js → PkToolShowDiagram-9THUQfbg.js} +2 -2
  26. package/dist-vue/_chunks/{PkToolShowDiagram-KjhjQe3_.js.map → PkToolShowDiagram-9THUQfbg.js.map} +1 -1
  27. package/dist-vue/_chunks/{PkToolShowSources-DsKrrIEK.js → PkToolShowSources-BfWNsyLw.js} +2 -2
  28. package/dist-vue/_chunks/{PkToolShowSources-DsKrrIEK.js.map → PkToolShowSources-BfWNsyLw.js.map} +1 -1
  29. package/dist-vue/composables.js +32 -8
  30. package/dist-vue/composables.js.map +1 -1
  31. package/dist-vue/index.js +9 -9
  32. package/dist-vue/index.js.map +1 -1
  33. package/dist-vue/packages/models/src/schema/Document.d.ts +2 -2
  34. package/dist-vue/packages/models/src/schema/Media.d.ts +28 -1
  35. package/dist-vue/packages/models/src/schema/Member.d.ts +19 -0
  36. package/package.json +1 -1
  37. package/dist/_chunks/markdown-CqYYB88s.js.map +0 -1
  38. package/dist-vue/_chunks/Document-DoWHaIlA.js.map +0 -1
@@ -492,13 +492,13 @@ export type DocumentQueueMessage = DocumentProcessingJob | SpreadsheetConversion
492
492
  * Why a document is not processed because of the organization's AI settings,
493
493
  * as `DocumentRefusal` in `database/documentAi` says.
494
494
  */
495
- export declare const DOCUMENT_REFUSAL_REASONS: readonly ["gateway-disabled", "endpoint-off", "model-unavailable", "no-model", "format-unsupported", "credit-exhausted"];
495
+ export declare const DOCUMENT_REFUSAL_REASONS: readonly ["gateway-disabled", "endpoint-off", "model-unavailable", "no-model", "format-unsupported", "credit-exhausted", "transcription-off"];
496
496
  export type DocumentRefusalReason = (typeof DOCUMENT_REFUSAL_REASONS)[number];
497
497
  /**
498
498
  * Why a file is refused for what it is, as `UnprocessableFileError` in the
499
499
  * document processor says.
500
500
  */
501
- export declare const UNPROCESSABLE_FILE_REASONS: readonly ["file-unreadable", "file-protected", "file-type-unsupported", "spreadsheet-protected", "spreadsheet-unreadable", "spreadsheet-macros", "spreadsheet-too-large", "spreadsheet-no-table"];
501
+ export declare const UNPROCESSABLE_FILE_REASONS: readonly ["file-unreadable", "file-protected", "file-type-unsupported", "spreadsheet-protected", "spreadsheet-unreadable", "spreadsheet-macros", "spreadsheet-too-large", "spreadsheet-no-table", "audio-no-speech", "audio-too-large"];
502
502
  export type UnprocessableFileReason = (typeof UNPROCESSABLE_FILE_REASONS)[number];
503
503
  /** Every `reason` a failed document can carry in `metadata.lastIndexError`. */
504
504
  export declare const DOCUMENT_INDEX_ERROR_REASONS: readonly (DocumentRefusalReason | UnprocessableFileReason)[];
@@ -1,9 +1,36 @@
1
1
  import { z } from 'zod';
2
2
  export declare const MAX_FILE_SIZE: number;
3
+ /**
4
+ * The largest audio that is transcribed (spec `audio-file-transcription.md`,
5
+ * D2): about seven hours of m4a at 32 kbps, ten minutes of CD quality wav.
6
+ * The SDK sends it as base64 inside a JSON body, so a job holds about four
7
+ * times this in memory. The upload alert
8
+ * (`problemReasons.file-upload-validation.audio-too-large`) states it in MB.
9
+ */
10
+ export declare const MAX_AUDIO_FILE_SIZE: number;
3
11
  export declare const MIME_TYPE_REGEX: RegExp;
4
12
  export declare const SUPPORTED_IMAGE_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"];
5
13
  export declare const SUPPORTED_VIDEO_TYPES: readonly ["video/mp4", "video/webm", "video/ogg"];
6
- export declare const SUPPORTED_AUDIO_TYPES: readonly ["audio/mpeg", "audio/wav", "audio/ogg"];
14
+ /**
15
+ * The audio a transcription model reads (spec `audio-file-transcription.md`,
16
+ * D2), by every type a browser or a drive declares for it: an m4a arrives as
17
+ * `audio/mp4` or `audio/x-m4a` depending on the system, a wav as any of three.
18
+ * `audio/webm`, what a browser records, waits for a test.
19
+ */
20
+ export declare const SUPPORTED_AUDIO_TYPES: readonly ["audio/mpeg", "audio/mp4", "audio/x-m4a", "audio/aac", "audio/wav", "audio/x-wav", "audio/wave", "audio/ogg", "audio/flac", "audio/x-flac"];
21
+ /**
22
+ * Whether a file is audio of any kind, read or not: what a channel that does
23
+ * not transcribe has to keep away from the model, since most models refuse an
24
+ * audio part and the whole turn with it.
25
+ */
26
+ export declare const isAudioFile: (name?: string, mimeType?: string) => boolean;
27
+ /**
28
+ * Whether a file is audio the transcription reads: by its type, else by its
29
+ * extension when the type says nothing (none, `application/octet-stream`) or
30
+ * is another system's name for an audio of ours (`audio/mp3`,
31
+ * `audio/vnd.wave`, `audio/opus`): the model reads the bytes, not the type.
32
+ */
33
+ export declare const isTranscribableAudio: (name?: string, mimeType?: string) => boolean;
7
34
  export declare enum StoragePolicy {
8
35
  DEFAULT = "default",
9
36
  COLD = "cold",
@@ -132,6 +132,25 @@ export declare const MemberSchema: z.ZodObject<{
132
132
  }, z.core.$strip>;
133
133
  }, z.core.$strip>;
134
134
  export type Member = z.infer<typeof MemberSchema>;
135
+ /**
136
+ * One of the caller's own memberships, as the Organizations section of the
137
+ * profile shows it beside the organization's name: what the session's list
138
+ * of organizations does not carry.
139
+ */
140
+ export declare const OwnMembershipSchema: z.ZodObject<{
141
+ organizationId: z.ZodString;
142
+ role: z.ZodEnum<{
143
+ user: "user";
144
+ member: "member";
145
+ admin: "admin";
146
+ owner: "owner";
147
+ }>;
148
+ memberSince: z.ZodCoercedDate<unknown>;
149
+ memberCount: z.ZodNumber;
150
+ isOnlyOwner: z.ZodBoolean;
151
+ requiresTwoFactor: z.ZodBoolean;
152
+ }, z.core.$strip>;
153
+ export type OwnMembership = z.infer<typeof OwnMembershipSchema>;
135
154
  export declare const MemberUpdateSchema: z.ZodObject<{
136
155
  id: z.ZodString;
137
156
  organizationId: z.ZodString;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8wave/ai-elements",
3
3
  "private": false,
4
- "version": "0.116.0-beta.3",
4
+ "version": "0.117.0-beta.10",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite --port 8082 --open --host",
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdown-CqYYB88s.js","names":["z.object","z.string","z.array","z.boolean","z.object","z.string","z.number","z.enum","z.array","z.record","z.unknown","z.boolean","z.object","z.enum","z.string","z.boolean","z.number","z.literal","z.uuid","z.preprocess","z.discriminatedUnion","z.array","z.union","z.unknown","z.url","z.record","z.coerce.date","url","unknown","object","string","number","array","literal","_enum","boolean","date","preprocess","uuid","union","record","zodQs","MediaSchema","\"textEmbedding3Small\"","\"textEmbedding3Large\"","\"geminiEmbedding2\"","EmbeddingModel","SupportedLanguage","\"it\"","\"en\"","\"es\"","\"fr\"","\"de\"","ExtractionStrategy","\"READABILITY\"","\"AI\"","BrowserEngine","\"FETCH\"","\"PUPPETEER\"","\"REMOTE\"","HeaderSchema","HeadesSchema","decodeOnly","headers","header","key","TabularColumnSchema","TabularTableSchema","TabularSkippedSheetSchema","TabularConversionSchema","DocumentTabularSchema","IndexStatus","\"Pending\"","\"Processing\"","\"Success\"","\"Failed\"","SearchIndexStatus","\"Indexed\"","\"Unavailable\"","\"Empty\"","ConversionOptionsSchema","DocumentSourceProvider","\"Web\"","\"SharePoint\"","\"GoogleDrive\"","DocumentRefreshResult","\"Updated\"","\"Unchanged\"","\"Shrunk\"","\"Gone\"","DocumentRefreshInterval","\"Daily\"","\"Weekly\"","\"Monthly\"","DocumentLastRefreshSchema","commonSourceFields","WebDocumentSourceSchema","cloudSourceFields","SharePointDocumentSourceSchema","GoogleDriveDocumentSourceSchema","DocumentSourceSchema","value","discriminatedUnion","DOCUMENT_REFUSAL_REASONS","UNPROCESSABLE_FILE_REASONS","DocumentIndexErrorSchema","DocumentMetadataSchema","val","DocumentType","\"File\"","\"Url\"","\"RevisedAnswer\"","\"PlainText\"","DocumentSchema","data","ctx","getTypeIcon","type","isImageMime","mimeType","isInlineViewable","fileKindForMime","mime","fileIconForMime","stripMarkdown","text"],"sources":["../../../../packages/models/src/schema/constants.ts","../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Tabular.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-haiku-5.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 'mistral/mistral-large-4',\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\n/** Upper bound of a secret value, so a form cannot store an arbitrarily large blob. */\nexport const MAX_SECRET_VALUE_LENGTH = 4096\n\n/**\n * A header that carries a credential, as the API returns it: its name, and\n * whether a value is stored. The headers of API tools and MCP servers carry\n * the keys of the services they call (`Authorization`, `X-Api-Key`), so the\n * value never leaves the backend, the contract of the headers of the\n * organization's model provider.\n */\nexport const SecretHeaderSchema = z.object({\n key: z.string(),\n hasValue: z.boolean(),\n})\n\n/**\n * Secret headers as a save sends them. On an update an empty value keeps the\n * one stored under the same name, while the URL keeps its origin; a name left\n * out is removed. A row without a name is an empty row of the form, and\n * dropped. Two names that differ only in case are one HTTP header, so they\n * are refused: a kept value would match the first and the request send the\n * last.\n */\nexport const SecretHeaderListSchema = decodeOnly(\n z.array(\n z.object({\n key: z.string().optional(),\n value: z.string().max(MAX_SECRET_VALUE_LENGTH).optional(),\n }),\n ),\n z\n .array(z.object({ key: z.string(), value: z.string().optional() }))\n .refine(\n (headers) =>\n new Set(headers.map(({ key }) => key.toLowerCase())).size ===\n headers.length,\n { params: { i18n: 'validation.duplicateHeader' } },\n ),\n (headers) =>\n headers\n .filter((header) => header.key?.trim())\n .map(({ key, value }) => ({ key: key!.trim(), value })),\n)\n\nexport type SecretHeader = z.infer<typeof SecretHeaderSchema>\nexport type SecretHeaderDto = z.infer<typeof SecretHeaderListSchema>[number]\n/**\n * A secret header as stored (`agent_api_tools.headers`, `mcp_server.headers`),\n * with `value` encrypted by `encryptSecret`; rows saved before 0.115.0 hold it\n * in clear until their next save. Backend only: it never reaches a response.\n */\nexport type StoredSecretHeader = { key: string; value: string }\n","import * as z from 'zod'\n\n// Spreadsheets converted for SQL: the schema of each table, shared by the\n// converter (Azure Function), the query tool (backend) and the two places that\n// keep it (spec `tabular-data-sql-query.md`, \"Modello dati\").\n\nexport const TabularColumnSchema = z.object({\n /** The name in SQL: the header text, made unique. */\n name: z.string(),\n /** The DuckDB type. */\n type: z.string(),\n /** The Excel column letter, so a found record can be edited by address. */\n column: z.string().optional(),\n /** How many values are formula results, as stored in the file. */\n formulas: z.number().optional(),\n /**\n * A text column whose values are almost all of one other kind: dates with\n * a few notes in between, as in a real fleet export.\n */\n mostly: z\n .object({\n kind: z.enum(['date', 'number', 'boolean']),\n share: z.string(),\n })\n .optional(),\n})\n\nexport const TabularTableSchema = z.object({\n sheet: z.string(),\n /** The SQL name of the table. */\n table: z.string(),\n /** The Excel row of the header; the records start below it. */\n headerRow: z.number().optional(),\n rowCount: z.number(),\n columns: z.array(TabularColumnSchema),\n /** The first records, as JSON values. */\n sample: z.array(z.record(z.string(), z.unknown())),\n})\n\nexport const TabularSkippedSheetSchema = z.object({\n sheet: z.string(),\n /**\n * `chart` is a chart sheet; `no-table` has no header row among its first\n * thirty (a form, a summary, an empty sheet); `too-wide` has more columns\n * than a table takes; `unreadable` failed to convert.\n */\n reason: z.enum(['chart', 'no-table', 'too-wide', 'unreadable']),\n})\n\nexport const TabularConversionSchema = z.object({\n /** A conversion by an older converter is redone instead of trusted. */\n converterVersion: z.number(),\n tables: z.array(TabularTableSchema),\n skipped: z.array(TabularSkippedSheetSchema),\n /**\n * The workbook asks Excel to recalculate on open, as every version the\n * chat writes does: the formula results it stores may be stale.\n */\n staleFormulas: z.boolean(),\n})\n\nexport type TabularColumn = z.infer<typeof TabularColumnSchema>\nexport type TabularTable = z.infer<typeof TabularTableSchema>\nexport type TabularSkippedSheet = z.infer<typeof TabularSkippedSheetSchema>\nexport type TabularConversion = z.infer<typeof TabularConversionSchema>\n\n/** The conversion of a chat attachment, in `chat_attachments.tabular`. */\nexport const TabularStateSchema = z.object({\n status: z.enum(['pending', 'ready', 'failed']),\n /** When the conversion was last asked for, ISO. */\n requestedAt: z.string(),\n conversion: TabularConversionSchema.optional(),\n /** Why it failed, worded for the model to pass on. */\n failure: z.string().optional(),\n /** The converter that failed: a newer one tries again. */\n failedConverterVersion: z.number().optional(),\n})\n\nexport type TabularState = z.infer<typeof TabularStateSchema>\n\n/** The conversion of a knowledge-base file, in `documents.metadata.tabular`. */\nexport const DocumentTabularSchema = TabularConversionSchema.extend({\n /**\n * sha256 of the converter version and the file: a refresh with the same\n * file, converted the same way, changes nothing.\n */\n sourceHash: z.string(),\n})\n\nexport type DocumentTabular = z.infer<typeof DocumentTabularSchema>\n\n/** A knowledge-base spreadsheet an agent can query, with its schema. */\nexport interface TabularDocument {\n id: string\n name: string\n tabular: DocumentTabular\n}\n\n/**\n * The `document-processing` message that asks for the conversion of a chat\n * attachment, an uploaded spreadsheet or a version the agent wrote. It shares\n * the documents' queue, so no new queue is needed in Azure.\n */\nexport interface SpreadsheetConversionJob {\n kind: 'spreadsheet'\n attachmentId: string\n}\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'\nimport { DocumentTabularSchema, type SpreadsheetConversionJob } from './Tabular'\n\n/**\n * Status of the document pipeline: conversion and chunking. `Success` does not\n * say the chunks are in the index: that is {@link SearchIndexStatus}.\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n/**\n * Whether the search can find a document's chunks, apart from the pipeline\n * that produced them: `IndexStatus.Success` means converted and cut, and the\n * chunks reach the index afterwards, through the chunk sync\n * (docs/specs/kb-index-integrity.md, K1).\n */\nexport enum SearchIndexStatus {\n /** Every chunk has its point, as Qdrant confirmed to the sync. */\n Indexed = 'indexed',\n /** Chunks waiting for the sync. */\n Pending = 'pending',\n /** The sync gave up on these chunks; a reprocess tries again. */\n Failed = 'failed',\n /** The organization cannot compute embeddings right now. */\n Unavailable = 'unavailable',\n /** No chunk at all: there is nothing for the search to find. */\n Empty = 'empty',\n}\n\n/** What {@link searchIndexStatusOf} needs to know about one document. */\nexport interface SearchIndexFacts {\n /** Chunk rows of the document. */\n chunks: number\n /** Of those, the rows whose point the sync wrote and Qdrant confirmed. */\n syncedChunks: number\n /** When the newest chunk row was written, in epoch milliseconds. */\n newestChunkAt: number | null\n /** `metadata.searchIndexError.at`, written when a sync dead-letters. */\n searchIndexFailedAt: string | null | undefined\n /** Whether the organization can compute embeddings right now. */\n canEmbed: boolean\n /**\n * A spreadsheet read as tables and converted (`metadata.tabular`, index\n * status `success`): the agent queries it with SQL, and it has no chunks\n * by design.\n */\n tabular?: boolean\n}\n\n/**\n * The searchability of a document, from its chunk flags.\n *\n * The flag is the one source: the sync sets it only after Qdrant has counted\n * the points it wrote, and every path that rewrites or revives chunks clears\n * it. A recorded failure counts only against the chunks it was about: rows\n * written after it belong to a newer sync, still to come.\n */\nexport function searchIndexStatusOf(\n facts: SearchIndexFacts,\n): SearchIndexStatus {\n // Available as soon as it is converted: without chunks it would read as\n // empty, while its tables are what the agent searches.\n if (facts.tabular) {\n return SearchIndexStatus.Indexed\n }\n if (facts.chunks === 0) {\n return SearchIndexStatus.Empty\n }\n if (facts.syncedChunks >= facts.chunks) {\n return SearchIndexStatus.Indexed\n }\n const failedAt = facts.searchIndexFailedAt\n ? Date.parse(facts.searchIndexFailedAt)\n : Number.NaN\n if (\n Number.isFinite(failedAt) &&\n facts.newestChunkAt !== null &&\n facts.newestChunkAt <= failedAt\n ) {\n return SearchIndexStatus.Failed\n }\n return facts.canEmbed\n ? SearchIndexStatus.Pending\n : SearchIndexStatus.Unavailable\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 * `false` keeps a spreadsheet as text, chunked like any other file: a\n * user's file, searched with the others in \"My files\", which\n * `querySpreadsheet` does not reach. A spreadsheet otherwise becomes tables.\n */\n asTables: z.boolean().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/** Everything the `document-processing` queue carries. */\nexport type DocumentQueueMessage =\n DocumentProcessingJob | SpreadsheetConversionJob\n\n/**\n * Why a document is not processed because of the organization's AI settings,\n * as `DocumentRefusal` in `database/documentAi` says.\n */\nexport const DOCUMENT_REFUSAL_REASONS = [\n /** What it needs runs on the gateway, which the organization does not use. */\n 'gateway-disabled',\n /** What it needs runs on the organization's endpoint, which is off. */\n 'endpoint-off',\n /**\n * The model it names is not switched on: on the endpoint, or among the\n * organization's gateway models, with no platform model to take its place.\n */\n 'model-unavailable',\n /** No model to run the AI on: none chosen, none in the environment. */\n 'no-model',\n /** An endpoint model is asked to read a format it cannot be sent. */\n 'format-unsupported',\n /** OCR is ours, and without the gateway nothing else stops at zero. */\n 'credit-exhausted',\n] as const\n\nexport type DocumentRefusalReason = (typeof DOCUMENT_REFUSAL_REASONS)[number]\n\n/**\n * Why a file is refused for what it is, as `UnprocessableFileError` in the\n * document processor says.\n */\nexport const UNPROCESSABLE_FILE_REASONS = [\n /** Damaged, or not the format its name says. */\n 'file-unreadable',\n /** Protected by a password. */\n 'file-protected',\n /** A type nothing here converts. */\n 'file-type-unsupported',\n /** Encrypted, or an old .xls renamed to .xlsx. */\n 'spreadsheet-protected',\n /** Not a valid .xlsx package. */\n 'spreadsheet-unreadable',\n 'spreadsheet-macros',\n /** Over the cap on what a read inflates, `limitBytes` says which. */\n 'spreadsheet-too-large',\n /** No sheet with a header row, so nothing to query. */\n 'spreadsheet-no-table',\n] as const\n\nexport type UnprocessableFileReason =\n (typeof UNPROCESSABLE_FILE_REASONS)[number]\n\n/** Every `reason` a failed document can carry in `metadata.lastIndexError`. */\nexport const DOCUMENT_INDEX_ERROR_REASONS: readonly (\n DocumentRefusalReason | UnprocessableFileReason\n)[] = [...DOCUMENT_REFUSAL_REASONS, ...UNPROCESSABLE_FILE_REASONS]\n\n/**\n * Why the last run of a document failed in a way no other attempt can change,\n * as the processor records it in `metadata.lastIndexError`. A dead source URL\n * carries its HTTP `status` and `url`; anything else one of the\n * {@link DOCUMENT_INDEX_ERROR_REASONS}, with the `limitBytes` it went past\n * when it is too large. `reason` stays a string here, so a row written by a\n * newer processor still reads. `message` is the English one of the logs.\n */\nexport const DocumentIndexErrorSchema = z\n .object({\n reason: z.string().optional(),\n status: z.number().optional(),\n url: z.string().optional(),\n limitBytes: z.number().optional(),\n message: z.string().optional(),\n at: z.string().optional(),\n })\n .loose()\n\nexport type DocumentIndexError = z.infer<typeof DocumentIndexErrorSchema>\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 /**\n * The chunk sync of this document dead-lettered: written by the DLQ\n * drain, read by `searchIndexStatusOf` against the chunks' age.\n */\n searchIndexError: z.object({ at: z.string() }).optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n\n // === Spreadsheets queried with SQL instead of chunked ===\n tabular: DocumentTabularSchema.optional(),\n\n // === Why the last run failed for good, removed by a success ===\n // A shape nobody expected drops the reason, not the whole response.\n lastIndexError: DocumentIndexErrorSchema.optional().catch(undefined),\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 /** Whether the search can find the chunks (K1), apart from `indexStatus`. */\n searchStatus: z.enum(SearchIndexStatus).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 /** Who sent the document to the trash, by name (populated in trash reads). */\n deletedByName: z.string().nullable().optional(),\n})\n\n/**\n * The document types the trash holds: the ones a person adds to a knowledge\n * base by hand. A revised answer lives and dies with its own page, so it is\n * never listed, restored or emptied from here.\n */\nexport const DOCUMENT_TRASH_TYPES = [\n DocumentType.File,\n DocumentType.Url,\n DocumentType.PlainText,\n] as const\n\n/**\n * Why a restore was refused (docs/specs/document-trash.md, Decision 3), sent\n * as `additionalData.reason` of a `conflict` problem. The third case of the\n * decision, a folder that no longer exists, is not a refusal: the document\n * goes back to the root and the answer says so.\n */\nexport enum DocumentRestoreRefusal {\n /** Every agent the document belonged to has been deleted. */\n AgentGone = 'agent-gone',\n /**\n * A live document of the same source holds its remote identity; its id\n * travels as `additionalData.documentId`.\n */\n IdentityTaken = 'identity-taken',\n}\n\nexport const DocumentRestoreResultSchema = z.object({\n document: DocumentSchema,\n /** Its folder was gone, so it went back to the root of its agent. */\n returnedToRoot: z.boolean(),\n})\n\nexport const DocumentTrashSummarySchema = z.object({\n /** Documents in the trash of the agent, the ones it can restore. */\n count: z.number().int(),\n /**\n * Days a document stays in the trash before it is destroyed for good,\n * `0` when nothing destroys it (`trashRetentionDaysOf`).\n */\n retentionDays: z.number().int().min(0),\n})\n\nexport const DocumentEmptyTrashDtoSchema = z.object({\n agentId: z.uuid(),\n})\n\nexport const DocumentEmptyTrashResultSchema = z.object({\n purged: z.number().int(),\n /** Still in the trash: one call empties a batch, the caller asks again. */\n remaining: z.number().int(),\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', 'deletedAt']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n // 'true' lists the trash instead of the knowledge base: a flat list, by\n // agent, of what that agent can restore. Every other read keeps ignoring\n // deleted documents.\n 'filter[deleted]': z.enum(['true', 'false']).optional(),\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>\nexport type DocumentRestoreResult = z.infer<typeof DocumentRestoreResultSchema>\nexport type DocumentTrashSummary = z.infer<typeof DocumentTrashSummarySchema>\nexport type DocumentEmptyTrashDto = z.infer<typeof DocumentEmptyTrashDtoSchema>\nexport type DocumentEmptyTrashResult = z.infer<\n typeof DocumentEmptyTrashResultSchema\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\n/**\n * The color of a search state: green when the agents find the document, the\n * wait and the fixable ones in warning, the failure in danger, and gray where\n * there is nothing to find.\n */\nexport const getSearchStatusColor = (status: SearchIndexStatus) => {\n switch (status) {\n case SearchIndexStatus.Indexed:\n return 'success'\n case SearchIndexStatus.Failed:\n return 'danger'\n case SearchIndexStatus.Empty:\n return 'gray'\n default:\n return 'warning'\n }\n}\n\nexport const getSearchStatusIcon = (status: SearchIndexStatus) => {\n switch (status) {\n case SearchIndexStatus.Indexed:\n return 'ri:check-line'\n case SearchIndexStatus.Pending:\n return 'ri:hourglass-line'\n case SearchIndexStatus.Empty:\n return 'ri:indeterminate-circle-line'\n default:\n return 'ri:error-warning-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-DoWHaIlA.js","names":["z.object","z.string","z.array","z.boolean","z.object","z.string","z.number","z.enum","z.array","z.record","z.unknown","z.boolean","z.object","z.enum","z.string","z.boolean","z.number","z.literal","z.uuid","z.preprocess","z.discriminatedUnion","z.array","z.union","z.unknown","z.url","z.record","z.coerce.date","unknown","union","uuid","number","record","literal","_enum","boolean","discriminatedUnion","object","url","string","preprocess","date","MediaSchema","zodQs","decodeOnly","GATEWAY_MODELS","PER_MILLION_TOKENS","EMBED_MODEL","\"textEmbedding3Small\"","\"textEmbedding3Large\"","\"geminiEmbedding2\"","EmbeddingModel","EVALUATION_MODEL","SupportedLanguage","\"it\"","\"en\"","\"es\"","\"fr\"","\"de\"","ExtractionStrategy","\"READABILITY\"","\"AI\"","BrowserEngine","\"FETCH\"","\"PUPPETEER\"","\"REMOTE\"","BYOK_MODEL_PREFIX","isByokModel","id","HeaderSchema","HeadesSchema","array","headers","header","key","TabularColumnSchema","TabularTableSchema","TabularSkippedSheetSchema","TabularConversionSchema","DocumentTabularSchema","IndexStatus","\"Pending\"","\"Processing\"","\"Success\"","\"Failed\"","SearchIndexStatus","\"Indexed\"","\"Unavailable\"","\"Empty\"","ConversionOptionsSchema","DocumentSourceProvider","\"Web\"","\"SharePoint\"","\"GoogleDrive\"","DocumentRefreshResult","\"Updated\"","\"Unchanged\"","\"Shrunk\"","\"Gone\"","DocumentRefreshInterval","\"Daily\"","\"Weekly\"","\"Monthly\"","DocumentLastRefreshSchema","commonSourceFields","WebDocumentSourceSchema","cloudSourceFields","SharePointDocumentSourceSchema","GoogleDriveDocumentSourceSchema","DocumentSourceSchema","value","DOCUMENT_REFUSAL_REASONS","UNPROCESSABLE_FILE_REASONS","DocumentIndexErrorSchema","DocumentMetadataSchema","val","DocumentType","\"File\"","\"Url\"","\"RevisedAnswer\"","\"PlainText\"","DocumentSchema","data","ctx","getTypeIcon","type","getFileIcon","extension","isImageMime","mimeType","isInlineViewable","fileKindForMime","mime","fileIconForMime"],"sources":["../../../../packages/models/src/schema/constants.ts","../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Tabular.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-haiku-5.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 'mistral/mistral-large-4',\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\n/** Upper bound of a secret value, so a form cannot store an arbitrarily large blob. */\nexport const MAX_SECRET_VALUE_LENGTH = 4096\n\n/**\n * A header that carries a credential, as the API returns it: its name, and\n * whether a value is stored. The headers of API tools and MCP servers carry\n * the keys of the services they call (`Authorization`, `X-Api-Key`), so the\n * value never leaves the backend, the contract of the headers of the\n * organization's model provider.\n */\nexport const SecretHeaderSchema = z.object({\n key: z.string(),\n hasValue: z.boolean(),\n})\n\n/**\n * Secret headers as a save sends them. On an update an empty value keeps the\n * one stored under the same name, while the URL keeps its origin; a name left\n * out is removed. A row without a name is an empty row of the form, and\n * dropped. Two names that differ only in case are one HTTP header, so they\n * are refused: a kept value would match the first and the request send the\n * last.\n */\nexport const SecretHeaderListSchema = decodeOnly(\n z.array(\n z.object({\n key: z.string().optional(),\n value: z.string().max(MAX_SECRET_VALUE_LENGTH).optional(),\n }),\n ),\n z\n .array(z.object({ key: z.string(), value: z.string().optional() }))\n .refine(\n (headers) =>\n new Set(headers.map(({ key }) => key.toLowerCase())).size ===\n headers.length,\n { params: { i18n: 'validation.duplicateHeader' } },\n ),\n (headers) =>\n headers\n .filter((header) => header.key?.trim())\n .map(({ key, value }) => ({ key: key!.trim(), value })),\n)\n\nexport type SecretHeader = z.infer<typeof SecretHeaderSchema>\nexport type SecretHeaderDto = z.infer<typeof SecretHeaderListSchema>[number]\n/**\n * A secret header as stored (`agent_api_tools.headers`, `mcp_server.headers`),\n * with `value` encrypted by `encryptSecret`; rows saved before 0.115.0 hold it\n * in clear until their next save. Backend only: it never reaches a response.\n */\nexport type StoredSecretHeader = { key: string; value: string }\n","import * as z from 'zod'\n\n// Spreadsheets converted for SQL: the schema of each table, shared by the\n// converter (Azure Function), the query tool (backend) and the two places that\n// keep it (spec `tabular-data-sql-query.md`, \"Modello dati\").\n\nexport const TabularColumnSchema = z.object({\n /** The name in SQL: the header text, made unique. */\n name: z.string(),\n /** The DuckDB type. */\n type: z.string(),\n /** The Excel column letter, so a found record can be edited by address. */\n column: z.string().optional(),\n /** How many values are formula results, as stored in the file. */\n formulas: z.number().optional(),\n /**\n * A text column whose values are almost all of one other kind: dates with\n * a few notes in between, as in a real fleet export.\n */\n mostly: z\n .object({\n kind: z.enum(['date', 'number', 'boolean']),\n share: z.string(),\n })\n .optional(),\n})\n\nexport const TabularTableSchema = z.object({\n sheet: z.string(),\n /** The SQL name of the table. */\n table: z.string(),\n /** The Excel row of the header; the records start below it. */\n headerRow: z.number().optional(),\n rowCount: z.number(),\n columns: z.array(TabularColumnSchema),\n /** The first records, as JSON values. */\n sample: z.array(z.record(z.string(), z.unknown())),\n})\n\nexport const TabularSkippedSheetSchema = z.object({\n sheet: z.string(),\n /**\n * `chart` is a chart sheet; `no-table` has no header row among its first\n * thirty (a form, a summary, an empty sheet); `too-wide` has more columns\n * than a table takes; `unreadable` failed to convert.\n */\n reason: z.enum(['chart', 'no-table', 'too-wide', 'unreadable']),\n})\n\nexport const TabularConversionSchema = z.object({\n /** A conversion by an older converter is redone instead of trusted. */\n converterVersion: z.number(),\n tables: z.array(TabularTableSchema),\n skipped: z.array(TabularSkippedSheetSchema),\n /**\n * The workbook asks Excel to recalculate on open, as every version the\n * chat writes does: the formula results it stores may be stale.\n */\n staleFormulas: z.boolean(),\n})\n\nexport type TabularColumn = z.infer<typeof TabularColumnSchema>\nexport type TabularTable = z.infer<typeof TabularTableSchema>\nexport type TabularSkippedSheet = z.infer<typeof TabularSkippedSheetSchema>\nexport type TabularConversion = z.infer<typeof TabularConversionSchema>\n\n/** The conversion of a chat attachment, in `chat_attachments.tabular`. */\nexport const TabularStateSchema = z.object({\n status: z.enum(['pending', 'ready', 'failed']),\n /** When the conversion was last asked for, ISO. */\n requestedAt: z.string(),\n conversion: TabularConversionSchema.optional(),\n /** Why it failed, worded for the model to pass on. */\n failure: z.string().optional(),\n /** The converter that failed: a newer one tries again. */\n failedConverterVersion: z.number().optional(),\n})\n\nexport type TabularState = z.infer<typeof TabularStateSchema>\n\n/** The conversion of a knowledge-base file, in `documents.metadata.tabular`. */\nexport const DocumentTabularSchema = TabularConversionSchema.extend({\n /**\n * sha256 of the converter version and the file: a refresh with the same\n * file, converted the same way, changes nothing.\n */\n sourceHash: z.string(),\n})\n\nexport type DocumentTabular = z.infer<typeof DocumentTabularSchema>\n\n/** A knowledge-base spreadsheet an agent can query, with its schema. */\nexport interface TabularDocument {\n id: string\n name: string\n tabular: DocumentTabular\n}\n\n/**\n * The `document-processing` message that asks for the conversion of a chat\n * attachment, an uploaded spreadsheet or a version the agent wrote. It shares\n * the documents' queue, so no new queue is needed in Azure.\n */\nexport interface SpreadsheetConversionJob {\n kind: 'spreadsheet'\n attachmentId: string\n}\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'\nimport { DocumentTabularSchema, type SpreadsheetConversionJob } from './Tabular'\n\n/**\n * Status of the document pipeline: conversion and chunking. `Success` does not\n * say the chunks are in the index: that is {@link SearchIndexStatus}.\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n/**\n * Whether the search can find a document's chunks, apart from the pipeline\n * that produced them: `IndexStatus.Success` means converted and cut, and the\n * chunks reach the index afterwards, through the chunk sync\n * (docs/specs/kb-index-integrity.md, K1).\n */\nexport enum SearchIndexStatus {\n /** Every chunk has its point, as Qdrant confirmed to the sync. */\n Indexed = 'indexed',\n /** Chunks waiting for the sync. */\n Pending = 'pending',\n /** The sync gave up on these chunks; a reprocess tries again. */\n Failed = 'failed',\n /** The organization cannot compute embeddings right now. */\n Unavailable = 'unavailable',\n /** No chunk at all: there is nothing for the search to find. */\n Empty = 'empty',\n}\n\n/** What {@link searchIndexStatusOf} needs to know about one document. */\nexport interface SearchIndexFacts {\n /** Chunk rows of the document. */\n chunks: number\n /** Of those, the rows whose point the sync wrote and Qdrant confirmed. */\n syncedChunks: number\n /** When the newest chunk row was written, in epoch milliseconds. */\n newestChunkAt: number | null\n /** `metadata.searchIndexError.at`, written when a sync dead-letters. */\n searchIndexFailedAt: string | null | undefined\n /** Whether the organization can compute embeddings right now. */\n canEmbed: boolean\n /**\n * A spreadsheet read as tables and converted (`metadata.tabular`, index\n * status `success`): the agent queries it with SQL, and it has no chunks\n * by design.\n */\n tabular?: boolean\n}\n\n/**\n * The searchability of a document, from its chunk flags.\n *\n * The flag is the one source: the sync sets it only after Qdrant has counted\n * the points it wrote, and every path that rewrites or revives chunks clears\n * it. A recorded failure counts only against the chunks it was about: rows\n * written after it belong to a newer sync, still to come.\n */\nexport function searchIndexStatusOf(\n facts: SearchIndexFacts,\n): SearchIndexStatus {\n // Available as soon as it is converted: without chunks it would read as\n // empty, while its tables are what the agent searches.\n if (facts.tabular) {\n return SearchIndexStatus.Indexed\n }\n if (facts.chunks === 0) {\n return SearchIndexStatus.Empty\n }\n if (facts.syncedChunks >= facts.chunks) {\n return SearchIndexStatus.Indexed\n }\n const failedAt = facts.searchIndexFailedAt\n ? Date.parse(facts.searchIndexFailedAt)\n : Number.NaN\n if (\n Number.isFinite(failedAt) &&\n facts.newestChunkAt !== null &&\n facts.newestChunkAt <= failedAt\n ) {\n return SearchIndexStatus.Failed\n }\n return facts.canEmbed\n ? SearchIndexStatus.Pending\n : SearchIndexStatus.Unavailable\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 * `false` keeps a spreadsheet as text, chunked like any other file: a\n * user's file, searched with the others in \"My files\", which\n * `querySpreadsheet` does not reach. A spreadsheet otherwise becomes tables.\n */\n asTables: z.boolean().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/** Everything the `document-processing` queue carries. */\nexport type DocumentQueueMessage =\n DocumentProcessingJob | SpreadsheetConversionJob\n\n/**\n * Why a document is not processed because of the organization's AI settings,\n * as `DocumentRefusal` in `database/documentAi` says.\n */\nexport const DOCUMENT_REFUSAL_REASONS = [\n /** What it needs runs on the gateway, which the organization does not use. */\n 'gateway-disabled',\n /** What it needs runs on the organization's endpoint, which is off. */\n 'endpoint-off',\n /**\n * The model it names is not switched on: on the endpoint, or among the\n * organization's gateway models, with no platform model to take its place.\n */\n 'model-unavailable',\n /** No model to run the AI on: none chosen, none in the environment. */\n 'no-model',\n /** An endpoint model is asked to read a format it cannot be sent. */\n 'format-unsupported',\n /** OCR is ours, and without the gateway nothing else stops at zero. */\n 'credit-exhausted',\n] as const\n\nexport type DocumentRefusalReason = (typeof DOCUMENT_REFUSAL_REASONS)[number]\n\n/**\n * Why a file is refused for what it is, as `UnprocessableFileError` in the\n * document processor says.\n */\nexport const UNPROCESSABLE_FILE_REASONS = [\n /** Damaged, or not the format its name says. */\n 'file-unreadable',\n /** Protected by a password. */\n 'file-protected',\n /** A type nothing here converts. */\n 'file-type-unsupported',\n /** Encrypted, or an old .xls renamed to .xlsx. */\n 'spreadsheet-protected',\n /** Not a valid .xlsx package. */\n 'spreadsheet-unreadable',\n 'spreadsheet-macros',\n /** Over the cap on what a read inflates, `limitBytes` says which. */\n 'spreadsheet-too-large',\n /** No sheet with a header row, so nothing to query. */\n 'spreadsheet-no-table',\n] as const\n\nexport type UnprocessableFileReason =\n (typeof UNPROCESSABLE_FILE_REASONS)[number]\n\n/** Every `reason` a failed document can carry in `metadata.lastIndexError`. */\nexport const DOCUMENT_INDEX_ERROR_REASONS: readonly (\n DocumentRefusalReason | UnprocessableFileReason\n)[] = [...DOCUMENT_REFUSAL_REASONS, ...UNPROCESSABLE_FILE_REASONS]\n\n/**\n * Why the last run of a document failed in a way no other attempt can change,\n * as the processor records it in `metadata.lastIndexError`. A dead source URL\n * carries its HTTP `status` and `url`; anything else one of the\n * {@link DOCUMENT_INDEX_ERROR_REASONS}, with the `limitBytes` it went past\n * when it is too large. `reason` stays a string here, so a row written by a\n * newer processor still reads. `message` is the English one of the logs.\n */\nexport const DocumentIndexErrorSchema = z\n .object({\n reason: z.string().optional(),\n status: z.number().optional(),\n url: z.string().optional(),\n limitBytes: z.number().optional(),\n message: z.string().optional(),\n at: z.string().optional(),\n })\n .loose()\n\nexport type DocumentIndexError = z.infer<typeof DocumentIndexErrorSchema>\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 /**\n * The chunk sync of this document dead-lettered: written by the DLQ\n * drain, read by `searchIndexStatusOf` against the chunks' age.\n */\n searchIndexError: z.object({ at: z.string() }).optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n\n // === Spreadsheets queried with SQL instead of chunked ===\n tabular: DocumentTabularSchema.optional(),\n\n // === Why the last run failed for good, removed by a success ===\n // A shape nobody expected drops the reason, not the whole response.\n lastIndexError: DocumentIndexErrorSchema.optional().catch(undefined),\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 /** Whether the search can find the chunks (K1), apart from `indexStatus`. */\n searchStatus: z.enum(SearchIndexStatus).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 /** Who sent the document to the trash, by name (populated in trash reads). */\n deletedByName: z.string().nullable().optional(),\n})\n\n/**\n * The document types the trash holds: the ones a person adds to a knowledge\n * base by hand. A revised answer lives and dies with its own page, so it is\n * never listed, restored or emptied from here.\n */\nexport const DOCUMENT_TRASH_TYPES = [\n DocumentType.File,\n DocumentType.Url,\n DocumentType.PlainText,\n] as const\n\n/**\n * Why a restore was refused (docs/specs/document-trash.md, Decision 3), sent\n * as `additionalData.reason` of a `conflict` problem. The third case of the\n * decision, a folder that no longer exists, is not a refusal: the document\n * goes back to the root and the answer says so.\n */\nexport enum DocumentRestoreRefusal {\n /** Every agent the document belonged to has been deleted. */\n AgentGone = 'agent-gone',\n /**\n * A live document of the same source holds its remote identity; its id\n * travels as `additionalData.documentId`.\n */\n IdentityTaken = 'identity-taken',\n}\n\nexport const DocumentRestoreResultSchema = z.object({\n document: DocumentSchema,\n /** Its folder was gone, so it went back to the root of its agent. */\n returnedToRoot: z.boolean(),\n})\n\nexport const DocumentTrashSummarySchema = z.object({\n /** Documents in the trash of the agent, the ones it can restore. */\n count: z.number().int(),\n /**\n * Days a document stays in the trash before it is destroyed for good,\n * `0` when nothing destroys it (`trashRetentionDaysOf`).\n */\n retentionDays: z.number().int().min(0),\n})\n\nexport const DocumentEmptyTrashDtoSchema = z.object({\n agentId: z.uuid(),\n})\n\nexport const DocumentEmptyTrashResultSchema = z.object({\n purged: z.number().int(),\n /** Still in the trash: one call empties a batch, the caller asks again. */\n remaining: z.number().int(),\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', 'deletedAt']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n // 'true' lists the trash instead of the knowledge base: a flat list, by\n // agent, of what that agent can restore. Every other read keeps ignoring\n // deleted documents.\n 'filter[deleted]': z.enum(['true', 'false']).optional(),\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>\nexport type DocumentRestoreResult = z.infer<typeof DocumentRestoreResultSchema>\nexport type DocumentTrashSummary = z.infer<typeof DocumentTrashSummarySchema>\nexport type DocumentEmptyTrashDto = z.infer<typeof DocumentEmptyTrashDtoSchema>\nexport type DocumentEmptyTrashResult = z.infer<\n typeof DocumentEmptyTrashResultSchema\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\n/**\n * The color of a search state: green when the agents find the document, the\n * wait and the fixable ones in warning, the failure in danger, and gray where\n * there is nothing to find.\n */\nexport const getSearchStatusColor = (status: SearchIndexStatus) => {\n switch (status) {\n case SearchIndexStatus.Indexed:\n return 'success'\n case SearchIndexStatus.Failed:\n return 'danger'\n case SearchIndexStatus.Empty:\n return 'gray'\n default:\n return 'warning'\n }\n}\n\nexport const getSearchStatusIcon = (status: SearchIndexStatus) => {\n switch (status) {\n case SearchIndexStatus.Indexed:\n return 'ri:check-line'\n case SearchIndexStatus.Pending:\n return 'ri:hourglass-line'\n case SearchIndexStatus.Empty:\n return 'ri:indeterminate-circle-line'\n default:\n return 'ri:error-warning-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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"}