diff --git a/tools/ui/pwa-assets-dark.config.ts b/tools/ui/pwa-assets-dark.config.ts
index 1446b47a8..4d8114ee7 100644
--- a/tools/ui/pwa-assets-dark.config.ts
+++ b/tools/ui/pwa-assets-dark.config.ts
@@ -1,5 +1,5 @@
import { writeThemeFavicons } from './scripts/favicon-colorize';
-import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa';
+import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa.constants';
import { defineConfig } from '@vite-pwa/assets-generator/config';
writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK, {
diff --git a/tools/ui/pwa-assets.config.ts b/tools/ui/pwa-assets.config.ts
index 5fed0a595..f9f8662a2 100644
--- a/tools/ui/pwa-assets.config.ts
+++ b/tools/ui/pwa-assets.config.ts
@@ -4,7 +4,7 @@ import {
PWA_ASSET_GENERATOR,
PWA_GENERATOR_DEVICES,
THEME_COLORS
-} from './src/lib/constants/pwa';
+} from './src/lib/constants/pwa.constants';
import { SplashOrientation } from './src/lib/enums/splash.enums';
import {
combinePresetAndAppleSplashScreens,
diff --git a/tools/ui/scripts/vite-plugin-build-info.ts b/tools/ui/scripts/vite-plugin-build-info.ts
index 800238630..ec864e8d0 100644
--- a/tools/ui/scripts/vite-plugin-build-info.ts
+++ b/tools/ui/scripts/vite-plugin-build-info.ts
@@ -1,4 +1,4 @@
-import { BUILD_CONFIG } from '../src/lib/constants/pwa';
+import { BUILD_CONFIG } from '../src/lib/constants/pwa.constants';
import { existsSync, writeFileSync } from 'node:fs';
import { resolve } from 'path';
import type { Plugin } from 'vite';
diff --git a/tools/ui/scripts/vite-plugin-relativize-base.ts b/tools/ui/scripts/vite-plugin-relativize-base.ts
index f8eac1d66..0e47741ae 100644
--- a/tools/ui/scripts/vite-plugin-relativize-base.ts
+++ b/tools/ui/scripts/vite-plugin-relativize-base.ts
@@ -1,4 +1,4 @@
-import { BUILD_CONFIG } from '../src/lib/constants/pwa';
+import { BUILD_CONFIG } from '../src/lib/constants/pwa.constants';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { resolve } from 'path';
import type { Plugin } from 'vite';
diff --git a/tools/ui/scripts/vite-plugin-splash-screen.ts b/tools/ui/scripts/vite-plugin-splash-screen.ts
index 45d931bab..62b7a063a 100644
--- a/tools/ui/scripts/vite-plugin-splash-screen.ts
+++ b/tools/ui/scripts/vite-plugin-splash-screen.ts
@@ -1,5 +1,10 @@
-import { NEWLINE, TAB } from '../src/lib/constants/code';
-import { APPLE_DEVICES, BUILD_CONFIG, REGEX_PATTERNS, SPLASH_LINK } from '../src/lib/constants/pwa';
+import {
+ APPLE_DEVICES,
+ BUILD_CONFIG,
+ REGEX_PATTERNS,
+ SPLASH_LINK
+} from '../src/lib/constants/pwa.constants';
+import { NEWLINE, TAB } from '../src/lib/constants/special-characters.constants';
import { SplashOrientation } from '../src/lib/enums/splash.enums';
import type { SplashDimensions } from '../src/lib/types';
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
diff --git a/tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte b/tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte
index 7655f18e8..2d54df89d 100644
--- a/tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte
+++ b/tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte
@@ -1,7 +1,7 @@
diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenServerError.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenServerError.svelte
index ae5d91bee..cae37d6f8 100644
--- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenServerError.svelte
+++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenServerError.svelte
@@ -1,7 +1,7 @@
diff --git a/tools/ui/src/lib/constants/agentic.ts b/tools/ui/src/lib/constants/agentic.constants.ts
similarity index 77%
rename from tools/ui/src/lib/constants/agentic.ts
rename to tools/ui/src/lib/constants/agentic.constants.ts
index 582572a29..e57104e8a 100644
--- a/tools/ui/src/lib/constants/agentic.ts
+++ b/tools/ui/src/lib/constants/agentic.constants.ts
@@ -5,22 +5,14 @@ export const ATTACHMENT_SAVED_REGEX = /\[Attachment saved: ([^\]]+)\]/;
// JSON detection: trimmed content opens with an object or array literal.
export const TOOL_RESULT_JSON_OPEN_REGEX = /^[[{]/;
-// Markdown structural markers used by `looksLikeMarkdown`. Inline / line-level.
-export const MARKDOWN_CODE_FENCE_REGEX = /^(```|~~~)/m;
-export const MARKDOWN_ATX_HEADING_REGEX = /^#{1,6}\s+\S/;
-export const MARKDOWN_BLOCKQUOTE_REGEX = /^>\s+\S/;
-export const MARKDOWN_LIST_BULLET_REGEX = /^\s*[-*+]\s+\S/;
-export const MARKDOWN_LIST_NUMBERED_REGEX = /^\s*\d+[.)]\s+\S/;
-export const MARKDOWN_LINK_REGEX = /\[[^\]\n]+\]\([^)\s]+\)/;
-export const MARKDOWN_BOLD_REGEX = /\*\*[^*\n]+\*\*|__[^_\n]+__/;
-export const MARKDOWN_TABLE_SEPARATOR_REGEX = /^\s*\|?[\s:|-]+\|?\s*$/;
-
// Search-summary wire format used by file-glob and grep tools:
//
// ---
// Total matches: N
-export const SEARCH_SUMMARY_SEPARATOR = '---\n';
-export const SEARCH_SUMMARY_TOTAL_REGEX = /Total matches:\s*(\d+)/;
+export const SEARCH_SUMMARY = {
+ SEPARATOR: '---\n',
+ TOTAL_REGEX: /Total matches:\s*(\d+)/
+} as const;
// Separator rendered between stats in the tool-result footer (e.g. between a
// result message and the byte/edit count). Plain ASCII spaces bracket a hyphen
diff --git a/tools/ui/src/lib/constants/api-endpoints.ts b/tools/ui/src/lib/constants/api-endpoints.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/api-endpoints.ts
rename to tools/ui/src/lib/constants/api-endpoints.constants.ts
diff --git a/tools/ui/src/lib/constants/app.ts b/tools/ui/src/lib/constants/app.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/app.ts
rename to tools/ui/src/lib/constants/app.constants.ts
diff --git a/tools/ui/src/lib/constants/attachment-labels.ts b/tools/ui/src/lib/constants/attachment-labels.ts
deleted file mode 100644
index be9999c0f..000000000
--- a/tools/ui/src/lib/constants/attachment-labels.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export const ATTACHMENT_LABEL_FILE = 'File';
-export const ATTACHMENT_LABEL_PDF_FILE = 'PDF File';
-export const ATTACHMENT_LABEL_MCP_PROMPT = 'MCP Prompt';
-export const ATTACHMENT_LABEL_MCP_RESOURCE = 'MCP Resource';
diff --git a/tools/ui/src/lib/constants/attachment-menu.ts b/tools/ui/src/lib/constants/attachment-menu.constants.ts
similarity index 75%
rename from tools/ui/src/lib/constants/attachment-menu.ts
rename to tools/ui/src/lib/constants/attachment-menu.constants.ts
index 1cd7f9ba5..62e03bea6 100644
--- a/tools/ui/src/lib/constants/attachment-menu.ts
+++ b/tools/ui/src/lib/constants/attachment-menu.constants.ts
@@ -1,33 +1,12 @@
import { FolderOpen, MessageSquare, Zap } from '@lucide/svelte';
-import { FILE_TYPE_ICONS } from '$lib/constants/icons';
+import { FILE_TYPE_ICONS } from '$lib/constants';
import {
AttachmentAction,
AttachmentItemEnabledWhen,
AttachmentItemVisibleWhen,
AttachmentMenuItemId
} from '$lib/enums';
-import type { Component } from 'svelte';
-
-export interface AttachmentMenuItem {
- /** Unique identifier for the item */
- id: AttachmentMenuItemId;
- /** Display label */
- label: string;
- /** Lucide icon component */
- icon: Component;
- /** Extra CSS class applied to the item (e.g. for test selectors) */
- class?: string;
- /** Whether the item requires a specific modality to be enabled */
- enabledWhen?: AttachmentItemEnabledWhen;
- /** Tooltip shown when the item is disabled */
- disabledTooltip?: string;
- /** Callback key on the Props interface to invoke when clicked */
- action: AttachmentAction;
- /** Whether the item is only shown when a specific capability is present */
- visibleWhen?: AttachmentItemVisibleWhen;
- /** Whether this item has a tooltip even when enabled (uses dynamic text) */
- hasEnabledTooltip?: boolean;
-}
+import type { AttachmentMenuItem } from '$lib/types';
/**
* File attachment menu items shown in both the desktop dropdown and mobile sheet.
diff --git a/tools/ui/src/lib/constants/auto-scroll.ts b/tools/ui/src/lib/constants/auto-scroll.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/auto-scroll.ts
rename to tools/ui/src/lib/constants/auto-scroll.constants.ts
diff --git a/tools/ui/src/lib/constants/binary-detection.ts b/tools/ui/src/lib/constants/binary-detection.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/binary-detection.ts
rename to tools/ui/src/lib/constants/binary-detection.constants.ts
diff --git a/tools/ui/src/lib/constants/built-in-tools.ts b/tools/ui/src/lib/constants/built-in-tools.constants.ts
similarity index 82%
rename from tools/ui/src/lib/constants/built-in-tools.ts
rename to tools/ui/src/lib/constants/built-in-tools.constants.ts
index 5bd24ffe8..679c61459 100644
--- a/tools/ui/src/lib/constants/built-in-tools.ts
+++ b/tools/ui/src/lib/constants/built-in-tools.constants.ts
@@ -20,13 +20,7 @@ import {
Terminal
} from '@lucide/svelte';
import { BuiltInTool, ToolSource } from '$lib/enums';
-import type { Component } from 'svelte';
-
-export interface BuiltinToolUiEntry {
- icon: Component;
- label: string;
- source: ToolSource.BUILTIN | ToolSource.FRONTEND;
-}
+import type { BuiltinToolUiEntry } from '$lib/types';
export const BUILTIN_TOOL_UI: Readonly> = {
[BuiltInTool.EDIT_FILE]: { icon: FilePen, label: 'Edit file', source: ToolSource.BUILTIN },
@@ -56,9 +50,3 @@ export const BUILTIN_TOOL_UI: Readonly>
},
[BuiltInTool.WRITE_FILE]: { icon: FilePlus, label: 'Write file', source: ToolSource.BUILTIN }
} as const;
-
-export function getBuiltinToolUi(toolName: string | undefined): BuiltinToolUiEntry | null {
- if (!toolName) return null;
-
- return (BUILTIN_TOOL_UI as Record)[toolName] ?? null;
-}
diff --git a/tools/ui/src/lib/constants/cache.constants.ts b/tools/ui/src/lib/constants/cache.constants.ts
new file mode 100644
index 000000000..b60792d99
--- /dev/null
+++ b/tools/ui/src/lib/constants/cache.constants.ts
@@ -0,0 +1,44 @@
+/**
+ * Cache configuration constants
+ */
+
+/**
+ * Default cache limits when no per-cache overrides are given.
+ */
+export const CACHE = {
+ /** Default maximum number of entries in a cache */
+ DEFAULT_MAX_ENTRIES: 100,
+ /** Default TTL (Time-To-Live) for cache entries in milliseconds (5 minutes) */
+ DEFAULT_TTL_MS: 5 * 60 * 1000
+} as const;
+
+/**
+ * TTL and size for the model props cache.
+ * Props don't change frequently, so we can cache them longer.
+ */
+export const MODEL_PROPS_CACHE = {
+ /** Maximum number of model props to cache */
+ MAX_ENTRIES: 50,
+ /** TTL for model props cache entries in milliseconds (10 minutes) */
+ TTL_MS: 10 * 60 * 1000
+} as const;
+
+/**
+ * TTL and size for the MCP resource cache.
+ */
+export const MCP_RESOURCE_CACHE = {
+ /** Maximum number of MCP resources to cache */
+ MAX_ENTRIES: 50,
+ /** TTL for MCP resource cache entries in milliseconds (5 minutes) */
+ TTL_MS: 5 * 60 * 1000
+} as const;
+
+/**
+ * Limits for pruning inactive conversation states held in memory.
+ */
+export const INACTIVE_CONVERSATION = {
+ /** Maximum age (in ms) for inactive conversation states before cleanup (30 minutes) */
+ MAX_AGE_MS: 30 * 60 * 1000,
+ /** Maximum number of inactive conversation states to keep in memory */
+ MAX_STATES: 10
+} as const;
diff --git a/tools/ui/src/lib/constants/cache.ts b/tools/ui/src/lib/constants/cache.ts
deleted file mode 100644
index 07fe86834..000000000
--- a/tools/ui/src/lib/constants/cache.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Cache configuration constants
- */
-
-/**
- * Default TTL (Time-To-Live) for cache entries in milliseconds
- * @default 5 minutes
- */
-export const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
-
-/**
- * Default maximum number of entries in a cache
- * @default 100
- */
-export const DEFAULT_CACHE_MAX_ENTRIES = 100;
-
-/**
- * TTL for model props cache in milliseconds
- * Props don't change frequently, so we can cache them longer
- * @default 10 minutes
- */
-export const MODEL_PROPS_CACHE_TTL_MS = 10 * 60 * 1000;
-
-/**
- * Maximum number of model props to cache
- * @default 50
- */
-export const MODEL_PROPS_CACHE_MAX_ENTRIES = 50;
-
-/**
- * Maximum number of MCP resources to cache
- * @default 50
- */
-export const MCP_RESOURCE_CACHE_MAX_ENTRIES = 50;
-
-/**
- * TTL for MCP resource cache entries in milliseconds
- * @default 5 minutes
- */
-export const MCP_RESOURCE_CACHE_TTL_MS = 5 * 60 * 1000;
-
-/**
- * Maximum number of inactive conversation states to keep in memory
- * States for conversations beyond this limit will be cleaned up
- * @default 10
- */
-export const MAX_INACTIVE_CONVERSATION_STATES = 10;
-
-/**
- * Maximum age (in ms) for inactive conversation states before cleanup
- * States older than this will be removed during cleanup
- * @default 30 minutes
- */
-export const INACTIVE_CONVERSATION_STATE_MAX_AGE_MS = 30 * 60 * 1000;
diff --git a/tools/ui/src/lib/constants/chat-form.ts b/tools/ui/src/lib/constants/chat-form.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/chat-form.ts
rename to tools/ui/src/lib/constants/chat-form.constants.ts
diff --git a/tools/ui/src/lib/constants/cli-flags.ts b/tools/ui/src/lib/constants/cli-flags.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/cli-flags.ts
rename to tools/ui/src/lib/constants/cli-flags.constants.ts
diff --git a/tools/ui/src/lib/constants/code-block.constants.ts b/tools/ui/src/lib/constants/code-block.constants.ts
new file mode 100644
index 000000000..05db575f9
--- /dev/null
+++ b/tools/ui/src/lib/constants/code-block.constants.ts
@@ -0,0 +1,50 @@
+// Constants for the markdown code-block renderer: language/fence handling and CSS classes.
+
+/** Parsing and escaping helpers for the markdown code-block renderer. */
+export const CODE_BLOCK = {
+ AMPERSAND_REGEX: /&/g,
+ /** Language fallback used when no language is specified. */
+ DEFAULT_LANGUAGE: 'text',
+ /** Matches opening/closing markdown code fences. */
+ FENCE_PATTERN: /^```|\n```/g,
+ GT_REGEX: />/g,
+ /** Matches the language specifier at the start of a code fence. */
+ LANG_PATTERN: /^(\w*)\n?/,
+ LT_REGEX: //g;
-export const FENCE_PATTERN = /^```|\n```/g;
-
-// Whitespace-only empty lines (between start of string and first non-empty line).
-// Used by trimCodePadding to drop leading/trailing phantom blank rows from LLM
-// payload wrappers without touching internal blank lines.
-export const TRIM_LEADING_PADDING_REGEX = /^(?:[ \t]*\n)+/;
-export const TRIM_TRAILING_PADDING_REGEX = /(?:\n[ \t]*)+$/;
-
-// Matches either Unix or Windows path separators so `String.split(REGEX)` can
-// recover the trailing file-name segment from either `/foo/bar.txt` or
-// `C:\foo\bar.txt`. Used wherever a parameter accepts a user-supplied path.
-export const FILE_PATH_SEPARATOR_REGEX = /[\\/]/;
-
-// Separates a file name from its extension, e.g. the '.' in `cover.png`.
-export const FILE_EXTENSION_SEPARATOR = '.';
-
-// Matches the `text:` prefix that file-type identifiers use to denote a
-// plain-text language (e.g. `text:typescript`). Used by tool-call renderers
-// to recover the underlying highlight.js language.
-export const TEXT_LANGUAGE_PREFIX_REGEX = /^text:/;
diff --git a/tools/ui/src/lib/constants/content-detection.constants.ts b/tools/ui/src/lib/constants/content-detection.constants.ts
new file mode 100644
index 000000000..c5c05819a
--- /dev/null
+++ b/tools/ui/src/lib/constants/content-detection.constants.ts
@@ -0,0 +1,20 @@
+/**
+ * String patterns for detecting content kind from MIME types and URIs.
+ * Used with startsWith/includes checks, not as discriminated values.
+ */
+
+export const MIME_TYPE_PREFIXES = {
+ IMAGE: 'image/',
+ TEXT: 'text'
+} as const;
+
+export const MIME_TYPE_SUBSTRINGS = {
+ JAVASCRIPT: 'javascript',
+ JSON: 'json',
+ TYPESCRIPT: 'typescript'
+} as const;
+
+export const URI_PATTERNS = {
+ DATABASE_KEYWORD: 'database',
+ DATABASE_SCHEME: 'db://'
+} as const;
diff --git a/tools/ui/src/lib/constants/context-gauge-popup.ts b/tools/ui/src/lib/constants/context-gauge-popup.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/context-gauge-popup.ts
rename to tools/ui/src/lib/constants/context-gauge-popup.constants.ts
diff --git a/tools/ui/src/lib/constants/context-keys.ts b/tools/ui/src/lib/constants/context-keys.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/context-keys.ts
rename to tools/ui/src/lib/constants/context-keys.constants.ts
diff --git a/tools/ui/src/lib/constants/control-actions.ts b/tools/ui/src/lib/constants/control-actions.constants.ts
similarity index 73%
rename from tools/ui/src/lib/constants/control-actions.ts
rename to tools/ui/src/lib/constants/control-actions.constants.ts
index 935ae9542..c8ebf701b 100644
--- a/tools/ui/src/lib/constants/control-actions.ts
+++ b/tools/ui/src/lib/constants/control-actions.constants.ts
@@ -3,5 +3,3 @@
export const CONTROL_ACTION = {
END_REASONING: 'reasoning_end'
} as const;
-
-export type ControlAction = (typeof CONTROL_ACTION)[keyof typeof CONTROL_ACTION];
diff --git a/tools/ui/src/lib/constants/conversation-import.ts b/tools/ui/src/lib/constants/conversation-import.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/conversation-import.ts
rename to tools/ui/src/lib/constants/conversation-import.constants.ts
diff --git a/tools/ui/src/lib/constants/css-classes.ts b/tools/ui/src/lib/constants/css-classes.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/css-classes.ts
rename to tools/ui/src/lib/constants/css-classes.constants.ts
diff --git a/tools/ui/src/lib/constants/database.ts b/tools/ui/src/lib/constants/database.constants.ts
similarity index 93%
rename from tools/ui/src/lib/constants/database.ts
rename to tools/ui/src/lib/constants/database.constants.ts
index 95e698f40..f2c961039 100644
--- a/tools/ui/src/lib/constants/database.ts
+++ b/tools/ui/src/lib/constants/database.constants.ts
@@ -5,7 +5,7 @@
* naming changes.
*/
-import { STORAGE_APP_NAME } from './storage';
+import { STORAGE_APP_NAME } from './storage.constants';
/** IndexedDB database name */
export const DB_NAME = STORAGE_APP_NAME;
diff --git a/tools/ui/src/lib/constants/diagram-blocks.ts b/tools/ui/src/lib/constants/diagram-blocks.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/diagram-blocks.ts
rename to tools/ui/src/lib/constants/diagram-blocks.constants.ts
diff --git a/tools/ui/src/lib/constants/error.ts b/tools/ui/src/lib/constants/error.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/error.ts
rename to tools/ui/src/lib/constants/error.constants.ts
diff --git a/tools/ui/src/lib/constants/floating-ui-constraints.ts b/tools/ui/src/lib/constants/floating-ui-constraints.ts
deleted file mode 100644
index 003fc77ac..000000000
--- a/tools/ui/src/lib/constants/floating-ui-constraints.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const VIEWPORT_GUTTER = 8;
-export const MENU_OFFSET = 6;
diff --git a/tools/ui/src/lib/constants/formatters.ts b/tools/ui/src/lib/constants/formatters.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/formatters.ts
rename to tools/ui/src/lib/constants/formatters.constants.ts
diff --git a/tools/ui/src/lib/constants/headers.constants.ts b/tools/ui/src/lib/constants/headers.constants.ts
new file mode 100644
index 000000000..f40df0571
--- /dev/null
+++ b/tools/ui/src/lib/constants/headers.constants.ts
@@ -0,0 +1,35 @@
+/** HTTP header handling for API and MCP requests. */
+export const HEADERS = {
+ /** Canonical casing for the Authorization header (RFC 7235) */
+ AUTHORIZATION: 'Authorization',
+ /** Bearer scheme prefix used for Authorization headers (RFC 6750) */
+ BEARER: 'Bearer ',
+ /** Content-Type HTTP header name */
+ CONTENT_TYPE: 'Content-Type',
+ /** Partial-redaction rules for MCP headers: header name -> visible trailing chars */
+ PARTIAL_REDACT: new Map([['mcp-session-id', 5]]),
+
+ /** Header names whose values should be redacted in diagnostic logs */
+ REDACTED: new Set([
+ 'authorization',
+ 'api-key',
+ 'cookie',
+ 'mcp-session-id',
+ 'proxy-authorization',
+ 'set-cookie',
+ 'x-auth-token',
+ 'x-api-key'
+ ]),
+
+ /** Header carrying the stream-session identity (conversation id, optionally with a model suffix) */
+ X_CONVERSATION_ID_HEADER: 'X-Conversation-Id',
+
+ /** Header asking the server to encode a tool's output differently, e.g. read_file returning base64. */
+ X_RESP_TYPE_HEADER: 'x-resp-type',
+
+ /** Header carrying the working directory a tool call runs in; the model cannot override it */
+ X_TOOL_CWD_HEADER: 'x-tool-cwd'
+};
+
+/** `X_RESP_TYPE_HEADER` value that makes read_file return raw bytes as base64 instead of text. */
+export const RESP_TYPE_BASE64 = 'base64';
diff --git a/tools/ui/src/lib/constants/icons.ts b/tools/ui/src/lib/constants/icons.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/icons.ts
rename to tools/ui/src/lib/constants/icons.constants.ts
diff --git a/tools/ui/src/lib/constants/image-size.ts b/tools/ui/src/lib/constants/image-size.ts
deleted file mode 100644
index 8a7f921fa..000000000
--- a/tools/ui/src/lib/constants/image-size.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const MEGAPIXELS_TO_PIXELS = 1_000_000;
-
-export const HEIC_JPEG_QUALITY = 0.85;
diff --git a/tools/ui/src/lib/constants/image.constants.ts b/tools/ui/src/lib/constants/image.constants.ts
new file mode 100644
index 000000000..53a90eaa4
--- /dev/null
+++ b/tools/ui/src/lib/constants/image.constants.ts
@@ -0,0 +1,32 @@
+/** Image handling constants */
+
+export const IMAGE = {
+ /** JPEG quality used when transcoding HEIC images. */
+ HEIC_JPEG_QUALITY: 0.85,
+ /** Unit conversion: pixels per megapixel. */
+ MEGAPIXELS_TO_PIXELS: 1_000_000
+} as const;
+
+/**
+ * JPEG and EXIF binary format constants for orientation parsing.
+ */
+export const EXIF = {
+ /** APP1 segment marker byte, carries the EXIF payload */
+ APP1_MARKER: 0xe1,
+ /** "Exif" signature opening the APP1 payload, big endian uint32 */
+ EXIF_SIGNATURE: 0x45786966,
+ /** Size in bytes of one IFD directory entry */
+ IFD_ENTRY_SIZE: 12,
+ /** JPEG start of image marker */
+ JPEG_SOI_MARKER: 0xffd8,
+ /** EXIF tag id holding the orientation value */
+ ORIENTATION_TAG: 0x0112,
+ /** Bytes of file prefix to scan, the APP1 EXIF segment sits near the start */
+ SCAN_BYTE_LIMIT: 128 * 1024,
+ /** Start of scan marker byte, compressed data begins and no EXIF follows */
+ SOS_MARKER: 0xda,
+ /** TIFF byte order mark for little endian ("II") */
+ TIFF_LITTLE_ENDIAN: 0x4949,
+ /** TIFF magic number following the byte order mark */
+ TIFF_MAGIC: 42
+} as const;
diff --git a/tools/ui/src/lib/constants/index.ts b/tools/ui/src/lib/constants/index.ts
index 357a33a62..17e5f4d00 100644
--- a/tools/ui/src/lib/constants/index.ts
+++ b/tools/ui/src/lib/constants/index.ts
@@ -1,67 +1,61 @@
// Central constants export file
// All constants should be imported from '$lib/constants'
-export * from './agentic';
-export * from './api-endpoints';
-export * from './app';
-export * from './attachment-labels';
-export * from './database';
-export * from './reasoning-effort';
-export * from './reasoning-effort-tokens';
-export * from './recommended-mcp-servers';
-export * from './storage';
-export * from './attachment-menu';
-export * from './auto-scroll';
-export * from './context-gauge-popup';
-export * from './conversation-import';
-export * from './binary-detection';
-export * from './built-in-tools';
-export * from './cache';
-export * from './chat-form';
-export * from './chat-commands';
-export * from './cli-flags';
-export * from './code-blocks';
-export * from './icons';
-export * from './code';
-export * from './context-keys';
-export * from './control-actions';
-export * from './css-classes';
-export * from './floating-ui-constraints';
-export * from './formatters';
-export * from './key-value-pairs';
-export * from './icons';
-export * from './latex-protection';
-export * from './literal-html';
-export * from './markdown';
-export * from './mermaid-blocks';
-export * from './svg-blocks';
-export * from './diagram-blocks';
-export * from './max-bundle-size';
-export * from './mcp';
-export * from './mcp-form';
-export * from './mcp-resource';
-export * from './mention-badge';
-export * from './message-export';
-export * from './path-display';
-export * from './model-id';
-export * from './model-loading';
-export * from './sse';
-export * from './precision';
-export * from './processing-info';
-export * from './pwa';
+export * from './agentic.constants';
+export * from './api-endpoints.constants';
+export * from './app.constants';
+export * from './database.constants';
+export * from './reasoning-effort.constants';
+export * from './recommended-mcp-servers.constants';
+export * from './storage.constants';
+export * from './icons.constants';
+export * from './attachment-menu.constants';
+export * from './auto-scroll.constants';
+export * from './context-gauge-popup.constants';
+export * from './conversation-import.constants';
+export * from './binary-detection.constants';
+export * from './content-detection.constants';
+export * from './built-in-tools.constants';
+export * from './cache.constants';
+export * from './chat-form.constants';
+export * from './cli-flags.constants';
+export * from './code-block.constants';
+export * from './context-keys.constants';
+export * from './control-actions.constants';
+export * from './css-classes.constants';
+export * from './formatters.constants';
+export * from './headers.constants';
+export * from './key-value-pairs.constants';
+export * from './latex-protection.constants';
+export * from './literal-html.constants';
+export * from './markdown.constants';
+export * from './mermaid-blocks.constants';
+export * from './svg-blocks.constants';
+export * from './diagram-blocks.constants';
+export * from './max-bundle-size.constants';
+export * from './error.constants';
+export * from './image.constants';
+export * from './mcp.constants';
+export * from './mcp-form.constants';
+export * from './mcp-resource.constants';
+export * from './mention-badge.constants';
+export * from './message-export.constants';
+export * from './path-display.constants';
+export * from './model-id.constants';
+export * from './model-loading.constants';
+export * from './precision.constants';
+export * from './pwa.constants';
+export * from './routes.constants';
+export * from './sandbox.constants';
+export * from './settings-keys.constants';
+export * from './settings-registry.constants';
+export * from './special-characters.constants';
+export * from './stream.constants';
+export * from './supported-file-types.constants';
+export * from './table-html-restorer.constants';
+export * from './title-generation.constants';
+export * from './ui.constants';
+export * from './uri-template.constants';
+export * from './url.constants';
+export * from './working-directory.constants';
export * from './read-media';
-export * from './routes';
-export * from './sandbox';
-export * from './settings-keys';
-export * from './settings-registry';
-export * from './stream';
-export * from './supported-file-types';
-export * from './table-html-restorer';
-export * from './title-generation';
-export * from './tools';
-export * from './tooltip-config';
-export * from './ui';
-export * from './uri-template';
-export * from './url';
-export * from './viewport';
-export * from './working-directory';
diff --git a/tools/ui/src/lib/constants/jpeg-exif.ts b/tools/ui/src/lib/constants/jpeg-exif.ts
deleted file mode 100644
index 5b2591b04..000000000
--- a/tools/ui/src/lib/constants/jpeg-exif.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * JPEG and EXIF binary format constants for orientation parsing.
- */
-
-/** Bytes of file prefix to scan, the APP1 EXIF segment sits near the start */
-export const EXIF_SCAN_BYTE_LIMIT = 128 * 1024;
-
-/** JPEG start of image marker */
-export const JPEG_SOI_MARKER = 0xffd8;
-
-/** APP1 segment marker byte, carries the EXIF payload */
-export const APP1_MARKER = 0xe1;
-
-/** Start of scan marker byte, compressed data begins and no EXIF follows */
-export const SOS_MARKER = 0xda;
-
-/** "Exif" signature opening the APP1 payload, big endian uint32 */
-export const EXIF_SIGNATURE = 0x45786966;
-
-/** TIFF byte order mark for little endian ("II") */
-export const TIFF_LITTLE_ENDIAN = 0x4949;
-
-/** TIFF magic number following the byte order mark */
-export const TIFF_MAGIC = 42;
-
-/** EXIF tag id holding the orientation value */
-export const EXIF_ORIENTATION_TAG = 0x0112;
-
-/** Size in bytes of one IFD directory entry */
-export const IFD_ENTRY_SIZE = 12;
diff --git a/tools/ui/src/lib/constants/key-value-pairs.ts b/tools/ui/src/lib/constants/key-value-pairs.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/key-value-pairs.ts
rename to tools/ui/src/lib/constants/key-value-pairs.constants.ts
diff --git a/tools/ui/src/lib/constants/latex-protection.ts b/tools/ui/src/lib/constants/latex-protection.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/latex-protection.ts
rename to tools/ui/src/lib/constants/latex-protection.constants.ts
diff --git a/tools/ui/src/lib/constants/literal-html.ts b/tools/ui/src/lib/constants/literal-html.constants.ts
similarity index 56%
rename from tools/ui/src/lib/constants/literal-html.ts
rename to tools/ui/src/lib/constants/literal-html.constants.ts
index ed1b0cf0d..8efa6b574 100644
--- a/tools/ui/src/lib/constants/literal-html.ts
+++ b/tools/ui/src/lib/constants/literal-html.constants.ts
@@ -1,5 +1,3 @@
-export const LINE_BREAK = /\r?\n/;
-
export const PHRASE_PARENTS = new Set([
'paragraph',
'heading',
@@ -10,6 +8,3 @@ export const PHRASE_PARENTS = new Set([
'linkReference',
'tableCell'
]);
-
-export const NBSP = '\u00a0';
-export const TAB_AS_SPACES = NBSP.repeat(4);
diff --git a/tools/ui/src/lib/constants/markdown.constants.ts b/tools/ui/src/lib/constants/markdown.constants.ts
new file mode 100644
index 000000000..c8289c39a
--- /dev/null
+++ b/tools/ui/src/lib/constants/markdown.constants.ts
@@ -0,0 +1,17 @@
+export const IMAGE_NOT_ERROR_BOUND_SELECTOR = 'img:not([data-error-bound])';
+export const DATA_ERROR_BOUND_ATTR = 'errorBound';
+export const DATA_ERROR_HANDLED_ATTR = 'errorHandled';
+export const BOOL_TRUE_STRING = 'true';
+export const BOOL_FALSE_STRING = 'false';
+
+/** Markdown structural markers used by `looksLikeMarkdown`. Inline / line-level. */
+export const MARKDOWN = {
+ ATX_HEADING_REGEX: /^#{1,6}\s+\S/,
+ BLOCKQUOTE_REGEX: /^>\s+\S/,
+ BOLD_REGEX: /\*\*[^*\n]+\*\*|__[^_\n]+__/,
+ CODE_FENCE_REGEX: /^(```|~~~)/m,
+ LINK_REGEX: /\[[^\]\n]+\]\([^)\s]+\)/,
+ LIST_BULLET_REGEX: /^\s*[-*+]\s+\S/,
+ LIST_NUMBERED_REGEX: /^\s*\d+[.)]\s+\S/,
+ TABLE_SEPARATOR_REGEX: /^\s*\|?[\s:|-]+\|?\s*$/
+} as const;
diff --git a/tools/ui/src/lib/constants/markdown.ts b/tools/ui/src/lib/constants/markdown.ts
deleted file mode 100644
index 1cace78a3..000000000
--- a/tools/ui/src/lib/constants/markdown.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export const IMAGE_NOT_ERROR_BOUND_SELECTOR = 'img:not([data-error-bound])';
-export const DATA_ERROR_BOUND_ATTR = 'errorBound';
-export const DATA_ERROR_HANDLED_ATTR = 'errorHandled';
-export const BOOL_TRUE_STRING = 'true';
-export const BOOL_FALSE_STRING = 'false';
diff --git a/tools/ui/src/lib/constants/max-bundle-size.ts b/tools/ui/src/lib/constants/max-bundle-size.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/max-bundle-size.ts
rename to tools/ui/src/lib/constants/max-bundle-size.constants.ts
diff --git a/tools/ui/src/lib/constants/mcp-form.ts b/tools/ui/src/lib/constants/mcp-form.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/mcp-form.ts
rename to tools/ui/src/lib/constants/mcp-form.constants.ts
diff --git a/tools/ui/src/lib/constants/mcp-resource.ts b/tools/ui/src/lib/constants/mcp-resource.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/mcp-resource.ts
rename to tools/ui/src/lib/constants/mcp-resource.constants.ts
diff --git a/tools/ui/src/lib/constants/mcp.ts b/tools/ui/src/lib/constants/mcp.constants.ts
similarity index 57%
rename from tools/ui/src/lib/constants/mcp.ts
rename to tools/ui/src/lib/constants/mcp.constants.ts
index 854cbf9f7..11013d2cb 100644
--- a/tools/ui/src/lib/constants/mcp.ts
+++ b/tools/ui/src/lib/constants/mcp.constants.ts
@@ -36,11 +36,14 @@ export const DEFAULT_MCP_CONFIG = {
export const MCP_SERVER_ID_PREFIX = 'LlamaUI-MCP-Server';
-export const MCP_RECONNECT_INITIAL_DELAY = 1000;
-export const MCP_RECONNECT_BACKOFF_MULTIPLIER = 2;
-export const MCP_RECONNECT_MAX_DELAY = 30000;
-/** Per-attempt timeout for a single reconnection attempt before giving up and backing off. */
-export const MCP_RECONNECT_ATTEMPT_TIMEOUT_MS = 15_000;
+/** Backoff policy for reconnecting to a dropped MCP server. */
+export const MCP_RECONNECT = {
+ /** Per-attempt timeout for a single reconnection attempt before giving up and backing off. */
+ ATTEMPT_TIMEOUT_MS: 15_000,
+ BACKOFF_MULTIPLIER: 2,
+ INITIAL_DELAY: 1000,
+ MAX_DELAY: 30000
+};
/** Maximum number of MCP server avatars to display in the chat form */
export const MAX_DISPLAYED_MCP_AVATARS = 4;
@@ -48,40 +51,20 @@ export const MAX_DISPLAYED_MCP_AVATARS = 4;
/** Expected count when two theme-less icons represent a light/dark pair */
export const EXPECTED_THEMED_ICON_PAIR_COUNT = 2;
-/** CORS proxy URL query parameter name */
-export const CORS_PROXY_URL_PARAM = 'url';
+/** CORS proxy connection settings */
+export const CORS_PROXY = {
+ /** Header prefix for headers that should be forwarded by the CORS proxy */
+ HEADER_PREFIX: 'x-llama-server-proxy-header-',
+ /** CORS proxy URL query parameter name */
+ URL_PARAM: 'url'
+} as const;
-/** Header prefix for headers that should be forwarded by the CORS proxy */
-export const CORS_PROXY_HEADER_PREFIX = 'x-llama-server-proxy-header-';
-
-/** Number of trailing characters to keep visible when partially redacting mcp-session-id */
-export const MCP_SESSION_ID_VISIBLE_CHARS = 5;
-
-/** Partial-redaction rules for MCP headers: header name -> visible trailing chars */
-export const MCP_PARTIAL_REDACT_HEADERS = new Map([
- ['mcp-session-id', MCP_SESSION_ID_VISIBLE_CHARS]
-]);
-
-/** Bearer scheme prefix used for Authorization headers (RFC 6750) */
-export const BEARER_PREFIX = 'Bearer ';
-
-/** Canonical casing for the Authorization header (RFC 7235) */
-export const AUTHORIZATION_HEADER = 'Authorization';
-
-/** Content-Type HTTP header name */
-export const CONTENT_TYPE_HEADER = 'Content-Type';
-
-/** Header names whose values should be redacted in diagnostic logs */
-export const REDACTED_HEADERS = new Set([
- 'authorization',
- 'api-key',
- 'cookie',
- 'mcp-session-id',
- 'proxy-authorization',
- 'set-cookie',
- 'x-auth-token',
- 'x-api-key'
-]);
+/** Standard SSE endpoint path indicators */
+export const MCP_SSE = {
+ ENDPOINT: '/sse',
+ ENDPOINT_QUERY: '/sse?',
+ ENDPOINT_SLASH: '/sse/'
+} as const;
/** Human-readable labels for MCP transport types */
export const MCP_TRANSPORT_LABELS: Record = {
@@ -96,8 +79,3 @@ export const MCP_TRANSPORT_ICONS: Record = {
[MCPTransportType.STREAMABLE_HTTP]: Globe,
[MCPTransportType.WEBSOCKET]: Zap
};
-
-/** Standard SSE endpoint path indicators */
-export const MCP_SSE_ENDPOINT = '/sse';
-export const MCP_SSE_ENDPOINT_SLASH = '/sse/';
-export const MCP_SSE_ENDPOINT_QUERY = '/sse?';
diff --git a/tools/ui/src/lib/constants/mention-badge.ts b/tools/ui/src/lib/constants/mention-badge.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/mention-badge.ts
rename to tools/ui/src/lib/constants/mention-badge.constants.ts
diff --git a/tools/ui/src/lib/constants/mermaid-blocks.ts b/tools/ui/src/lib/constants/mermaid-blocks.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/mermaid-blocks.ts
rename to tools/ui/src/lib/constants/mermaid-blocks.constants.ts
diff --git a/tools/ui/src/lib/constants/message-export.constants.ts b/tools/ui/src/lib/constants/message-export.constants.ts
new file mode 100644
index 000000000..f6c576d79
--- /dev/null
+++ b/tools/ui/src/lib/constants/message-export.constants.ts
@@ -0,0 +1,24 @@
+// Conversation exporter / filename constants
+
+export const EXPORT_CONV = {
+ // Producer marker carried by the session record of a JSONL export
+ HARNESS: 'llama.app',
+ // Length of the trimmed conversation ID in the filename
+ ID_TRIM_LENGTH: 8,
+ // Replacements to the ISO date for use in the export filename
+ ISO_DATE_TIME_SEPARATOR: 'T',
+
+ ISO_DATE_TIME_SEPARATOR_REPLACEMENT: '_',
+
+ ISO_TIME_SEPARATOR: ':',
+ ISO_TIME_SEPARATOR_REPLACEMENT: '-',
+ // Characters to keep in the ISO timestamp. 19 keeps 2026-01-01T00:00:00
+ ISO_TIMESTAMP_SLICE: 19,
+
+ MULTIPLE_UNDERSCORE_REGEX: /_+/g,
+ // Maximum length of the sanitized conversation name snippet
+ NAME_SUFFIX_MAX_LENGTH: 20,
+ // Replacements for making the conversation title filename-friendly
+ NON_ALPHANUMERIC_REGEX: /[^a-z0-9]/gi,
+ NONALNUM_REPLACEMENT: '_'
+} as const;
diff --git a/tools/ui/src/lib/constants/message-export.ts b/tools/ui/src/lib/constants/message-export.ts
deleted file mode 100644
index fc4dbe259..000000000
--- a/tools/ui/src/lib/constants/message-export.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// Conversation filename constants
-
-// Length of the trimmed conversation ID in the filename
-export const EXPORT_CONV_ID_TRIM_LENGTH = 8;
-// Maximum length of the sanitized conversation name snippet
-export const EXPORT_CONV_NAME_SUFFIX_MAX_LENGTH = 20;
-// Characters to keep in the ISO timestamp. 19 keeps 2026-01-01T00:00:00
-export const ISO_TIMESTAMP_SLICE_LENGTH = 19;
-
-// Producer marker carried by the session record of a JSONL export
-export const SESSION_HARNESS = 'llama.app';
-
-// Replacements for making the conversation title filename-friendly
-export const NON_ALPHANUMERIC_REGEX = /[^a-z0-9]/gi;
-export const EXPORT_CONV_NONALNUM_REPLACEMENT = '_';
-export const MULTIPLE_UNDERSCORE_REGEX = /_+/g;
-
-// Replacements to the ISO date for use in the export filename
-export const ISO_DATE_TIME_SEPARATOR = 'T';
-export const ISO_DATE_TIME_SEPARATOR_REPLACEMENT = '_';
-
-export const ISO_TIME_SEPARATOR = ':';
-export const ISO_TIME_SEPARATOR_REPLACEMENT = '-';
diff --git a/tools/ui/src/lib/constants/model-id.constants.ts b/tools/ui/src/lib/constants/model-id.constants.ts
new file mode 100644
index 000000000..081a13e0e
--- /dev/null
+++ b/tools/ui/src/lib/constants/model-id.constants.ts
@@ -0,0 +1,43 @@
+/**
+ * Parsing of `org/ModelName[-tag][:quant]` style model IDs.
+ */
+
+export const MODEL_ID = {
+ /**
+ * Matches an activated-parameter-count segment, e.g. `A10B`, `a2.4b`.
+ * The leading `A`/`a` distinguishes it from a regular params segment.
+ */
+ ACTIVATED_PARAMS_RE: /^[Aa]\d+(\.\d+)?[BbMmKkTt]$/,
+
+ /** Matches prefix for custom quantization types, e.g. `UD-Q8_K_XL`. */
+ CUSTOM_QUANTIZATION_PREFIX_RE: /^UD$/i,
+ /** Container format segments to exclude from tags (every model uses these). */
+ IGNORED_SEGMENTS: new Set(['GGUF', 'GGML']),
+ /** Sentinel value returned by `indexOf` when a substring is not found. */
+ NOT_FOUND: -1,
+
+ /** Separates `` from `` in a model ID, e.g. `org/ModelName`. */
+ ORG_SEPARATOR: '/',
+
+ /**
+ * Matches a parameter-count segment, e.g. `7B`, `1.5b`, `120M`.
+ * The optional leading `E` covers effective-parameter sizes, e.g. Gemma's
+ * `E2B`/`E4B` (MatFormer models sized by resident params).
+ */
+ PARAMS_RE: /^[Ee]?\d+(\.\d+)?[BbMmKkTt]$/,
+
+ /**
+ * Matches a quantization/precision segment, e.g. `Q4_K_M`, `IQ4_XS`, `F16`, `BF16`, `MXFP4`.
+ * Case-insensitive to handle both uppercase and lowercase inputs.
+ */
+ QUANTIZATION_SEGMENT_RE: /^(I?Q\d+(_[A-Z0-9]+)*|F\d+|BF\d+|MXFP\d+(_[A-Z0-9]+)*)$/i,
+
+ /** Separates the model path from the quantization tag, e.g. `model:Q4_K_M`. */
+ QUANTIZATION_SEPARATOR: ':',
+
+ /** Separates named segments within the model path, e.g. `ModelName-7B-GGUF`. */
+ SEGMENT_SEPARATOR: '-',
+
+ /** Matches a trailing weight file extension, e.g. `model.gguf` -> `model`. */
+ WEIGHT_EXTENSION_RE: /\.(gguf|ggml)$/i
+};
diff --git a/tools/ui/src/lib/constants/model-id.ts b/tools/ui/src/lib/constants/model-id.ts
deleted file mode 100644
index 4108a2132..000000000
--- a/tools/ui/src/lib/constants/model-id.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/** Sentinel value returned by `indexOf` when a substring is not found. */
-export const MODEL_ID_NOT_FOUND = -1;
-
-/** Separates `` from `` in a model ID, e.g. `org/ModelName`. */
-export const MODEL_ID_ORG_SEPARATOR = '/';
-
-/** Separates named segments within the model path, e.g. `ModelName-7B-GGUF`. */
-export const MODEL_ID_SEGMENT_SEPARATOR = '-';
-
-/** Separates the model path from the quantization tag, e.g. `model:Q4_K_M`. */
-export const MODEL_ID_QUANTIZATION_SEPARATOR = ':';
-
-/**
- * Matches a quantization/precision segment, e.g. `Q4_K_M`, `IQ4_XS`, `F16`, `BF16`, `MXFP4`.
- * Case-insensitive to handle both uppercase and lowercase inputs.
- */
-export const MODEL_QUANTIZATION_SEGMENT_RE =
- /^(I?Q\d+(_[A-Z0-9]+)*|F\d+|BF\d+|MXFP\d+(_[A-Z0-9]+)*)$/i;
-
-/**
- * Matches prefix for custom quantization types, e.g. `UD-Q8_K_XL`.
- */
-export const MODEL_CUSTOM_QUANTIZATION_PREFIX_RE = /^UD$/i;
-
-/**
- * Matches a parameter-count segment, e.g. `7B`, `1.5b`, `120M`.
- * The optional leading `E` covers effective-parameter sizes, e.g. Gemma's
- * `E2B`/`E4B` (MatFormer models sized by resident params).
- */
-export const MODEL_PARAMS_RE = /^[Ee]?\d+(\.\d+)?[BbMmKkTt]$/;
-
-/**
- * Matches an activated-parameter-count segment, e.g. `A10B`, `a2.4b`.
- * The leading `A`/`a` distinguishes it from a regular params segment.
- */
-export const MODEL_ACTIVATED_PARAMS_RE = /^[Aa]\d+(\.\d+)?[BbMmKkTt]$/;
-
-/**
- * Container format segments to exclude from tags (every model uses these).
- */
-export const MODEL_IGNORED_SEGMENTS = new Set(['GGUF', 'GGML']);
-
-/**
- * Matches a trailing weight file extension, e.g. `model.gguf` -> `model`.
- */
-export const MODEL_WEIGHT_EXTENSION_RE = /\.(gguf|ggml)$/i;
diff --git a/tools/ui/src/lib/constants/model-loading.ts b/tools/ui/src/lib/constants/model-loading.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/model-loading.ts
rename to tools/ui/src/lib/constants/model-loading.constants.ts
diff --git a/tools/ui/src/lib/constants/path-display.ts b/tools/ui/src/lib/constants/path-display.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/path-display.ts
rename to tools/ui/src/lib/constants/path-display.constants.ts
diff --git a/tools/ui/src/lib/constants/precision.ts b/tools/ui/src/lib/constants/precision.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/precision.ts
rename to tools/ui/src/lib/constants/precision.constants.ts
diff --git a/tools/ui/src/lib/constants/processing-info.ts b/tools/ui/src/lib/constants/processing-info.ts
deleted file mode 100644
index 2c3f7dc53..000000000
--- a/tools/ui/src/lib/constants/processing-info.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export const PROCESSING_INFO_TIMEOUT = 2000;
-
-/**
- * Statistics units labels
- */
-export const STATS_UNITS = {
- TOKENS_PER_SECOND: 't/s'
-} as const;
diff --git a/tools/ui/src/lib/constants/pwa.ts b/tools/ui/src/lib/constants/pwa.constants.ts
similarity index 99%
rename from tools/ui/src/lib/constants/pwa.ts
rename to tools/ui/src/lib/constants/pwa.constants.ts
index 4da37c06b..e807f4a97 100644
--- a/tools/ui/src/lib/constants/pwa.ts
+++ b/tools/ui/src/lib/constants/pwa.constants.ts
@@ -3,7 +3,7 @@
* definitions across the codebase.
*/
-import { APP_NAME } from './app';
+import { APP_NAME } from './app.constants';
export const MEDIA_QUERIES = {
DISPLAY_MODE_STANDALONE: '(display-mode: standalone)',
diff --git a/tools/ui/src/lib/constants/reasoning-effort-tokens.ts b/tools/ui/src/lib/constants/reasoning-effort-tokens.ts
deleted file mode 100644
index a0c244e5e..000000000
--- a/tools/ui/src/lib/constants/reasoning-effort-tokens.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ReasoningEffort } from '$lib/enums';
-
-/**
- * Reasoning effort to token budget mapping.
- * Maps the ReasoningEffort enum values to concrete token counts for the server.
- */
-export const REASONING_EFFORT_TOKENS: Record = {
- [ReasoningEffort.HIGH]: 8192,
- [ReasoningEffort.LOW]: 512,
- [ReasoningEffort.MAX]: -1, // unlimited
- [ReasoningEffort.MEDIUM]: 2048
-};
diff --git a/tools/ui/src/lib/constants/reasoning-effort.ts b/tools/ui/src/lib/constants/reasoning-effort.constants.ts
similarity index 71%
rename from tools/ui/src/lib/constants/reasoning-effort.ts
rename to tools/ui/src/lib/constants/reasoning-effort.constants.ts
index 4cbb7388b..e8ec5f0e8 100644
--- a/tools/ui/src/lib/constants/reasoning-effort.ts
+++ b/tools/ui/src/lib/constants/reasoning-effort.constants.ts
@@ -22,3 +22,14 @@ export const REASONING_EFFORT_LEVELS: ReasoningEffortLevel[] = [
{ label: 'High', value: ReasoningEffort.HIGH },
{ hasInfo: true, label: 'Max', value: ReasoningEffort.MAX }
];
+
+/**
+ * Reasoning effort to token budget mapping.
+ * Maps the ReasoningEffort enum values to concrete token counts for the server.
+ */
+export const REASONING_EFFORT_TOKENS: Record = {
+ [ReasoningEffort.HIGH]: 8192,
+ [ReasoningEffort.LOW]: 512,
+ [ReasoningEffort.MAX]: -1, // unlimited
+ [ReasoningEffort.MEDIUM]: 2048
+};
diff --git a/tools/ui/src/lib/constants/recommended-mcp-servers.ts b/tools/ui/src/lib/constants/recommended-mcp-servers.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/recommended-mcp-servers.ts
rename to tools/ui/src/lib/constants/recommended-mcp-servers.constants.ts
diff --git a/tools/ui/src/lib/constants/routes.ts b/tools/ui/src/lib/constants/routes.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/routes.ts
rename to tools/ui/src/lib/constants/routes.constants.ts
diff --git a/tools/ui/src/lib/constants/sandbox.constants.ts b/tools/ui/src/lib/constants/sandbox.constants.ts
new file mode 100644
index 000000000..9846e471a
--- /dev/null
+++ b/tools/ui/src/lib/constants/sandbox.constants.ts
@@ -0,0 +1,13 @@
+import { BuiltInTool } from '$lib/enums';
+
+export const SANDBOX_TOOL_NAME = BuiltInTool.RUN_JAVASCRIPT;
+
+export const SANDBOX_TIMEOUT_MS_DEFAULT = 10000;
+
+export const SANDBOX_TIMEOUT_MS_MAX = 30000;
+
+export const SANDBOX_OUTPUT_MAX_CHARS = 8192;
+
+export const SANDBOX_EMPTY_OUTPUT = '(no output)';
+
+export const SANDBOX_TRUNCATION_NOTICE = '[output truncated]';
diff --git a/tools/ui/src/lib/constants/settings-keys.ts b/tools/ui/src/lib/constants/settings-keys.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/settings-keys.ts
rename to tools/ui/src/lib/constants/settings-keys.constants.ts
diff --git a/tools/ui/src/lib/constants/settings-registry.ts b/tools/ui/src/lib/constants/settings-registry.constants.ts
similarity index 97%
rename from tools/ui/src/lib/constants/settings-registry.ts
rename to tools/ui/src/lib/constants/settings-registry.constants.ts
index ada029a33..e44459957 100644
--- a/tools/ui/src/lib/constants/settings-registry.ts
+++ b/tools/ui/src/lib/constants/settings-registry.constants.ts
@@ -1,12 +1,9 @@
-import { CLI_FLAGS } from './cli-flags';
-import { DEFAULT_MCP_CONFIG } from './mcp';
-import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes';
-import { SETTINGS_KEYS } from './settings-keys';
-import { TITLE_GENERATION } from './title-generation';
-import {
- FILE_GLOB_SEARCH_PICKERS_DEFAULT_SEARCH_DEPTH,
- FILE_GLOB_SEARCH_PICKERS_MAX_SEARCH_DEPTH
-} from './working-directory';
+import { CLI_FLAGS } from './cli-flags.constants';
+import { DEFAULT_MCP_CONFIG } from './mcp.constants';
+import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes.constants';
+import { SETTINGS_KEYS } from './settings-keys.constants';
+import { TITLE_GENERATION } from './title-generation.constants';
+import { FILE_GLOB_SEARCH_PICKERS } from './working-directory.constants';
import {
AlertTriangle,
Code,
@@ -14,7 +11,6 @@ import {
Funnel,
ListRestart,
Monitor,
- Monitor as MonitorIcon,
Moon,
PencilRuler,
Sliders,
@@ -53,7 +49,7 @@ const STANDALONE_SECTIONS: { title: SettingsSectionTitle; slug: string; icon: Co
}
];
const COLOR_MODE_OPTIONS: Array<{ value: string; label: string; icon: Component }> = [
- { icon: MonitorIcon, label: 'System', value: ColorMode.SYSTEM },
+ { icon: Monitor, label: 'System', value: ColorMode.SYSTEM },
{ icon: Sun, label: 'Light', value: ColorMode.LIGHT },
{ icon: Moon, label: 'Dark', value: ColorMode.DARK }
];
@@ -106,14 +102,14 @@ const SETTINGS_REGISTRY: Record = {
type: SettingsFieldType.INPUT
},
{
- defaultValue: FILE_GLOB_SEARCH_PICKERS_DEFAULT_SEARCH_DEPTH,
+ defaultValue: FILE_GLOB_SEARCH_PICKERS.DEFAULT_SEARCH_DEPTH,
help: 'How many directory levels below the working directory the @-mention file search descends. Larger values surface deeply nested files but take longer on large trees.',
isPositiveInteger: true,
key: SETTINGS_KEYS.MENTION_SEARCH_MAX_DEPTH,
label: 'Mention search depth',
- max: FILE_GLOB_SEARCH_PICKERS_MAX_SEARCH_DEPTH,
+ max: FILE_GLOB_SEARCH_PICKERS.MAX_SEARCH_DEPTH,
min: 1,
- placeholder: `${FILE_GLOB_SEARCH_PICKERS_DEFAULT_SEARCH_DEPTH}`,
+ placeholder: `${FILE_GLOB_SEARCH_PICKERS.DEFAULT_SEARCH_DEPTH}`,
section: SETTINGS_SECTION_SLUGS.AGENTIC,
type: SettingsFieldType.INPUT
}
diff --git a/tools/ui/src/lib/constants/special-characters.constants.ts b/tools/ui/src/lib/constants/special-characters.constants.ts
new file mode 100644
index 000000000..aaeebca33
--- /dev/null
+++ b/tools/ui/src/lib/constants/special-characters.constants.ts
@@ -0,0 +1,16 @@
+// Control / whitespace / formatting characters that appear literally inside rendered text.
+
+/** Line feed. */
+export const NEWLINE = '\n';
+
+/** Horizontal tab. */
+export const TAB = '\t';
+
+/** Non-breaking space. */
+export const NBSP = '\u00a0';
+
+/** Non-breaking spaces used to render a tab stop that whitespace collapsing would otherwise squash. */
+export const TAB_AS_SPACES = NBSP.repeat(4);
+
+/** Matches a CR-terminated or bare LF line break. */
+export const LINE_BREAK = /\r?\n/;
diff --git a/tools/ui/src/lib/constants/storage.ts b/tools/ui/src/lib/constants/storage.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/storage.ts
rename to tools/ui/src/lib/constants/storage.constants.ts
diff --git a/tools/ui/src/lib/constants/sse.ts b/tools/ui/src/lib/constants/stream.constants.ts
similarity index 53%
rename from tools/ui/src/lib/constants/sse.ts
rename to tools/ui/src/lib/constants/stream.constants.ts
index 0eb4b6ede..64f67243c 100644
--- a/tools/ui/src/lib/constants/sse.ts
+++ b/tools/ui/src/lib/constants/stream.constants.ts
@@ -1,3 +1,11 @@
+// grace window after a visibilitychange before we kick a reader whose socket likely died
+// while the tab was hidden. covers brief background pauses without thrashing live streams
+export const STREAM_VISIBILITY_KICK_MS = 3000;
+
+// separator joining a conversation id and its per-model stream identity
+// suffix (conv::model) used by the server side replay buffer
+export const CONVERSATION_ID_SEPARATOR = '::';
+
/**
* Server-sent events wire format, shared by the chat stream and the
* /models/sse status feed (text/event-stream).
diff --git a/tools/ui/src/lib/constants/stream.ts b/tools/ui/src/lib/constants/stream.ts
deleted file mode 100644
index 67951ee95..000000000
--- a/tools/ui/src/lib/constants/stream.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// grace window after a visibilitychange before we kick a reader whose socket likely died
-// while the tab was hidden. covers brief background pauses without thrashing live streams
-export const STREAM_VISIBILITY_KICK_MS = 3000;
diff --git a/tools/ui/src/lib/constants/supported-file-types.ts b/tools/ui/src/lib/constants/supported-file-types.constants.ts
similarity index 100%
rename from tools/ui/src/lib/constants/supported-file-types.ts
rename to tools/ui/src/lib/constants/supported-file-types.constants.ts
diff --git a/tools/ui/src/lib/constants/svg-blocks.constants.ts b/tools/ui/src/lib/constants/svg-blocks.constants.ts
new file mode 100644
index 000000000..705800c26
--- /dev/null
+++ b/tools/ui/src/lib/constants/svg-blocks.constants.ts
@@ -0,0 +1,57 @@
+/**
+ * Constants for rendering svg code blocks inline.
+ */
+export const SVG = {
+ // CSS classes applied to the inline svg block and its chrome.
+ BLOCK_CLASS: 'svg-block',
+ /**
+ * Shadow root style for the zoom dialog svg. Lets the svg grow past its
+ * intrinsic size so pan and zoom have room to work.
+ */
+ DIALOG_SHADOW_STYLE:
+ ':host{display:inline-block}svg{min-height:min(50vh,12rem);min-width:min(80vw,20rem);max-width:none;max-height:none;height:auto;width:auto;display:block}',
+ ID_ATTR: 'data-svg-id',
+
+ /**
+ * Shadow root style for an inline svg block. Mirrors the centered, padded
+ * sizing the light dom used before the svg moved behind a shadow boundary.
+ */
+ INLINE_SHADOW_STYLE:
+ ':host{display:block;width:100%;text-align:center}svg{display:block;margin:0 auto;width:auto;height:auto;max-width:100%;max-height:70vh;min-height:8rem;padding:3rem 1rem}',
+ // Languages that mark a code block as svg content.
+ LANGUAGE: 'svg',
+ /**
+ * Hard size ceiling for a single inline svg block.
+ * Above this the source is left as raw text instead of being rendered.
+ */
+ MAX_BYTES: 256 * 1024,
+
+ RENDERED_ATTR: 'data-svg-rendered',
+ /**
+ * DOMPurify config for untrusted svg coming from model output.
+ *
+ * foreignObject and script stay forbidden unconditionally, they are the only
+ * inline svg vectors that execute arbitrary html or js. Everything else is
+ * allowed for maximum rendering compatibility: href and xlink:href stay so
+ * use, image, a and animateMotion work, and DOMPurify still neutralizes
+ * javascript: and data: uri schemes natively. External resource refs are
+ * allowed by design on a local first tool, the user browser fetches them.
+ *
+ * The sanitized svg is always mounted inside a shadow root (see svg-shadow),
+ * so an author