-
Notifications
You must be signed in to change notification settings - Fork 38
Migrate WebUI to use the ClientSDK #363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Migrate WebUI to use the ClientSDK #363
Conversation
…just imports and env; remove legacy src/app/webui
…as; ensure sdk/core deps resolved via workspace
…n; export typed CatalogQuery; update routes to type-narrow
…mode web can find it
…dexto --mode web can find it" This reverts commit c09f96930232b3211f607499ded9038c85b01497.
WalkthroughThis PR centralizes WebUI types through @dexto/client-sdk, adds Zod-based validation to the client SDK, implements many Next.js API proxy routes that call DextoClient with unified error mapping, updates build/packaging scripts, and introduces WebUI helper libs/types and related component typing adjustments. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Browser
participant Route as Next.js Route
participant SDK as DextoClient
participant API as Upstream API
Browser->>Route: HTTP request (GET/POST)
Route->>Route: parse + validate (Zod)
Route->>SDK: create client (baseUrl, apiKey?, ws=false)
alt valid
SDK->>API: call upstream endpoint
API-->>SDK: response (JSON/YAML)
SDK-->>Route: data
Route-->>Browser: 2xx response
else validation error
Route-->>Browser: 400 { error, code: "VALIDATION_ERROR" }
else upstream error
Route->>Route: resolveStatus/resolveMessage
Route-->>Browser: status { error }
end
sequenceDiagram
autonumber
actor Browser
participant ExecuteRoute as /mcp/.../tools/.../execute
participant SDK as DextoClient
participant MCP as MCP Server
Browser->>ExecuteRoute: POST { params, body }
ExecuteRoute->>ExecuteRoute: validate params/body
ExecuteRoute->>SDK: executeMCPTool(serverId, toolName, body)
SDK->>MCP: execute tool
MCP-->>SDK: tool result
SDK-->>ExecuteRoute: { data }
ExecuteRoute-->>Browser: 200 { success: true, data }
alt error
ExecuteRoute->>ExecuteRoute: resolveStatus/resolveMessage
ExecuteRoute-->>Browser: status { error }
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 43
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (10)
packages/webui/components/ErrorBanner.tsx (2)
41-41
: A11y: announce errors to assistive tech.Add ARIA to ensure screen readers are notified when the banner appears.
- <div className="w-full rounded-lg p-4 mb-4 border shadow-sm bg-destructive/10 border-destructive/40"> + <div role="alert" aria-live="assertive" aria-atomic="true" className="w-full rounded-lg p-4 mb-4 border shadow-sm bg-destructive/10 border-destructive/40">
31-35
: Harden against circular structures inissue.context
.
JSON.stringify
throws on circular refs and will break the error UI. Use a safe serializer.Apply:
- if (issue.context) { - const contextStr = typeof issue.context === 'string' ? issue.context : JSON.stringify(issue.context, null, 2); - text += `\nContext: ${contextStr}`; - } + if (issue.context) { + const contextStr = safeStringify(issue.context); + text += `\nContext: ${contextStr}`; + }- {typeof issue.context === 'string' - ? issue.context - : JSON.stringify(issue.context, null, 2) - } + {safeStringify(issue.context)}Add this helper near the top (after imports):
const safeStringify = (value: unknown): string => { if (typeof value === 'string') return value; try { return JSON.stringify(value, null, 2); } catch { return '[Unserializable context]'; } };Also applies to: 109-112
packages/webui/next.config.ts (3)
51-51
:allowedDevOrigins
is not a standard Next.js config key.Unless you’ve augmented NextConfig, this will be ignored and may be a TS type error. Recommend removing it and relying on dev‑only headers below.
Apply this diff to drop the unsupported key:
- allowedDevOrigins: allowedOrigins,
62-69
: CORS for/_next/*
should be dev‑only.Avoid sending
Access-Control-Allow-Origin: *
in production.Apply this diff:
- async headers() { - return [ - { - source: '/_next/:path*', - headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }], - }, - ]; - }, + async headers() { + return _isDev + ? [ + { + source: '/_next/:path*', + headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }], + }, + ] + : []; + },
53-60
: Dev proxy rewrite points to localhost; gate it to dev or make it configurable.Unconditionally proxying to
http://localhost:${apiPort}
will break in prod.Apply this minimal dev‑only fix:
- async rewrites() { - const apiPort = process.env.API_PORT ?? '3001'; - return [ - { - source: '/api/:path*', - destination: `http://localhost:${apiPort}/api/:path*`, // Proxy to backend - }, - ]; - }, + async rewrites() { + if (!_isDev) return []; + const apiPort = process.env.API_PORT ?? '3001'; + return [ + { + source: '/api/:path*', + destination: `http://localhost:${apiPort}/api/:path*`, // Dev proxy + }, + ]; + },Optional: support
API_BASE_URL
for non‑dev deployments and only fall back to localhost in dev.packages/webui/components/ChatApp.tsx (2)
490-495
: Remove no-op IIFE + ESLint disable.This is dead code solely to appease the linter. Drop it to keep the component lean.
- {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} - {(() => { - /* no-op to allow inline constant-like usage below via variable */ - return null; - })()}
179-205
: Avoid throwing plain Error in WebUI — replace with Dexto errors or handle locallyrg found 31 occurrences of throw new Error(...) in packages/webui — replace these with module-specific error factories (DextoRuntimeError / DextoValidationError) or handle the error locally by setting UI error state and early-return.
- packages/webui/components/ServersPanel.tsx: 71, 119, 131, 150, 233
- packages/webui/components/ChatApp.tsx: 188, 213, 316, 354
- packages/webui/components/hooks/ChatContext.tsx: 102, 160, 166, 334
- packages/webui/components/SessionPanel.tsx: 82, 137, 160, 191
- packages/webui/components/SearchPanel.tsx: 126, 137
- packages/webui/components/InputArea.tsx: 313, 321
- packages/webui/components/ApiKeyModal.tsx: 38, 42
- packages/webui/components/GlobalSearchModal.tsx: 74
- packages/webui/components/AddCustomServerModal.tsx: 168, 171, 174, 177, 180
- packages/webui/components/Playground/PlaygroundView.tsx: 116, 162, 344
If keeping local handling, replace throws with setting the component error state (e.g., setExportError(...)) and an early return; otherwise import/use the client-sdk/core error factories and throw those structured errors.
packages/webui/eslint.config.mjs (1)
28-46
: Harden restriction to catch deep @dexto/core imports.no-restricted-imports with paths only blocks exact specifiers. Add patterns to block @dexto/core/* too.
'no-restricted-imports': [ 'error', { - paths: [ + paths: [ { name: '@dexto/core/logger', message: 'Web UI must not import the Node logger. Use the API for runtime or rely on browser console logging.', }, { name: '@dexto/core', message: 'Web UI must source shared types from @dexto/client-sdk to keep browser bundles lean.', }, - ], + ], + patterns: [ + { + group: ['@dexto/core/*'], + message: + 'Do not import from @dexto/core/* in WebUI. Use @dexto/client-sdk for types/API.', + } + ], }, ],packages/webui/README.md (1)
40-56
: Dev docs: switch to pnpm and remove machine-specific prompts.Repo enforces pnpm (preinstall only-allow pnpm). Replace npm commands and clean host/prompt lines to avoid copy‑paste failures.
-Start one server for the API at the root directory of this project [port 3001]: +Start the API at the repo root [port 3001]: ```bash -[ 2:29PM ] [ ~/Projects/dexto(storage✗) ] - $ npm run build && npm link && dexto --mode server +pnpm run build && pnpm -C packages/cli link --global && dexto --mode server-then start the npm dev server [port 3000]
+Then start the WebUI dev server [port 3000]-[ 2:31PM ] [ karaj@MacBook-Pro:~/Projects/dexto/src/app/webui(storage✗) ] -npm run dev +pnpm -C packages/webui dev
-This is temporary because the application functionality uses Dexto APIs built in the same project
+Note: temporary two‑process setup; UI depends on the local Dexto API.</blockquote></details> <details> <summary>packages/webui/types.ts (1)</summary><blockquote> `14-16`: **Avoid any in public types** Prefer `unknown` to satisfy the “avoid any” guideline. ```diff - default?: any; + default?: unknown;
🧹 Nitpick comments (23)
packages/webui/components/ErrorBanner.tsx (1)
21-26
: Fallback to top-levelerror.detailedIssues
when no nestedcontext.detailedIssues
.Prevents dropping useful issues if the server doesn’t nest them.
- const firstIssue = error.detailedIssues?.[0]; - const detailedIssues = (firstIssue?.context && - typeof firstIssue.context === 'object' && - 'detailedIssues' in firstIssue.context) - ? (firstIssue.context as { detailedIssues: Issue[] }).detailedIssues - : []; + const firstIssue = error.detailedIssues?.[0]; + const contextual = + firstIssue?.context && + typeof firstIssue.context === 'object' && + 'detailedIssues' in firstIssue.context + ? (firstIssue.context as { detailedIssues: Issue[] }).detailedIssues + : undefined; + const detailedIssues = contextual ?? (error.detailedIssues ?? []);packages/webui/next.config.ts (1)
16-18
:_isDev
currently unused (pre‑change).It will be used by the proposed guards; otherwise remove to satisfy linters.
packages/webui/package.json (1)
2-3
: Package hygiene: align Next/ESLint versions; confirm name change impact.
- Align eslint-config-next with next 15.5.2 to avoid rule/version drift.
- Renaming to "dexto-webui" is fine for a private pkg; double‑check any workspace scripts or docs that referenced the old name.
- "eslint-config-next": "15.3.1", + "eslint-config-next": "15.5.2",Please run:
- pnpm -C packages/webui next lint
- pnpm -w run typecheck
Also applies to: 9-10, 12-12, 33-35, 36-45
packages/webui/components/ChatApp.tsx (1)
245-271
: Send path looks solid; avoid setTimeout shim unless needed.If ordering is reliable, prefer calling positionLastUserNearTop() and setFollowStreaming(true) immediately after await sendMessage to reduce flicker. Keep the timeout only if you have a reproducible layout race.
- try { - await sendMessage(content, imageData, fileData); - // After sending, position the new user message near the top, - // then enable followStreaming to follow the assistant reply. - setTimeout(() => { - positionLastUserNearTop(); - setFollowStreaming(true); - }, 0); + try { + await sendMessage(content, imageData, fileData); + positionLastUserNearTop(); + setFollowStreaming(true);scripts/copy-webui-dist.ts (1)
29-31
: Copying both .next and .next/standalone inflates payload.If standalone exists, copying entire .next is redundant and large. Consider preferring standalone first, and only copying full .next as a guarded fallback.
packages/webui/app/api/message-sync/route.ts (2)
7-16
: Optional: centralize DextoClient creation to a small helper to remove repetition across routes.Avoids drift and simplifies env handling.
Example helper (new file) you can reuse:
// packages/webui/lib/make-client.ts import { DextoClient } from '@dexto/client-sdk'; export function makeClient(options: { enableWebSocket?: boolean } = {}) { return new DextoClient( { baseUrl: process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001', ...(process.env.DEXTO_API_KEY ? { apiKey: process.env.DEXTO_API_KEY } : {}), }, { enableWebSocket: false, ...options } ); }Then in routes:
- const client = new DextoClient( - { /* ... */ }, - { enableWebSocket: false } - ); + const client = makeClient({ enableWebSocket: false });
18-26
: Validate message is a non‑empty string; return 400 early.Prevents opaque 500s from downstream validation.
Apply this diff:
- const { message, sessionId, imageData, fileData } = await req.json(); + const { message, sessionId, imageData, fileData } = await req.json(); + if (typeof message !== 'string' || message.trim().length === 0) { + return NextResponse.json( + { error: 'message must be a non-empty string' }, + { status: 400 } + ); + }packages/webui/app/api/llm/current/route.ts (1)
7-16
: Optional: use shared client factory as in other routes.See suggested makeClient helper in message-sync comment and replace the inline constructor here.
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts (1)
19-26
: Optional sanity check for serverId.Dynamic routes should provide it, but a guard yields clearer 400s if misrouted.
Suggested snippet:
if (!serverId || typeof serverId !== 'string') { return NextResponse.json({ error: 'serverId is required' }, { status: 400 }); }packages/webui/app/api/search/messages/route.ts (2)
45-49
: Return 400 for invalid role instead of silently dropping it.Improves input validation consistency with limit/offset checks.
Apply this diff:
- const allowedRoles = new Set<string>(['user', 'assistant', 'system', 'tool']); - const role = - roleRaw && allowedRoles.has(roleRaw) - ? (roleRaw as 'user' | 'assistant' | 'system' | 'tool') - : undefined; + const allowedRoles = new Set<string>(['user', 'assistant', 'system', 'tool']); + if (roleRaw && !allowedRoles.has(roleRaw)) { + return NextResponse.json( + { error: 'role must be one of: user, assistant, system, tool' }, + { status: 400 } + ); + } + const role = roleRaw as 'user' | 'assistant' | 'system' | 'tool' | undefined;
5-16
: Optional: hoist allowedRoles to module scope to avoid per-request allocations.Add at top-level:
const allowedRoles = new Set<string>(['user', 'assistant', 'system', 'tool']);Then drop the in-function Set creation and use the shared constant.
packages/webui/app/api/reset/route.ts (1)
27-32
: Reject empty string sessionId (not just non-string).Prevents sending meaningless IDs downstream.
Apply this diff:
- if (sessionId !== undefined && typeof sessionId !== 'string') { + if (sessionId !== undefined && typeof sessionId !== 'string') { return NextResponse.json( { error: 'sessionId must be a string if provided' }, { status: 400 } ); } + if (typeof sessionId === 'string' && sessionId.trim().length === 0) { + return NextResponse.json( + { error: 'sessionId cannot be empty' }, + { status: 400 } + ); + }packages/webui/app/api/connect-server/route.ts (3)
28-30
: Treat whitespace-only names as invalid.Use trim() to avoid accepting " ".
- if (typeof name !== 'string' || name.length === 0) { + if (typeof name !== 'string' || name.trim().length === 0) { return NextResponse.json({ error: 'name must be a non-empty string' }, { status: 400 }); }
7-16
: DRY: factor DextoClient construction into a shared helper.Client bootstrapping is duplicated across routes; extract to lib (e.g., createClient({ enableWebSocket?: boolean })) and reuse here.
Example helper (new file): packages/webui/lib/client.ts
import { DextoClient } from '@dexto/client-sdk'; export function createClient(opts: { enableWebSocket?: boolean } = {}) { return new DextoClient( { baseUrl: process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001', ...(process.env.DEXTO_API_KEY ? { apiKey: process.env.DEXTO_API_KEY } : {}), }, { enableWebSocket: Boolean(opts.enableWebSocket) } ); }Usage in this file:
- const client = new DextoClient( - { /* ... */ }, - { enableWebSocket: false } - ); + const client = createClient({ enableWebSocket: false });
43-43
: Align error copy across MCP routes.Consider “Failed to connect MCP server” for consistency with /mcp/servers.
packages/webui/app/api/sessions/current/route.ts (1)
7-16
: Reuse a shared DextoClient factory.Same client construction pattern appears in many routes; centralize as a helper.
(See suggested createClient helper in connect-server comment.)
packages/webui/app/api/message/route.ts (1)
29-35
: Harmonize error mapping with message-sync route (400 on Zod/SyntaxError).Replicate the validation/SyntaxError checks used in packages/webui/app/api/message-sync/route.ts for consistent client UX.
packages/webui/app/api/llm/catalog/route.ts (2)
44-44
: Use template literal in logs; avoid comma-separated args.Conform to logging guideline.
- console.error('Error fetching LLM catalog:', error); + console.error(`Error fetching LLM catalog: ${error instanceof Error ? error.message : String(error)}`);
9-18
: Prefer shared client factory to reduce duplication.(See createClient helper suggestion in connect-server.)
packages/webui/app/api/config.yaml/route.ts (2)
25-31
: Avoidany
in catch and use shared error resolvers for consistency.Switch to
unknown
and resolve message/status uniformly.-import { NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; +import { resolveStatus, resolveMessage } from '@/lib/api-error.js'; @@ - } catch (err: any) { - const status = 500; - return NextResponse.json( - { error: err?.message || 'Failed to fetch config.yaml' }, - { status } - ); + } catch (err: unknown) { + const status = resolveStatus(err, 500); + return NextResponse.json( + { error: resolveMessage(err, 'Failed to fetch config.yaml') }, + { status } + ); }
11-18
: Optional: build query with URL/URLSearchParams to avoid manual string concatenation.Minor readability/escaping win.
- const params = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : ''; - const timeoutMs = Number(process.env.DEXTO_HTTP_TIMEOUT_MS) || 15000; - const res = await fetch(`${baseUrl}/api/config.yaml${params}`, { + const qs = new URLSearchParams(sessionId ? { sessionId } : {}).toString(); + const timeoutMs = Number(process.env.DEXTO_HTTP_TIMEOUT_MS) || 15000; + const res = await fetch(`${baseUrl}/api/config.yaml${qs ? `?${qs}` : ''}`, { cache: 'no-store', signal: AbortSignal.timeout?.(timeoutMs), headers: { Accept: 'application/x-yaml', ...(process.env.DEXTO_API_KEY ? { 'X-API-Key': process.env.DEXTO_API_KEY } : {}), }, });packages/webui/app/api/sessions/route.ts (1)
5-17
: Reduce duplication: centralize DextoClient constructionClient construction is repeated across routes. Extract to a helper (e.g., packages/webui/lib/client.ts) and reuse.
New helper:
// packages/webui/lib/client.ts import { DextoClient } from '@dexto/client-sdk'; export function createWebClient() { return new DextoClient( { baseUrl: process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001', ...(process.env.DEXTO_API_KEY ? { apiKey: process.env.DEXTO_API_KEY } : {}), }, { enableWebSocket: false } ); }Usage:
- const client = new DextoClient( - { /* ... */ }, - { enableWebSocket: false } - ); + const client = createWebClient();Also applies to: 29-41
packages/webui/types.ts (1)
61-62
: Stale comment references @core; update to SDKComment mentions
@core/tools/types.js
but the code imports from@dexto/client-sdk
. Update or remove to avoid confusion.-// ToolCall is now imported from @core/tools/types.js +// ToolCall is imported from @dexto/client-sdk
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (43)
package.json
(1 hunks)packages/client-sdk/src/types.ts
(3 hunks)packages/webui/README.md
(1 hunks)packages/webui/app/api/config.yaml/route.ts
(1 hunks)packages/webui/app/api/connect-server/route.ts
(1 hunks)packages/webui/app/api/greeting/route.ts
(1 hunks)packages/webui/app/api/llm/catalog/route.ts
(1 hunks)packages/webui/app/api/llm/current/route.ts
(1 hunks)packages/webui/app/api/llm/providers/route.ts
(1 hunks)packages/webui/app/api/llm/switch/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/[serverId]/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/route.ts
(1 hunks)packages/webui/app/api/message-sync/route.ts
(1 hunks)packages/webui/app/api/message/route.ts
(1 hunks)packages/webui/app/api/reset/route.ts
(1 hunks)packages/webui/app/api/search/messages/route.ts
(1 hunks)packages/webui/app/api/search/sessions/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/history/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/load/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/route.ts
(1 hunks)packages/webui/app/api/sessions/current/route.ts
(1 hunks)packages/webui/app/api/sessions/route.ts
(1 hunks)packages/webui/app/playground/page.tsx
(1 hunks)packages/webui/components/ChatApp.tsx
(4 hunks)packages/webui/components/ConnectServerModal.tsx
(1 hunks)packages/webui/components/ErrorBanner.tsx
(1 hunks)packages/webui/components/ModelPicker/ModelPickerModal.tsx
(5 hunks)packages/webui/components/ModelPicker/ProviderSection.tsx
(1 hunks)packages/webui/components/ModelPicker/constants.tsx
(3 hunks)packages/webui/components/ModelPicker/types.ts
(1 hunks)packages/webui/components/ServersPanel.tsx
(5 hunks)packages/webui/components/hooks/useChat.ts
(4 hunks)packages/webui/eslint.config.mjs
(2 hunks)packages/webui/lib/api-error.ts
(1 hunks)packages/webui/lib/types.ts
(1 hunks)packages/webui/lib/validation.ts
(1 hunks)packages/webui/next.config.ts
(1 hunks)packages/webui/package.json
(2 hunks)packages/webui/tsconfig.json
(0 hunks)packages/webui/types.ts
(2 hunks)scripts/copy-webui-dist.ts
(2 hunks)
💤 Files with no reviewable changes (1)
- packages/webui/tsconfig.json
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/file_extension.mdc)
For any new imports, ensure the import ends with .js for it to work
Files:
packages/webui/app/api/llm/catalog/route.ts
packages/webui/components/ModelPicker/ProviderSection.tsx
packages/webui/app/api/search/messages/route.ts
packages/webui/app/api/search/sessions/route.ts
packages/webui/app/api/reset/route.ts
packages/webui/app/api/config.yaml/route.ts
packages/webui/app/api/mcp/servers/[serverId]/route.ts
packages/webui/app/api/sessions/[sessionId]/route.ts
packages/webui/app/api/llm/providers/route.ts
packages/webui/app/api/mcp/servers/route.ts
packages/webui/app/api/message/route.ts
packages/webui/app/api/llm/switch/route.ts
packages/webui/components/ConnectServerModal.tsx
packages/webui/app/api/connect-server/route.ts
packages/webui/lib/api-error.ts
packages/webui/app/api/sessions/[sessionId]/load/route.ts
packages/webui/app/api/greeting/route.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts
scripts/copy-webui-dist.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts
packages/webui/app/playground/page.tsx
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/current/route.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ModelPicker/constants.tsx
packages/webui/app/api/sessions/current/route.ts
packages/webui/app/api/sessions/route.ts
packages/webui/components/hooks/useChat.ts
packages/webui/app/api/message-sync/route.ts
packages/webui/types.ts
packages/webui/next.config.ts
packages/webui/lib/validation.ts
packages/webui/lib/types.ts
packages/webui/components/ServersPanel.tsx
packages/webui/components/ModelPicker/types.ts
packages/client-sdk/src/types.ts
packages/webui/components/ChatApp.tsx
packages/webui/components/ModelPicker/ModelPickerModal.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}
: Use template literals with ${} for variables in logger calls; do not use comma-separated variables
Do not concatenate strings in logs (avoid '...'+var); use template literals instead
No trailing commas in logger parameter lists
Maintain consistent logger parameter order: message first, then context (e.g., null), then color
Error logging: prefer messages likeOperation failed: ${error.message}
or including the error in a template literal
Include contextual info in error logs, e.g.,Error in ${functionName}: ${error.message}
Info logs with colors should pass color as third argument (e.g., 'cyan', 'green', 'yellow') and message as first argument
Use appropriate colors: green=success, red=errors, yellow=warnings, cyan/cyanBright=status, blue=progress, magenta=special, gray=secondary
For debug logs, serialize objects with JSON.stringify and include call context (e.g., function name and args)
**/*.{ts,tsx}
: Zod schemas for configuration objects must call .strict()
Prefer discriminatedUnion over union for Zod schema unions
Use .describe() on every Zod schema field
Provide sensible defaults via .default() in Zod schemas
Use superRefine for complex cross-field validation in Zod
Prefer direct imports internally; avoid deep re-export chains
Avoid wildcard exports; use explicit named exports
Use strict null safety; handle null/undefined explicitly
Use type guards and provide proper error messages when handling errors
Avoid any; use specific types. In tests, prefer @ts-expect-error over as any for invalid input cases
Files:
packages/webui/app/api/llm/catalog/route.ts
packages/webui/components/ModelPicker/ProviderSection.tsx
packages/webui/app/api/search/messages/route.ts
packages/webui/app/api/search/sessions/route.ts
packages/webui/app/api/reset/route.ts
packages/webui/app/api/config.yaml/route.ts
packages/webui/app/api/mcp/servers/[serverId]/route.ts
packages/webui/app/api/sessions/[sessionId]/route.ts
packages/webui/app/api/llm/providers/route.ts
packages/webui/app/api/mcp/servers/route.ts
packages/webui/app/api/message/route.ts
packages/webui/app/api/llm/switch/route.ts
packages/webui/components/ConnectServerModal.tsx
packages/webui/app/api/connect-server/route.ts
packages/webui/lib/api-error.ts
packages/webui/app/api/sessions/[sessionId]/load/route.ts
packages/webui/app/api/greeting/route.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts
scripts/copy-webui-dist.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts
packages/webui/app/playground/page.tsx
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/current/route.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ModelPicker/constants.tsx
packages/webui/app/api/sessions/current/route.ts
packages/webui/app/api/sessions/route.ts
packages/webui/components/hooks/useChat.ts
packages/webui/app/api/message-sync/route.ts
packages/webui/types.ts
packages/webui/next.config.ts
packages/webui/lib/validation.ts
packages/webui/lib/types.ts
packages/webui/components/ServersPanel.tsx
packages/webui/components/ModelPicker/types.ts
packages/client-sdk/src/types.ts
packages/webui/components/ChatApp.tsx
packages/webui/components/ModelPicker/ModelPickerModal.tsx
**/*.{ts,js,tsx,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,js,tsx,mjs,cjs}
: Use Result<T,C> helpers (ok, fail, hasErrors, splitIssues, zodToIssues) from packages/core/src/utils/result.js instead of ad-hoc result handling
Do not throw plain Error; use DextoRuntimeError or DextoValidationError via module-specific error factories
Files:
packages/webui/app/api/llm/catalog/route.ts
packages/webui/components/ModelPicker/ProviderSection.tsx
packages/webui/app/api/search/messages/route.ts
packages/webui/app/api/search/sessions/route.ts
packages/webui/app/api/reset/route.ts
packages/webui/app/api/config.yaml/route.ts
packages/webui/app/api/mcp/servers/[serverId]/route.ts
packages/webui/app/api/sessions/[sessionId]/route.ts
packages/webui/app/api/llm/providers/route.ts
packages/webui/app/api/mcp/servers/route.ts
packages/webui/app/api/message/route.ts
packages/webui/app/api/llm/switch/route.ts
packages/webui/components/ConnectServerModal.tsx
packages/webui/app/api/connect-server/route.ts
packages/webui/lib/api-error.ts
packages/webui/app/api/sessions/[sessionId]/load/route.ts
packages/webui/app/api/greeting/route.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts
scripts/copy-webui-dist.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts
packages/webui/app/playground/page.tsx
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/current/route.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ModelPicker/constants.tsx
packages/webui/app/api/sessions/current/route.ts
packages/webui/app/api/sessions/route.ts
packages/webui/components/hooks/useChat.ts
packages/webui/app/api/message-sync/route.ts
packages/webui/types.ts
packages/webui/next.config.ts
packages/webui/lib/validation.ts
packages/webui/eslint.config.mjs
packages/webui/lib/types.ts
packages/webui/components/ServersPanel.tsx
packages/webui/components/ModelPicker/types.ts
packages/client-sdk/src/types.ts
packages/webui/components/ChatApp.tsx
packages/webui/components/ModelPicker/ModelPickerModal.tsx
**/*.{ts,tsx,js,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
All import specifiers must end with .js for ESM compatibility
Files:
packages/webui/app/api/llm/catalog/route.ts
packages/webui/components/ModelPicker/ProviderSection.tsx
packages/webui/app/api/search/messages/route.ts
packages/webui/app/api/search/sessions/route.ts
packages/webui/app/api/reset/route.ts
packages/webui/app/api/config.yaml/route.ts
packages/webui/app/api/mcp/servers/[serverId]/route.ts
packages/webui/app/api/sessions/[sessionId]/route.ts
packages/webui/app/api/llm/providers/route.ts
packages/webui/app/api/mcp/servers/route.ts
packages/webui/app/api/message/route.ts
packages/webui/app/api/llm/switch/route.ts
packages/webui/components/ConnectServerModal.tsx
packages/webui/app/api/connect-server/route.ts
packages/webui/lib/api-error.ts
packages/webui/app/api/sessions/[sessionId]/load/route.ts
packages/webui/app/api/greeting/route.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts
scripts/copy-webui-dist.ts
packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts
packages/webui/app/playground/page.tsx
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/current/route.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ModelPicker/constants.tsx
packages/webui/app/api/sessions/current/route.ts
packages/webui/app/api/sessions/route.ts
packages/webui/components/hooks/useChat.ts
packages/webui/app/api/message-sync/route.ts
packages/webui/types.ts
packages/webui/next.config.ts
packages/webui/lib/validation.ts
packages/webui/eslint.config.mjs
packages/webui/lib/types.ts
packages/webui/components/ServersPanel.tsx
packages/webui/components/ModelPicker/types.ts
packages/client-sdk/src/types.ts
packages/webui/components/ChatApp.tsx
packages/webui/components/ModelPicker/ModelPickerModal.tsx
🧠 Learnings (18)
📚 Learning: 2025-09-01T13:02:37.373Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#307
File: src/app/webui/components/hooks/useChat.ts:7-7
Timestamp: 2025-09-01T13:02:37.373Z
Learning: The team plans to remove duplicate types across the codebase shortly, including consolidating LLMProvider types between core/llm/registry.js and src/app/webui/types.ts, rather than using temporary aliasing solutions.
Applied to files:
packages/webui/components/ModelPicker/ProviderSection.tsx
packages/webui/components/ModelPicker/constants.tsx
packages/webui/components/ModelPicker/types.ts
packages/webui/components/ModelPicker/ModelPickerModal.tsx
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/middleware/errorHandler.ts : Map error types to HTTP status via mapErrorTypeToStatus; keep docs in sync with this source of truth
Applied to files:
packages/webui/lib/api-error.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/core/src/config/errors.ts : Follow the error factory pattern shown in packages/core/src/config/errors.ts when creating new module errors
Applied to files:
packages/webui/lib/api-error.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,js,tsx,mjs,cjs} : Do not throw plain Error; use DextoRuntimeError or DextoValidationError via module-specific error factories
Applied to files:
packages/webui/lib/api-error.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-08-25T15:34:53.807Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#298
File: src/core/llm/formatters/vercel.ts:206-229
Timestamp: 2025-08-25T15:34:53.807Z
Learning: The Vercel AI SDK v5 ToolResultPart.output uses `value` as the property name for all type variants (text, json, error-text, error-json, content), not separate property names like `text`, `json`, `content`. The correct type structure is:
```typescript
const output: {
type: "text";
value: string;
} | {
type: "json";
value: JSONValue;
} | {
type: "error-text";
value: string;
} | {
type: "error-json";
value: JSONValue;
}
```
Applied to files:
packages/webui/types.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Run pnpm run build, pnpm test, pnpm run lint, and pnpm run typecheck before completing any task
Applied to files:
package.json
📚 Learning: 2025-09-11T11:56:25.329Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#322
File: packages/core/tsconfig.json:8-11
Timestamp: 2025-09-11T11:56:25.329Z
Learning: In the Dexto monorepo, packages/core uses tsup with bundle: true for building, which automatically resolves TypeScript path aliases like "core/*" during the bundling process, making them safe to use unlike plain tsc compilation.
Applied to files:
packages/webui/next.config.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T12:14:01.516Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#322
File: packages/core/package.json:48-52
Timestamp: 2025-09-11T12:14:01.516Z
Learning: In the Dexto monorepo, packages/core uses tsup with bundle: true for building, which automatically resolves TypeScript path aliases like "core/*" during the bundling process, making them safe to use unlike plain tsc compilation.
Applied to files:
packages/webui/next.config.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-12T09:29:43.238Z
Learnt from: shaunak99
PR: truffle-ai/dexto#345
File: packages/client-sdk/src/types.ts:7-7
Timestamp: 2025-09-12T09:29:43.238Z
Learning: The .js extension requirement for ESM compatibility applies to relative imports (./file.js, ../file.js) and internal project imports, but NOT to package imports (e.g., 'dexto/core', 'zod'). Package imports rely on package.json entry point resolution.
Applied to files:
packages/webui/next.config.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct imports internally; avoid deep re-export chains
Applied to files:
packages/webui/next.config.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx,js,mjs,cjs} : All import specifiers must end with .js for ESM compatibility
Applied to files:
packages/webui/next.config.ts
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/**/*.ts : Validate API request bodies with Zod at the API boundary (defensive validation) before calling DextoAgent
Applied to files:
packages/webui/lib/validation.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Use superRefine for complex cross-field validation in Zod
Applied to files:
packages/webui/lib/validation.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Avoid wildcard exports; use explicit named exports
Applied to files:
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Use strict null safety; handle null/undefined explicitly
Applied to files:
packages/webui/eslint.config.mjs
📚 Learning: 2025-08-27T05:18:32.379Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#300
File: src/app/webui/components/MessageList.tsx:19-19
Timestamp: 2025-08-27T05:18:32.379Z
Learning: ESM import rule (.js extension requirement) does not apply to files in the `src/app/webui` directory - webui has different import conventions than the rest of the codebase.
Applied to files:
packages/webui/eslint.config.mjs
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Avoid any; use specific types. In tests, prefer ts-expect-error over as any for invalid input cases
Applied to files:
packages/webui/eslint.config.mjs
📚 Learning: 2025-08-12T12:24:31.480Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-08-12T12:24:31.480Z
Learning: During development, use npm run test:unit:watch
Applied to files:
packages/webui/README.md
🧬 Code graph analysis (26)
packages/webui/app/api/llm/catalog/route.ts (2)
packages/webui/lib/validation.ts (3)
validateQuery
(201-224)CatalogQuery
(21-21)CatalogQuerySchema
(10-19)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/search/messages/route.ts (3)
packages/webui/app/api/search/sessions/route.ts (1)
GET
(5-34)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/search/sessions/route.ts (2)
packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/reset/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/config.yaml/route.ts (1)
packages/webui/app/api/greeting/route.ts (1)
GET
(5-29)
packages/webui/app/api/mcp/servers/[serverId]/route.ts (3)
packages/webui/app/api/sessions/[sessionId]/route.ts (1)
DELETE
(30-53)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/sessions/[sessionId]/route.ts (6)
packages/webui/app/api/sessions/[sessionId]/history/route.ts (1)
GET
(5-31)packages/webui/app/api/sessions/current/route.ts (1)
GET
(5-27)packages/webui/app/api/sessions/route.ts (1)
GET
(5-27)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)packages/webui/app/api/mcp/servers/[serverId]/route.ts (1)
DELETE
(5-28)
packages/webui/app/api/llm/providers/route.ts (2)
packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/mcp/servers/route.ts (3)
packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)packages/webui/app/api/connect-server/route.ts (1)
POST
(5-47)
packages/webui/app/api/message/route.ts (3)
packages/webui/app/api/message-sync/route.ts (1)
POST
(5-46)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (3)
errorHasCode
(53-55)resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/llm/switch/route.ts (2)
packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/connect-server/route.ts (3)
packages/webui/app/api/mcp/servers/route.ts (1)
POST
(29-55)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/sessions/[sessionId]/load/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/greeting/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/mcp/servers/[serverId]/tools/route.ts (3)
packages/webui/app/api/mcp/servers/route.ts (1)
GET
(5-27)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (3)
errorHasCode
(53-55)resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/mcp/servers/[serverId]/tools/[toolName]/execute/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/llm/current/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/sessions/[sessionId]/history/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/sessions/current/route.ts (1)
packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/app/api/sessions/route.ts (2)
packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/components/hooks/useChat.ts (1)
packages/webui/lib/api-error.ts (1)
toAppError
(61-86)
packages/webui/app/api/message-sync/route.ts (3)
packages/webui/app/api/message/route.ts (1)
POST
(5-36)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (3)
errorHasCode
(53-55)resolveStatus
(12-30)resolveMessage
(35-48)
packages/webui/lib/types.ts (3)
packages/webui/components/hooks/useChat.ts (1)
ImagePart
(18-22)packages/client-sdk/src/types.ts (2)
ImagePart
(51-51)CatalogResponse
(119-122)packages/webui/components/ModelPicker/types.ts (2)
ProviderCatalog
(12-12)CatalogResponse
(7-7)
packages/webui/components/ModelPicker/types.ts (1)
packages/webui/lib/types.ts (1)
ProviderCatalog
(40-40)
packages/webui/components/ChatApp.tsx (2)
packages/client-sdk/src/client.ts (1)
sendMessage
(137-155)packages/webui/types.ts (1)
ServerRegistryEntry
(104-140)
packages/webui/components/ModelPicker/ModelPickerModal.tsx (1)
packages/webui/components/ModelPicker/constants.tsx (3)
PROVIDER_LOGOS
(8-16)LLMProviderID
(5-5)needsDarkModeInversion
(56-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review continued from previous batch...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/client-sdk/src/types.ts (1)
66-70
: Normalize baseURL/baseUrl in the client SDK (support both or standardize to baseURL)ClientConfig in packages/client-sdk/src/types.ts uses
baseUrl
while the SDK validation and core LLM schemas usebaseURL
— this causes a type/validation mismatch.
- Change
ClientConfig.baseUrl
→baseURL
(packages/client-sdk/src/types.ts:66-70) and update usages (packages/client-sdk/src/client.ts, http-client.ts, tests, and callers in webui).- Or keep backwards compatibility by normalizing in the constructor:
const baseURL = config.baseURL ?? config.baseUrl;
and usebaseURL
everywhere (also update packages/client-sdk/src/validation.ts to expectbaseURL
).
🧹 Nitpick comments (12)
packages/webui/components/ErrorBanner.tsx (2)
21-26
: Use a type guard to simplify/narrowcontext
instead of inline casts.Improves readability and narrows types safely (also checks array-ness), avoiding repeated
typeof
/in
checks and casts.Apply within this hunk:
- const firstIssue = error.detailedIssues?.[0]; - const detailedIssues = (firstIssue?.context && - typeof firstIssue.context === 'object' && - 'detailedIssues' in firstIssue.context) - ? (firstIssue.context as { detailedIssues: Issue[] }).detailedIssues - : []; + const firstIssue = error.detailedIssues?.[0]; + const ctx = firstIssue?.context; + const detailedIssues = hasDetailedIssuesContext(ctx) + ? ctx.detailedIssues + : [];Place this helper near the top of the file:
function hasDetailedIssuesContext(value: unknown): value is { detailedIssues: Issue[] } { if (!value || typeof value !== 'object') return false; const obj = value as Record<string, unknown>; return Array.isArray(obj.detailedIssues); }
41-41
: A11y: mark the banner as an alert for screen readers.Adds live-region semantics so errors are announced immediately.
- <div className="w-full rounded-lg p-4 mb-4 border shadow-sm bg-destructive/10 border-destructive/40"> + <div role="alert" aria-live="assertive" aria-atomic="true" className="w-full rounded-lg p-4 mb-4 border shadow-sm bg-destructive/10 border-destructive/40">packages/client-sdk/package.json (1)
19-20
: Use exact semver range for Zod in dependencies.The caret range
^3.25.76
allows minor version updates that could introduce breaking changes. Since this is a public SDK package, consider using an exact version to ensure consumers get predictable behavior.- "zod": "^3.25.76" + "zod": "3.25.76"packages/webui/app/api/llm/switch/route.ts (2)
27-31
: Improve Zod validation error messages.The current error message construction joins all error messages with commas, which can be hard to read. Consider using the formatted error helper or structured error response.
- const result = LLMSwitchRequestSchema.safeParse(body); - if (!result.success) { - const message = result.error.errors.map((e) => e.message).join(', '); - return NextResponse.json({ error: `Invalid request: ${message}` }, { status: 400 }); - } + const result = LLMSwitchRequestSchema.safeParse(body); + if (!result.success) { + const issues = result.error.issues.map((issue) => ({ + path: issue.path.join('.'), + message: issue.message + })); + return NextResponse.json({ + error: 'Invalid request', + details: issues + }, { status: 400 }); + }
33-39
: Clean up type casting for LLM config.The type assertions and intermediate variables make the code harder to follow. Consider simplifying the transformation logic.
- const { sessionId, ...configInput } = result.data; - const configPayload = configInput as Partial<LLMConfig> & { maxTokens?: number }; - const { maxTokens, ...llmConfig } = configPayload; - const normalizedConfig: Partial<LLMConfig> = { ...llmConfig }; - if (typeof maxTokens === 'number') { - normalizedConfig.maxOutputTokens = maxTokens; - } + const { sessionId, maxTokens, ...llmConfig } = result.data; + const normalizedConfig: Partial<LLMConfig> = { + ...llmConfig, + ...(typeof maxTokens === 'number' && { maxOutputTokens: maxTokens }) + };scripts/copy-webui-dist.ts (1)
28-43
: Consider validating the webui build before copying.Before copying files, it would be helpful to validate that the webui has been built successfully by checking for key build artifacts.
// Create target directory await fs.ensureDir(targetDir); + // Validate webui build exists + const nextBuildPath = path.join(sourceWebUIDir, '.next'); + if (!fs.existsSync(nextBuildPath)) { + console.error('❌ WebUI build not found. Please run "pnpm -C packages/webui build" first.'); + process.exit(1); + } + // Copy standalone build files and necessary config. The root `.next` already // contains both `standalone` and `static`, so avoid re-copying nested paths // that can introduce symlink/self-reference errors during fs copy.packages/client-sdk/src/validation.ts (5)
114-147
: Prefer discriminatedUnion over superRefine for MCP server config; add missing .describe() and sensible defaults.Using a discriminated union makes requirements explicit and removes custom error wiring. Also several fields lack .describe(), and args/env can safely default.
Apply this diff:
-export const McpServerRequestSchema = z - .object({ - name: z.string().min(1, 'Server name is required'), - config: z - .object({ - type: z.enum(['stdio', 'sse', 'http']), - command: z.string().optional(), - args: z.array(z.string()).optional(), - url: z.string().url().optional(), - env: z.record(z.string()).optional(), - }) - .strict() - .superRefine((cfg, ctx) => { - if (cfg.type === 'stdio') { - if (!cfg.command) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'command is required for stdio', - path: ['command'], - }); - } - } else if (cfg.type === 'sse' || cfg.type === 'http') { - if (!cfg.url) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'url is required for sse/http', - path: ['url'], - }); - } - } - }), - }) - .strict() - .describe('Request body for registering a new MCP server'); +export const McpServerRequestSchema = z + .object({ + name: z + .string() + .min(1, 'Server name is required') + .describe('Human‑readable MCP server name'), + config: z + .discriminatedUnion('type', [ + z + .object({ + type: z.literal('stdio').describe('Use stdio transport'), + command: z + .string() + .min(1, 'command is required for stdio') + .describe('Executable to launch the MCP server'), + args: z.array(z.string()).default([]).describe('Args for the process'), + env: z.record(z.string()).default({}).describe('Environment variables'), + }) + .strict(), + z + .object({ + type: z.literal('sse').describe('Use Server‑Sent Events transport'), + url: z.string().url().describe('Base URL for SSE MCP server'), + env: z.record(z.string()).default({}).describe('Environment variables'), + }) + .strict(), + z + .object({ + type: z.literal('http').describe('Use HTTP transport'), + url: z.string().url().describe('Base URL for HTTP MCP server'), + env: z.record(z.string()).default({}).describe('Environment variables'), + }) + .strict(), + ]) + .describe('Transport configuration for the MCP server'), + }) + .strict() + .describe('Request body for registering a new MCP server');
58-93
: Tighten LLMSwitch validation; align naming with ClientConfig.
- Ensure provided strings aren’t empty; enforce integer for maxTokens.
- Naming mismatch: this schema uses baseURL while ClientConfig uses baseUrl. Pick one and alias the other to avoid confusion.
Apply this diff:
- provider: z.string().describe('LLM provider to target').optional(), - model: z.string().describe('Model identifier provided by the selected provider').optional(), + provider: z.string().min(1).describe('LLM provider to target').optional(), + model: z.string().min(1).describe('Model identifier provided by the selected provider').optional(), router: z .enum(['vercel', 'in-built']) .describe('Routing layer to use for the model') .optional(), - baseURL: z + baseURL: z .string() .url() .describe('Optional custom API base URL for compatible providers') .optional(), - apiKey: z - .string() + apiKey: z + .string() + .min(1, 'API key cannot be empty') .describe('API key to use when switching to a different provider') .optional(), temperature: z .number() .min(0) .max(2) .describe('Sampling temperature controlling response creativity (0-2)') .optional(), - maxTokens: z - .number() - .min(1) + maxTokens: z + .number() + .int() + .min(1) .describe('Maximum number of tokens to allow for the response') .optional(), - sessionId: z - .string() + sessionId: z + .string() + .min(1, 'sessionId cannot be empty') .describe('Optional session identifier to scope the LLM switch') .optional(),Follow-up: If we keep baseURL here, consider accepting baseUrl as an alias and normalizing internally.
37-53
: Stricter MIME validation for fileData.Use a basic MIME pattern to avoid empty/invalid tokens.
Apply this diff:
- mimeType: z - .string() - .min(1, 'MIME type is required') + mimeType: z + .string() + .regex(/^[a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+$/, 'Invalid MIME type') .describe('MIME type describing the file attachment'),
96-100
: Ensure sessionId is non-empty when present/required.Empty strings currently pass. Add .min(1) to prevent bad IDs reaching handlers.
Apply this diff:
- sessionId: z - .string() + sessionId: z + .string() + .min(1, 'sessionId cannot be empty') .describe('Optional custom identifier to assign to the created session') .optional(), @@ - sessionId: z - .string() + sessionId: z + .string() + .min(1, 'sessionId cannot be empty') .describe('Optional session identifier to reset; omit to reset the active session') .optional(), @@ - sessionId: z - .string() + sessionId: z + .string() + .min(1, 'sessionId cannot be empty') .describe('Optional session identifier used to personalize the greeting') .optional(), @@ - sessionId: z.string().describe('Required session identifier'), + sessionId: z.string().min(1, 'sessionId is required').describe('Required session identifier'),Also applies to: 106-110, 215-219, 225-226
151-176
: Consider coercing boolean query params.Clients typically send strings; normalizing to booleans reduces downstream parsing.
Apply this diff (if compatible with consumers):
- hasKey: z - .enum(['true', 'false']) + hasKey: z + .coerce.boolean() .describe('Limit results based on whether an API key is configured') .optional(), @@ - defaultOnly: z - .enum(['true', 'false']) + defaultOnly: z + .coerce.boolean() .describe('When true, include only provider default models') .optional(),Note: This changes inferred types; verify call sites.
packages/client-sdk/src/types.ts (1)
99-107
: CatalogProvider shape looks good; consider readonly arrays.Public API benefit: prevents accidental mutation by consumers.
Apply this diff:
export interface CatalogProvider { name: string; hasApiKey: boolean; primaryEnvVar: string; - supportedRouters: LLMRouter[]; + supportedRouters: ReadonlyArray<LLMRouter>; supportsBaseURL: boolean; - models: CatalogModel[]; - supportedFileTypes?: SupportedFileType[]; + models: ReadonlyArray<CatalogModel>; + supportedFileTypes?: ReadonlyArray<SupportedFileType>; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
package.json
(1 hunks)packages/client-sdk/package.json
(1 hunks)packages/client-sdk/src/client.ts
(2 hunks)packages/client-sdk/src/index.ts
(1 hunks)packages/client-sdk/src/types.ts
(4 hunks)packages/client-sdk/src/validation.ts
(1 hunks)packages/webui/app/api/llm/catalog/route.ts
(1 hunks)packages/webui/app/api/llm/switch/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/[serverId]/route.ts
(1 hunks)packages/webui/app/api/mcp/servers/route.ts
(1 hunks)packages/webui/app/api/message-sync/route.ts
(1 hunks)packages/webui/app/api/message/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/history/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/load/route.ts
(1 hunks)packages/webui/app/api/sessions/[sessionId]/route.ts
(1 hunks)packages/webui/app/api/sessions/route.ts
(1 hunks)packages/webui/components/ErrorBanner.tsx
(1 hunks)packages/webui/lib/validation.ts
(1 hunks)scripts/copy-webui-dist.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/webui/app/api/sessions/[sessionId]/route.ts
- packages/webui/app/api/sessions/[sessionId]/load/route.ts
- packages/webui/app/api/message-sync/route.ts
- packages/webui/app/api/mcp/servers/route.ts
- packages/webui/lib/validation.ts
- packages/webui/app/api/sessions/route.ts
- packages/webui/app/api/llm/catalog/route.ts
- packages/webui/app/api/message/route.ts
- packages/webui/app/api/mcp/servers/[serverId]/route.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/file_extension.mdc)
For any new imports, ensure the import ends with .js for it to work
Files:
packages/client-sdk/src/index.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/client-sdk/src/validation.ts
packages/webui/components/ErrorBanner.tsx
scripts/copy-webui-dist.ts
packages/webui/app/api/llm/switch/route.ts
packages/client-sdk/src/client.ts
packages/client-sdk/src/types.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}
: Use template literals with ${} for variables in logger calls; do not use comma-separated variables
Do not concatenate strings in logs (avoid '...'+var); use template literals instead
No trailing commas in logger parameter lists
Maintain consistent logger parameter order: message first, then context (e.g., null), then color
Error logging: prefer messages likeOperation failed: ${error.message}
or including the error in a template literal
Include contextual info in error logs, e.g.,Error in ${functionName}: ${error.message}
Info logs with colors should pass color as third argument (e.g., 'cyan', 'green', 'yellow') and message as first argument
Use appropriate colors: green=success, red=errors, yellow=warnings, cyan/cyanBright=status, blue=progress, magenta=special, gray=secondary
For debug logs, serialize objects with JSON.stringify and include call context (e.g., function name and args)
**/*.{ts,tsx}
: Zod schemas for configuration objects must call .strict()
Prefer discriminatedUnion over union for Zod schema unions
Use .describe() on every Zod schema field
Provide sensible defaults via .default() in Zod schemas
Use superRefine for complex cross-field validation in Zod
Prefer direct imports internally; avoid deep re-export chains
Avoid wildcard exports; use explicit named exports
Use strict null safety; handle null/undefined explicitly
Use type guards and provide proper error messages when handling errors
Avoid any; use specific types. In tests, prefer @ts-expect-error over as any for invalid input cases
Files:
packages/client-sdk/src/index.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/client-sdk/src/validation.ts
packages/webui/components/ErrorBanner.tsx
scripts/copy-webui-dist.ts
packages/webui/app/api/llm/switch/route.ts
packages/client-sdk/src/client.ts
packages/client-sdk/src/types.ts
**/*.{ts,js,tsx,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,js,tsx,mjs,cjs}
: Use Result<T,C> helpers (ok, fail, hasErrors, splitIssues, zodToIssues) from packages/core/src/utils/result.js instead of ad-hoc result handling
Do not throw plain Error; use DextoRuntimeError or DextoValidationError via module-specific error factories
Files:
packages/client-sdk/src/index.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/client-sdk/src/validation.ts
packages/webui/components/ErrorBanner.tsx
scripts/copy-webui-dist.ts
packages/webui/app/api/llm/switch/route.ts
packages/client-sdk/src/client.ts
packages/client-sdk/src/types.ts
**/*.{ts,tsx,js,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
All import specifiers must end with .js for ESM compatibility
Files:
packages/client-sdk/src/index.ts
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/client-sdk/src/validation.ts
packages/webui/components/ErrorBanner.tsx
scripts/copy-webui-dist.ts
packages/webui/app/api/llm/switch/route.ts
packages/client-sdk/src/client.ts
packages/client-sdk/src/types.ts
**/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/index.ts
: Only create index.ts at logical module boundaries that represent cohesive public APIs
Avoid mega barrels; if an index.ts exports >20 symbols or pulls from >10 files, split into thematic sub-barrels
Files:
packages/client-sdk/src/index.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/**/*.ts : Validate API request bodies with Zod at the API boundary (defensive validation) before calling DextoAgent
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/index.ts : Only create index.ts at logical module boundaries that represent cohesive public APIs
Applied to files:
packages/client-sdk/src/index.ts
📚 Learning: 2025-09-12T09:29:43.238Z
Learnt from: shaunak99
PR: truffle-ai/dexto#345
File: packages/client-sdk/src/types.ts:7-7
Timestamp: 2025-09-12T09:29:43.238Z
Learning: The .js extension requirement for ESM compatibility applies to relative imports (./file.js, ../file.js) and internal project imports, but NOT to package imports (e.g., 'dexto/core', 'zod'). Package imports rely on package.json entry point resolution.
Applied to files:
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/switch/route.ts
📚 Learning: 2025-08-27T05:18:32.379Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#300
File: src/app/webui/components/MessageList.tsx:19-19
Timestamp: 2025-08-27T05:18:32.379Z
Learning: ESM import rule (.js extension requirement) does not apply to files in the `src/app/webui` directory - webui has different import conventions than the rest of the codebase.
Applied to files:
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/switch/route.ts
📚 Learning: 2025-09-22T16:01:15.709Z
Learnt from: shaunak99
PR: truffle-ai/dexto#363
File: packages/webui/app/api/greeting/route.ts:3-3
Timestamp: 2025-09-22T16:01:15.709Z
Learning: Do not suggest adding .js extensions to import statements in NextJS projects unless there's a specific technical reason (e.g., when importing from a pure ESM package that explicitly requires it).
Applied to files:
packages/webui/app/api/sessions/[sessionId]/history/route.ts
packages/webui/components/ErrorBanner.tsx
packages/webui/app/api/llm/switch/route.ts
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/**/*.ts : Validate API request bodies with Zod at the API boundary (defensive validation) before calling DextoAgent
Applied to files:
packages/client-sdk/src/validation.ts
packages/client-sdk/package.json
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,js,tsx,mjs,cjs} : Do not throw plain Error; use DextoRuntimeError or DextoValidationError via module-specific error factories
Applied to files:
packages/webui/components/ErrorBanner.tsx
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to **/*.{ts,tsx} : Use type guards and provide proper error messages when handling errors
Applied to files:
packages/webui/components/ErrorBanner.tsx
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Run pnpm run build, pnpm test, pnpm run lint, and pnpm run typecheck before completing any task
Applied to files:
package.json
📚 Learning: 2025-09-01T13:27:48.346Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#307
File: src/app/webui/app/layout.tsx:35-41
Timestamp: 2025-09-01T13:27:48.346Z
Learning: In Next.js apps within subdirectories like src/app/webui, static files are served from the local public directory (src/app/webui/public/) relative to where the Next.js app is configured, not from the repository root public/ directory.
Applied to files:
scripts/copy-webui-dist.ts
📚 Learning: 2025-09-11T11:56:25.329Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#322
File: packages/core/tsconfig.json:8-11
Timestamp: 2025-09-11T11:56:25.329Z
Learning: In the Dexto monorepo, packages/core uses tsup with bundle: true for building, which automatically resolves TypeScript path aliases like "core/*" during the bundling process, making them safe to use unlike plain tsc compilation.
Applied to files:
packages/client-sdk/package.json
📚 Learning: 2025-09-11T12:14:01.516Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#322
File: packages/core/package.json:48-52
Timestamp: 2025-09-11T12:14:01.516Z
Learning: In the Dexto monorepo, packages/core uses tsup with bundle: true for building, which automatically resolves TypeScript path aliases like "core/*" during the bundling process, making them safe to use unlike plain tsc compilation.
Applied to files:
packages/client-sdk/package.json
📚 Learning: 2025-08-25T15:34:53.807Z
Learnt from: rahulkarajgikar
PR: truffle-ai/dexto#298
File: src/core/llm/formatters/vercel.ts:206-229
Timestamp: 2025-08-25T15:34:53.807Z
Learning: The Vercel AI SDK v5 ToolResultPart.output uses `value` as the property name for all type variants (text, json, error-text, error-json, content), not separate property names like `text`, `json`, `content`. The correct type structure is:
```typescript
const output: {
type: "text";
value: string;
} | {
type: "json";
value: JSONValue;
} | {
type: "error-text";
value: string;
} | {
type: "error-json";
value: JSONValue;
}
```
Applied to files:
packages/client-sdk/src/types.ts
🧬 Code graph analysis (4)
packages/webui/app/api/sessions/[sessionId]/history/route.ts (3)
packages/webui/app/api/sessions/[sessionId]/route.ts (1)
GET
(5-32)packages/client-sdk/src/client.ts (1)
DextoClient
(44-435)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/client-sdk/src/validation.ts (1)
packages/webui/lib/validation.ts (21)
MessageRequestSchema
(7-7)z
(17-17)LLMSwitchRequestSchema
(8-8)SessionCreateRequestSchema
(9-9)ResetRequestSchema
(10-10)McpServerRequestSchema
(11-11)CatalogQuerySchema
(12-12)SearchMessagesQuerySchema
(13-13)SearchSessionsQuerySchema
(14-14)GreetingQuerySchema
(15-15)SessionIdQuerySchema
(16-16)CatalogQuery
(18-18)MessageRequest
(19-19)LLMSwitchRequest
(20-20)SessionCreateRequest
(21-21)ResetRequest
(22-22)McpServerRequest
(23-23)SearchMessagesQuery
(24-24)SearchSessionsQuery
(25-25)GreetingQuery
(26-26)SessionIdQuery
(27-27)
packages/webui/app/api/llm/switch/route.ts (3)
packages/client-sdk/src/validation.ts (1)
LLMSwitchRequestSchema
(58-92)packages/webui/types.ts (1)
LLMConfig
(85-101)packages/webui/lib/api-error.ts (2)
resolveStatus
(12-30)resolveMessage
(35-48)
packages/client-sdk/src/types.ts (4)
packages/webui/components/ModelPicker/types.ts (3)
CatalogProvider
(6-6)LLMRouter
(3-3)SupportedFileType
(4-4)packages/webui/lib/types.ts (2)
LLMRouter
(24-24)SupportedFileType
(25-25)packages/webui/components/hooks/useChat.ts (1)
LLMRouter
(15-15)packages/core/src/index.browser.ts (2)
LLMRouter
(17-17)SupportedFileType
(38-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-test
🔇 Additional comments (10)
packages/webui/components/ErrorBanner.tsx (1)
5-6
: Type-only imports look correct; leaving off .js is appropriate for Next.js here.Good switch to
import type
for bothErrorMessage
andIssue
. The omission of.js
extensions on these imports is correct given our Next.js setup and recent guidance.packages/client-sdk/src/index.ts (1)
1-5
: LGTM! Clean barrel export pattern.The addition of the validation module export is appropriate and follows the established pattern. This aligns well with the PR objective of migrating the WebUI to use the ClientSDK.
package.json (1)
21-21
: Good fix for the BUILD_STANDALONE environment variable placement.The environment variable is now correctly applied to the WebUI build step using cross-env for cross-platform compatibility, addressing the issue from the previous review.
packages/client-sdk/src/client.ts (1)
148-148
: Field rename fromcontent
tomessage
is consistent.The renaming of the message field aligns with the MessageInput type changes and maintains consistency between HTTP and WebSocket message formats.
Also applies to: 170-170
packages/webui/app/api/sessions/[sessionId]/history/route.ts (1)
5-32
: LGTM! Clean implementation following established patterns.The route handler follows the same patterns as other session routes with proper validation, error handling, and client initialization. The implementation correctly validates sessionId, handles errors with centralized utilities, and returns the expected response shape.
scripts/copy-webui-dist.ts (1)
91-173
: Server startup script looks robust with good fallback handling.The updated server.js generation properly handles ES modules, includes robust Next.js resolution with fallback paths, and has proper signal forwarding. The improvements from the previous review have been addressed.
packages/client-sdk/src/types.ts (2)
81-86
: Rename to message aligns with API – LGTM.Matches MessageRequestSchema and WebUI expectations.
50-61
: Core type re-exports – LGTM.Surface is explicit (no wildcard), aligns with guidelines.
packages/client-sdk/src/validation.ts (2)
58-70
: Router/fileType literal sets: confirmed match with core.
packages/core/src/llm/types.ts exports LLM_ROUTERS = ['vercel','in-built'] and SUPPORTED_FILE_TYPES = ['pdf','image','audio']; the literals in packages/client-sdk/src/validation.ts align — no change required.
178-201
: Coerce numeric query params and add safe defaults — verify callersReplace string/regex validation for limit/offset with coercion, ints, bounds and defaults (apply diff):
- limit: z - .string() - .regex(/^\d+$/, 'Limit must be a number') - .describe('Maximum number of results to return') - .optional(), - offset: z - .string() - .regex(/^\d+$/, 'Offset must be a number') - .describe('Number of results to skip for pagination') - .optional(), + limit: z + .coerce.number() + .int() + .positive() + .max(1000) + .default(50) + .describe('Maximum number of results to return'), + offset: z + .coerce.number() + .int() + .min(0) + .default(0) + .describe('Number of results to skip for pagination'),Automated consumer scan failed: ripgrep returned "unrecognized file type: tsx" so call-site verification was not completed — confirm callers (packages/webui and other consumers) accept numeric limit/offset or update them. Suggested local check:
rg -n -C2 --glob '**/*.{ts,tsx,js,jsx}' '\b(limit|offset)\b' packages/webui packages/client-sdk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
packages/client-sdk/src/validation.ts (5)
20-23
: Defaultstream
to false for ergonomic, predictable behavior.Avoids consumers needing to coalesce undefined. Keeps typing consistent.
- stream: z - .boolean() - .describe('Request a streaming response instead of a single payload') - .optional(), + stream: z + .boolean() + .describe('Request a streaming response instead of a single payload') + .default(false) + .optional(),
116-145
: Use a discriminated union forconfig
and add.describe()
on all fields.Removes custom refinement, tightens guarantees, and satisfies the “prefer discriminatedUnion” guideline. Also add sensible defaults for optional arrays/records.
- config: z - .object({ - type: z.enum(['stdio', 'sse', 'http']), - command: z.string().optional(), - args: z.array(z.string()).optional(), - url: z.string().url().optional(), - env: z.record(z.string()).optional(), - }) - .strict() - .superRefine((cfg, ctx) => { - if (cfg.type === 'stdio') { - if (!cfg.command) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'command is required for stdio', - path: ['command'], - }); - } - } else if (cfg.type === 'sse' || cfg.type === 'http') { - if (!cfg.url) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'url is required for sse/http', - path: ['url'], - }); - } - } - }), + config: z.discriminatedUnion('type', [ + z + .object({ + type: z.literal('stdio').describe('Standard I/O server'), + command: z.string().min(1, 'command is required for stdio').describe('Executable to launch the server'), + args: z.array(z.string()).describe('Arguments for the stdio command').default([]), + env: z.record(z.string()).describe('Environment variables for the stdio server').default({}), + }) + .strict(), + z + .object({ + type: z.literal('sse').describe('Server-Sent Events server'), + url: z.string().url().describe('Base URL for the SSE server'), + env: z.record(z.string()).describe('Environment variables for the SSE server').default({}), + }) + .strict(), + z + .object({ + type: z.literal('http').describe('HTTP server'), + url: z.string().url().describe('Base URL for the HTTP server'), + env: z.record(z.string()).describe('Environment variables for the HTTP server').default({}), + }) + .strict(), + ]),Also add
.describe()
onname
:- name: z.string().min(1, 'Server name is required'), + name: z.string().min(1, 'Server name is required').describe('Unique server name'),
154-169
: Parse boolean-ish query params into booleans.Return typed booleans for
hasKey
anddefaultOnly
to simplify callers.- hasKey: z - .enum(['true', 'false']) - .describe('Limit results based on whether an API key is configured') - .optional(), + hasKey: z + .enum(['true', 'false']) + .transform((v) => v === 'true') + .describe('Limit results based on whether an API key is configured') + .optional(), @@ - defaultOnly: z - .enum(['true', 'false']) - .describe('When true, include only provider default models') - .optional(), + defaultOnly: z + .enum(['true', 'false']) + .transform((v) => v === 'true') + .describe('When true, include only provider default models') + .optional(),Optionally default
mode
:- mode: z + mode: z .enum(['grouped', 'flat']) - .describe('Formatting of the response payload (grouped by provider or flat list)') + .describe('Formatting of the response payload (grouped by provider or flat list)') + .default('grouped') .optional(),
181-190
: Parse numeric query params and provide sensible defaults.Return numbers for
limit
/offset
and set defaults for stable paging.- limit: z - .string() - .regex(/^\d+$/, 'Limit must be a number') - .describe('Maximum number of results to return') - .optional(), + limit: z + .string() + .regex(/^\d+$/, 'Limit must be a number') + .transform((v) => parseInt(v, 10)) + .describe('Maximum number of results to return') + .default('50') + .optional(), - offset: z - .string() - .regex(/^\d+$/, 'Offset must be a number') - .describe('Number of results to skip for pagination') - .optional(), + offset: z + .string() + .regex(/^\d+$/, 'Offset must be a number') + .transform((v) => parseInt(v, 10)) + .describe('Number of results to skip for pagination') + .default('0') + .optional(),
225-226
: Enforce non-emptysessionId
.Prevent empty-string session identifiers.
- sessionId: z.string().describe('Required session identifier'), + sessionId: z.string().min(1, 'sessionId is required').describe('Required session identifier'),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.changeset/dark-keys-notice.md
(1 hunks)packages/client-sdk/src/validation.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/file_extension.mdc)
For any new imports, ensure the import ends with .js for it to work
Files:
packages/client-sdk/src/validation.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}
: Use template literals with ${} for variables in logger calls; do not use comma-separated variables
Do not concatenate strings in logs (avoid '...'+var); use template literals instead
No trailing commas in logger parameter lists
Maintain consistent logger parameter order: message first, then context (e.g., null), then color
Error logging: prefer messages likeOperation failed: ${error.message}
or including the error in a template literal
Include contextual info in error logs, e.g.,Error in ${functionName}: ${error.message}
Info logs with colors should pass color as third argument (e.g., 'cyan', 'green', 'yellow') and message as first argument
Use appropriate colors: green=success, red=errors, yellow=warnings, cyan/cyanBright=status, blue=progress, magenta=special, gray=secondary
For debug logs, serialize objects with JSON.stringify and include call context (e.g., function name and args)
**/*.{ts,tsx}
: Zod schemas for configuration objects must call .strict()
Prefer discriminatedUnion over union for Zod schema unions
Use .describe() on every Zod schema field
Provide sensible defaults via .default() in Zod schemas
Use superRefine for complex cross-field validation in Zod
Prefer direct imports internally; avoid deep re-export chains
Avoid wildcard exports; use explicit named exports
Use strict null safety; handle null/undefined explicitly
Use type guards and provide proper error messages when handling errors
Avoid any; use specific types. In tests, prefer @ts-expect-error over as any for invalid input cases
Files:
packages/client-sdk/src/validation.ts
**/*.{ts,js,tsx,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,js,tsx,mjs,cjs}
: Use Result<T,C> helpers (ok, fail, hasErrors, splitIssues, zodToIssues) from packages/core/src/utils/result.js instead of ad-hoc result handling
Do not throw plain Error; use DextoRuntimeError or DextoValidationError via module-specific error factories
Files:
packages/client-sdk/src/validation.ts
**/*.{ts,tsx,js,mjs,cjs}
📄 CodeRabbit inference engine (CLAUDE.md)
All import specifiers must end with .js for ESM compatibility
Files:
packages/client-sdk/src/validation.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/**/*.ts : Validate API request bodies with Zod at the API boundary (defensive validation) before calling DextoAgent
📚 Learning: 2025-09-11T13:26:31.111Z
Learnt from: CR
PR: truffle-ai/dexto#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-11T13:26:31.111Z
Learning: Applies to packages/cli/src/api/**/*.ts : Validate API request bodies with Zod at the API boundary (defensive validation) before calling DextoAgent
Applied to files:
packages/client-sdk/src/validation.ts
.changeset/dark-keys-notice.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-test
🔇 Additional comments (3)
.changeset/dark-keys-notice.md (1)
1-10
: Confirm semver level (patch vs minor) for public API/type surface changes.If any externally-consumed types or request field names changed (e.g., schema/type field renames), this may warrant a minor bump instead of a patch. Please confirm.
packages/client-sdk/src/validation.ts (2)
1-4
: Overall: solid Zod boundary with strict objects and descriptive messages.Schemas are .strict(), fields well-described, and types exported for consumers. Nice work.
66-71
: baseUrl alignment LGTM — repo still containsbaseURL
occurrences; standardize naming.Nice fix aligning schema to
baseUrl
. FoundbaseURL
still used; update tobaseUrl
(or document deliberate exceptions) in:
- packages/webui/types.ts
- packages/webui/components/hooks/ChatContext.tsx
- packages/webui/components/ModelPicker/ModelPickerModal.tsx
- packages/core/src/llm/schemas.ts
- packages/core/src/llm/services/factory.ts
- packages/core/src/llm/services/vercel.ts
- packages/core/src/llm/registry.ts
- docs/** and tests in packages/core/src/llm/*.test.ts
Summary by CodeRabbit
New Features
Documentation
Refactor
Chores