Commit Graph

3247 Commits

Author SHA1 Message Date
Yuneng Jiang 8a0ddd46d5 [Test] UI - Add Playwright E2E tests with local PostgreSQL
Add a self-contained Playwright E2E test suite that runs against a local
PostgreSQL database instead of Neon. Tests cover role-based access for all
5 user roles (proxy admin, admin viewer, internal user, internal viewer,
team admin) and authentication flows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:47:17 -07:00
yuneng-jiang 33c3f13443 Merge pull request #24880 from stuxf/fix/codeql-ui-alerts
fix: resolve CodeQL high-severity alerts in UI components
2026-03-31 18:23:56 -07:00
ryan-crabbe-berri 2f1cfb0548 Merge pull request #24751 from BerriAI/litellm_ryan-march-28
litellm ryan march 28
2026-03-31 17:25:30 -07:00
user d12f8490bf fix: resolve CodeQL high-severity alerts in UI components
Source fixes:
- page.tsx: add explicit isValidReturnUrl() check at redirect site
- public_model_hub.tsx: replace() → replaceAll() for all wildcard occurrences
- CodeSnippets.tsx: escape backslashes before quotes in generated Python
- TeamGuardrailsTab.tsx: escape backslashes before quotes in generated YAML

CodeQL suppressions for false positives:
- ChatUI.tsx: sessionStorage for apiKey/apiKeySource (sessionStorage is
  correct per project policy — scoped to tab, cleared on close)
- ChatUI.tsx: setInputMessage(prompt) where prompt is a hardcoded literal
- mcp_server_edit.tsx, create_mcp_server.tsx: sessionStorage for OAuth state
- useMcpOAuthFlow.tsx, useUserMcpOAuthFlow.tsx: sessionStorage wrappers
- LoginPage.tsx: localStorage.getItem for worker URL in SSO flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:06:05 +00:00
stuxf 7066c895f6 chore: harden npm supply chain — pin overrides, enforce npm ci, add ignore-scripts (#24838)
* chore: harden npm supply chain — pin overrides, enforce npm ci, add ignore-scripts

Replace open-ended >= version overrides with exact pins matching lockfile
versions across all 6 package.json files. Remove dead overrides for packages
not present in lockfiles. Switch CI and devcontainer from npm install to
npm ci for deterministic lockfile-based installs.

Add .npmrc to all 7 JS project directories with ignore-scripts=true (blocks
postinstall RAT vectors like the axios@1.14.1 supply chain attack) and
min-release-age=3d (refuses packages published <3 days ago, requires npm
>=11.10). Remove Yarn-only resolutions field from docs/my-website.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: bump sharp to 0.33.5 in docs, add docs .npmrc

sharp 0.32.x uses postinstall to download native binaries, which breaks
with ignore-scripts=true. sharp 0.33+ distributes via optionalDependencies
instead, making it compatible with the new .npmrc hardening.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove docs .npmrc to fix Vercel deploy

Vercel's build for docs/my-website uses npm install which needs
sharp 0.32.6's postinstall script. Since we don't control Vercel's
build process, remove the .npmrc from docs rather than fight it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: Dockerfile npm ci + nvm checksum verification

- Replace npm install with npm ci in Dockerfile.non_root,
  Dockerfile.custom_ui, and spend-logs/Dockerfile for deterministic
  lockfile-based installs
- Replace curl-pipe-bash nvm install with download-then-verify pattern
  in build_admin_ui.sh, build_ui.sh, and build_ui_custom_path.sh
- Update nvm from v0.38.0 (2021) to v0.40.4 (Jan 2026) with SHA256
  checksum verification before execution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: macOS sha256sum compat + clarify min-release-age scope

- Use shasum -a 256 fallback on macOS where sha256sum is unavailable
- Clarify in .npmrc comments that min-release-age only protects local
  npm install, not npm ci (used in CI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:41:37 -07:00
Ishaan Jaffer f636c3b3b7 pin axios 2026-03-30 20:20:23 -07:00
Ryan Crabbe 191bb9504d chore(ui): migrate Tremor Text/Badge to antd Tag and native spans in logging_settings_view
Replace Tremor Text with native <span> elements (with block class where
stacking is needed) and Tremor Badge with antd Tag. All Tailwind classes
preserved. Gray badge default mapped to undefined (antd Tag default).
2026-03-28 16:59:19 -07:00
Ryan Crabbe 8a2186a654 [Fix] Reset page to 1 on filter reset and use pageSize param in fallbacks
Add setCurrentPage(1) to handleFilterReset in the hook so page resets
regardless of which component consumes it. Replace hardcoded page_size: 50
in empty-state fallbacks with the pageSize parameter for consistency.
2026-03-28 16:19:03 -07:00
Ryan Crabbe fbddab6178 Handle search error state and cancel wasted API call on filter reset
- Set backendFilteredLogs to empty response on performSearch error so the
  UI shows "0 results" instead of appearing stuck in a loading state
- Cancel debounced search on filter reset instead of firing a request
  whose result would be ignored (hasBackendFilters is false after reset)
- Clear backendFilteredLogs immediately on filter change to prevent
  stale results from previous filter showing during debounce window
- Normalize response.data with nullish coalescing to handle missing data
- Use `not team_object.models` for None-safe emptiness check
2026-03-28 13:59:17 -07:00
Ryan Crabbe 12a55a8a6f Fix logs page showing unfiltered results when filter matches zero rows
The backendFilteredLogs state initialized to { data: [], ... } which was
indistinguishable from "API returned empty results". When backend filters
were active, the filteredLogs memo fell back to showing unfiltered `logs`
because it couldn't tell whether a search had completed or not.

Fix: use null as initial state so we can distinguish "not yet searched"
(null → show empty placeholder) from "search returned empty" ({ data: [] }
→ show empty results). This prevents the fallback to unfiltered logs that
caused the model filter to display mismatched data.
2026-03-28 12:03:03 -07:00
ryan-crabbe-berri 2eb3c20e76 Merge pull request #24718 from BerriAI/litellm_ryan-march-26
litellm ryan march 26
2026-03-28 09:01:11 -07:00
Ryan Crabbe 98ecf17550 fix(ui): refactor budget page to React Query hooks and fix crashes
- Migrate budget CRUD from manual state to React Query hooks (useBudgets, useCreateBudget, useUpdateBudget, useDeleteBudget)
- Fix crash when budget list contains null entries by filtering in query hook
- Fix max_budget type from string to number to match DB schema (double precision)
- Disable budget_id field in edit modal to prevent accidental changes
- Use budget_id as React key instead of array index
- Update tests to mock hooks instead of networking functions
2026-03-27 19:34:24 -07:00
Yuneng Jiang a074d1d68b [Infra] Mirror litellm_table_patch source changes (no binaries)
Cherry-pick source-only changes from litellm_table_patch, excluding
build artifacts from the incident response period.

- Remove destructive DROP COLUMN migration (20260311180521_schema_sync)
- Remove now-unnecessary restore migration (20260327232350)
- Bump litellm-proxy-extras 0.4.60 → 0.4.61
- Add regression test to block future DROP COLUMN migrations
- Fix double error handling in getTeamPermissionsCall

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:45:12 -07:00
Ryan Crabbe 7e50af9228 Migrate route_preview.tsx from Tremor to Ant Design
Replace Tremor Card/Title/Subtitle with antd Card/Typography equivalents.
2026-03-23 22:32:26 -07:00
Ryan Crabbe e40f68aec4 test(ui): add unit tests for 5 untested frontend components
- AntDLoadingSpinner: rendering, prop forwarding, icon styling
- MessageManager: static fallback, custom instance delegation
- claude_code_plugins/helpers: all pure utility functions (15 describe blocks, 55 tests)
- AgentSelector: fetch behavior, loading states, error handling, disabled state
- WorkerDropdown: conditional rendering, worker options, selection changes
2026-03-23 22:03:10 -07:00
yuneng-jiang 38d477507d remove outdated e2e test 2026-03-21 23:14:53 -07:00
yuneng-jiang 9073daeebc [Fix] UI - TeamDropdown: Match org dropdown styling and fix test mock
- Use Select.Option with font-medium alias + Text secondary ID to match OrganizationDropdown
- Default page size to 20
- Add useInfiniteTeams mock to AddModelForm tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:52:16 -07:00
yuneng-jiang aea8e32048 [Fix] UI: Team table refresh, infinite team dropdown, leftnav for dashboard routes
- OldTeams: refresh table via fetchTeamsV2 after team create instead of appending
- TeamDropdown: rewrite with useInfiniteTeams for paginated fetch, scroll-to-load, and debounced search
- Update all TeamDropdown consumers to use the new self-fetching API
- Dashboard layout: switch from Sidebar2 to SidebarProvider (leftnav)
- Leftnav: add MIGRATED_PAGES routing for path-based navigation (api-reference)
- Navbar: remove chat button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:05:26 -07:00
yuneng-jiang e3d4c29d37 Merge pull request #24323 from BerriAI/litellm_ryan_march_20
litellm ryan march 20
2026-03-21 15:57:28 -07:00
Krish Dholakia f911d8d865 Merge pull request #23818 from BerriAI/litellm_oss_staging_03_17_2026
fix(fireworks): skip #transform=inline for base64 data URLs (#23729)
2026-03-21 14:54:39 -07:00
Ishaan Jaff 2ea9e207bd Litellm ishaan march 20 (#24303)
* feat(redis): add circuit breaker to RedisCache to fast-fail when Redis is down (#24181)

* feat(redis): add circuit breaker env var constants

* feat(redis): add RedisCircuitBreaker and apply guard decorator to all async ops

* fix(dual_cache): fall back to L1 instead of re-raising on Redis increment failures

* test(caching): add circuit breaker unit tests

* fix(redis): fast-fail concurrent HALF_OPEN probes — only one probe at a time

* fix(dual_cache): return None fallback when in_memory_cache is absent and Redis fails

* test(caching): add regression tests for HALF_OPEN concurrency and None fallback

* Fix blocking sync next in __anext__ (#24177)

* Fix blocking sync next

* Update tests/test_litellm/litellm_core_utils/test_streaming_handler.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix PEP 479 regression in __anext__ sync iterator exhaustion

asyncio.to_thread re-raises thread exceptions inside a coroutine, where
PEP 479 converts StopIteration to RuntimeError before any except clause
can catch it. Add _next_sync_or_exhausted() module-level helper that
catches StopIteration in the thread and returns a sentinel instead, then
raise StopAsyncIteration in the coroutine.

Also rewrites the non-blocking test to use asyncio.gather() instead of
asyncio.create_task() (which returned None on Python 3.9 / pytest-asyncio
in CI), and adds an exhaustion regression test that drains the wrapper
fully and asserts no RuntimeError leaks out.

---------

Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat: add git-subdir source type to claude-code/plugins API (#24223)

Support a third plugin source type `git-subdir` alongside the existing
`github` and `url` types, as documented in the official Claude Code
plugin marketplaces spec.

New format: {"source": "git-subdir", "url": "...", "path": "subdir/path"}

- Validates url and path fields are present and non-empty
- Rejects absolute paths, '..' segments, backslashes, and percent-encoded
  traversal sequences (including double-encoded variants via regex check)
- Extracts path validation into _validate_git_subdir_path() helper
- Updates Pydantic field description to document all three source types
- Adds isValidUrl() check for url/git-subdir source types in the UI form
- Adds "Git Subdir" option to the UI form with a required Path field
- Adds unit tests covering success, update, missing/empty fields,
  path traversal variants, and unknown source type

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [FEAT] add extract_header and extract_footer to Mistral OCR supported params (#24213)

* docs: add git-subdir source type to claude-code plugin marketplace docs (#24289)

* fix(ui): swap J/K keyboard navigation in log details drawer (#24279) (#24286)

J should navigate down (next) and K should navigate up (previous),
matching vim/standard conventions.

* fix: use async_set_cache in user_api_key_auth hot path (#24302)

* fix: use async_set_cache in auth hot path to avoid blocking event loop

* test: assert no blocking set_cache call in _user_api_key_auth_builder

* test: broaden blocking call check to all sync DualCache methods

* test: fix regression test to actually catch blocking cache calls

* fix: ruff lint unused variable + UI build MessageManager error

- litellm/caching/redis_cache.py: remove unused variable 'e' in circuit
  breaker exception handler (F841)
- add_plugin_form.tsx: use MessageManager.error() instead of undefined
  message.error() for git URL validation

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* docs: add REDIS_CIRCUIT_BREAKER env vars to config_settings reference

Add REDIS_CIRCUIT_BREAKER_FAILURE_THRESHOLD and
REDIS_CIRCUIT_BREAKER_RECOVERY_TIMEOUT to the environment variables
reference table so test_env_keys.py passes.

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

---------

Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Vincenzo Barrea <manamana88@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Robert Kirscht <rkirscht242@gmail.com>
Co-authored-by: Imgyu Kim <kimimgo@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
2026-03-21 12:40:11 -07:00
Ryan Crabbe 773871025e Remove required asterisks from v3 login form fields
Hide the red asterisk indicators on Username and Password fields
while keeping the validation rules intact.
2026-03-21 12:32:17 -07:00
ryan-crabbe b21948fdc3 Merge pull request #24315 from BerriAI/litellm_copy_user_id_on_click
polish: add click-to-copy icon on User ID in internal users table
2026-03-21 12:06:36 -07:00
Sameer Kankute 49abf98a27 Merge branch 'main' into litellm_oss_staging_03_17_2026 2026-03-21 21:16:49 +05:30
Ryan Crabbe e1d2ab3a87 test(ui): add unit test for tags dropdown populated via useTags hook
Made-with: Cursor
2026-03-20 22:58:02 -07:00
Ryan Crabbe 8766004c36 fix(ui): use useTags hook so tag dropdown populates on key creation
The create key form used getPredefinedTags() which only extracted tags
from existing keys' metadata. If no keys had tags, the dropdown was
empty. Switch to the existing useTags() React Query hook that fetches
from /tag/list, matching the edit key form behavior.
2026-03-20 22:58:02 -07:00
yuneng-jiang 2ca4fa6189 Merge branch 'main' into litellm_yj_march_19_2026 2026-03-20 17:28:41 -07:00
yuneng-jiang 3ea69c9539 Merge remote-tracking branch 'origin' into litellm_yj_march_19_2026 2026-03-20 12:37:26 -07:00
yuneng-jiang 8c396e5ca9 Merge pull request #24172 from BerriAI/litellm_extract_useChatHistory_hook
[Refactor] Extract useChatHistory hook from ChatUI.tsx
2026-03-20 00:17:07 -07:00
yuneng-jiang bdf24757c0 Merge pull request #24189 from BerriAI/litellm_teams_table_modernize
[Feature] UI - Teams: Modernize Teams Table
2026-03-20 00:16:47 -07:00
yuneng-jiang 9b519c4754 Merge pull request #24192 from BerriAI/litellm_migrate_antd_message_to_context_api
[Fix] UI: AntD Messages Not Rendering
2026-03-20 00:16:04 -07:00
yuneng-jiang 34d954f8cb [Fix] UI: Migrate AntD message API to use context-based MessageManager
AntD v5 static message API doesn't render without an App wrapper or
useMessage() context holder. Mirrors the existing notification pattern
by adding message.useMessage() to AntdGlobalProvider and routing all
calls through a new MessageManager module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 23:29:38 -07:00
Ryan Crabbe ad43a35d76 feat: add control plane for multi-proxy worker management
Adds a control plane capability that enables a central admin instance
to manage multiple regional worker proxies from a single UI.

Backend:
- Worker registry loaded from YAML config (worker_id, name, url)
- /.well-known/litellm-ui-config exposes is_control_plane and workers list
- /v3/login + /v3/login/exchange: opaque code exchange for cross-origin
  username/password auth (JWT never in URL/logs, single-use 60s TTL)
- SSO cookie handoff with return_to → opaque code → exchange
- _validate_return_to: full origin validation (scheme+hostname+port)
- Startup warning when control_plane_url set without Redis
- Both /v3 endpoints gated behind control_plane_url config

Frontend:
- Worker selector dropdown on login page (gated behind is_control_plane)
- Cross-origin SSO code exchange handling on callback
- switchToWorkerUrl: localStorage-persisted worker URL for API calls
- useWorker hook: shared worker state management
- WorkerDropdown in navbar for switching workers
- Logout/switch clears worker state from localStorage

Tests:
- 7 tests for /v3/login + /v3/login/exchange
- 10 tests for _validate_return_to
- 2 tests for control plane discovery endpoint
2026-03-19 22:50:19 -07:00
yuneng-jiang 883611aa91 [Feature] UI - Teams: Modernize teams table with AntD, server-side pagination, and v2 API
Migrate the OldTeams table from Tremor to Ant Design components, matching the
Access Groups page pattern. Switch from /team/list to /v2/team/list for
server-side pagination, filtering, and sorting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 22:42:15 -07:00
yuneng-jiang f34fe4758a fix: stale closure, simplified session isolation, debounce-race in useChatHistory
- clearChatHistory: use functional setChatHistory updater so blob URL
  revocation operates on the latest snapshot, not a stale closure capture.
- Simplified mode: skip sessionStorage hydration and persistence for
  messageTraceId, responsesSessionId, and useApiSessionManagement so
  embedded widgets don't cross-contaminate the full playground session.
- Debounce race: skip re-writing empty chatHistory to sessionStorage
  after clearChatHistory already removed the key.
- Added 5 new tests covering these fixes (39 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:19:51 -07:00
yuneng-jiang 954bfcbd42 chore: remove verbose console.log calls from useChatHistory hook
Removes 17 console.log statements that fired on every streaming chunk
in updateTextUI, updateTimingData, updateUsageData, and other hot-path
functions. The console.error for sessionStorage parse failures is kept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 20:46:51 -07:00
Sameer Kankute c545c969f7 Merge branch 'main' into litellm_oss_staging_03_17_2026 2026-03-20 08:42:41 +05:30
yuneng-jiang e88425b881 refactor: wire ChatUI to use useChatHistory hook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:09:20 -07:00
yuneng-jiang f3c6915d61 feat: add useChatHistory hook with tests (extracted from ChatUI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:09:20 -07:00
yuneng-jiang 2e70c23307 Gate legacy redirect on authLoading to ensure proxyBaseUrl is resolved
The redirect useEffect fires before getUiConfig() completes, so
proxyBaseUrl is always "" on first render. Gate on !authLoading so
the redirect only fires after config is fetched, matching the pattern
used by the login redirect.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 14:05:56 -07:00
yuneng-jiang e9e5ed989c Move legacy redirect from render phase to useEffect
router.replace was called directly during render, which is unsafe in
React 18 concurrent mode. Move it into a useEffect and use a computed
flag (isLegacyRedirect) to show LoadingScreen while redirecting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 13:59:45 -07:00
yuneng-jiang 895951744d Fix leftnav navigation regression and useProxySettings initial state
The leftnav was updated to emit page="api-reference" but only "api_ref"
was in LEGACY_REDIRECTS, causing clicks to fall through to the default
Usage page. Add "api-reference" entry to the redirect map. Also include
LITELLM_UI_API_DOC_BASE_URL in the hook's initial state to avoid a
brief flash of incorrect base URL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 13:36:21 -07:00
yuneng-jiang 519afa494c [Refactor] UI - API Reference: Migrate to path-based routing
Move the API Reference page from query-param routing (?page=api_ref) to
Next.js path-based routing (/ui/api-reference). Add a LEGACY_REDIRECTS
map in the root page.tsx so users with old bookmarks are seamlessly
redirected. Future page migrations only need one new map entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 12:57:17 -07:00
yuneng-jiang 0b07f628ff [Test] UI: Add vitest coverage for 10 previously untested components
Add unit tests for:
- SimpleToolCallBlock, SimpleMessageBlock, CollapsibleMessage, HistoryTree (log details drawer)
- OnboardingForm (onboarding flow)
- TeamsHeaderTabs, TeamsTable (teams page)
- transform_key_info, filter_helpers (key/team helpers)
- queryKeysFactory (query key generation utility)

47 new tests covering conditional rendering, user interactions, data transformation, and error handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 10:30:03 -07:00
Ryan Crabbe 1d7cff22cb feat(ui): add click-to-copy icon on User ID in internal users table
Add a CopyOutlined icon next to the truncated User ID that copies
the full UUID to clipboard on click. Follows the existing pattern
used in model_hub_table_columns.tsx.
2026-03-19 08:57:42 -07:00
Sameer Kankute e2e4f9ed33 Merge branch 'main' into litellm_oss_staging_03_17_2026 2026-03-19 15:53:06 +05:30
yuneng-jiang d984b293de [Feature] UI - Leftnav: Add external link icon to Learning Resources
Add ExportOutlined icon next to nav items that link to external pages,
making it clear to users when a link opens in a new tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:56:25 -07:00
yuneng-jiang 0b63979d45 Fix build: cast endpointType to EndpointType at call site
ChatUI stores endpointType as string but the narrowed prop expects
EndpointType — add explicit cast at the call site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:04:42 -07:00
yuneng-jiang ebe329cdce Fix build: use as any for SyntaxHighlighter style prop
Matches the cast used in ChatUI.tsx — the react-syntax-highlighter
type definitions don't accept CSSProperties directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:02:18 -07:00
yuneng-jiang b55cb249fe Address Greptile feedback: use EndpointType enum, add CHAT MCP test
- Narrow endpointType prop from string to EndpointType enum
- Add missing test for MCP events on CHAT endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:46:41 -07:00