Skip to content

Conversation

shaunak99
Copy link
Collaborator

@shaunak99 shaunak99 commented Sep 22, 2025

Summary by CodeRabbit

  • New Features

    • Many new Web UI API endpoints: chat (send/reset/sync), sessions (create/list/get/delete/load/current/history), LLM (providers/catalog/current/switch), MCP servers (list/connect/delete/tools/list/execute), search (messages/sessions), config.yaml proxy, and greeting.
    • Improved production startup fallback: falls back to Next.js CLI when standalone server is unavailable.
  • Documentation

    • Developer setup updated: separate API and UI startup steps (ports 3001/3000).
  • Refactor

    • WebUI now uses a shared client SDK for types, validation, and centralized error handling.
  • Chores

    • Package rename, script and dependency updates (added zod), lint config adjustments.

Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

This 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

Cohort / File(s) Summary of changes
Root build script
package.json
Update build:webui to run build:core and build:client-sdk before building the WebUI (uses cross-env BUILD_STANDALONE=true).
Client SDK (types, validation, exports)
packages/client-sdk/src/types.ts, packages/client-sdk/src/validation.ts, packages/client-sdk/src/index.ts, packages/client-sdk/package.json
Re-export core types (TextPart, ImagePart, FilePart, ToolResult, ToolCall, Issue, server configs); change CatalogProvider alias → explicit interface; rename MessageInput.contentmessage; add Zod validation schemas and re-export them; add zod dependency; expose validation via index.
WebUI package metadata & config
packages/webui/package.json, packages/webui/tsconfig.json, packages/webui/next.config.ts, packages/webui/eslint.config.mjs, packages/webui/README.md
Rename package, adjust version and scripts, switch dev deps from @dexto/core@dexto/client-sdk, add zod, update lint guidance and developer startup instructions.
WebUI API routes (many new)
packages/webui/app/api/... (examples)
config.yaml/route.ts, connect-server/route.ts, greeting/route.ts, llm/*, mcp/*, message*.ts, reset/route.ts, search/*, sessions/*
Add numerous Next.js GET/POST/DELETE handlers that: build DextoClient (env-driven baseUrl, optional apiKey, ws disabled), validate inputs via re-exported Zod schemas, call backend methods, and return JSON/YAML with unified error mapping via resolveStatus/resolveMessage.
WebUI libs & error helpers
packages/webui/lib/api-error.ts, packages/webui/lib/validation.ts
Add error utilities (resolveStatus, resolveMessage, errorHasCode, toAppError) and a validation re-export surface that proxies SDK Zod schemas/types for WebUI consumption.
WebUI types surface
packages/webui/lib/types.ts, packages/webui/types.ts
Re-export SDK types for browser-safe surface; add AudioPart and ContentPart, alias ProviderCatalog/CatalogResponse to SDK types, and re-export ToolResult/ToolCall from client-sdk.
Components: type source & safety
packages/webui/components/* (e.g., ChatApp.tsx, ConnectServerModal.tsx, ErrorBanner.tsx, ServersPanel.tsx)
Switch type imports from @dexto/core@dexto/client-sdk; tighten typings (sendMessage args, server entries), improve error handling for non-Error values, and add minor UI flow tweaks (post-send positioning/following streaming).
Model Picker changes
packages/webui/components/ModelPicker/* (constants.tsx, types.ts, ModelPickerModal.tsx, ProviderSection.tsx)
Introduce LLMProviderID, move router/provider/catalog types to client-sdk, update logo/pricing mappings and typings, remove local catalog/model types in favor of SDK re-exports, and expand current LLM config shape.
Hooks & public type re-exports
packages/webui/components/hooks/useChat.ts
Re-export shared types from @dexto/client-sdk and replace toError usage with toAppError.
Playground page
packages/webui/app/playground/page.tsx
Add explicit import React from 'react' (no behavior change).
Build packaging script
scripts/copy-webui-dist.ts
Change copy strategy to include full .next, generate an ES module server.js, add fallback to start embedded Next CLI when standalone missing, forward signals to child, and improve logging and error handling.
Small edits
Various
Minor comment, import, ESLint rule updates across files to prefer @dexto/client-sdk and align types.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • rahulkarajgikar

Poem

I thump my paws, new routes take flight,
Types hop toward SDK daylight.
Zod checks paths and errors mend,
Build scripts hum and servers attend.
A rabbit's cheer — code stitched just right. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Migrate WebUI to use the ClientSDK" is a concise, single-sentence summary that accurately reflects the primary intent of the changeset: switching WebUI imports, types, validation, and package dependencies from @dexto/core to @dexto/client-sdk. It clearly signals the main migration focus without unnecessary detail or noise. Reviewers scanning history will understand the core change from this title.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 in issue.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 locally

rg 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-level error.detailedIssues when no nested context.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: Avoid any 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 construction

Client 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 SDK

Comment 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

📥 Commits

Reviewing files that changed from the base of the PR and between 09b8e33 and aab6f32.

⛔ 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 like Operation 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

Copy link

@coderabbitai coderabbitai bot left a 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...

Copy link

@coderabbitai coderabbitai bot left a 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 use baseURL — this causes a type/validation mismatch.

  • Change ClientConfig.baseUrlbaseURL (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 use baseURL everywhere (also update packages/client-sdk/src/validation.ts to expect baseURL).
🧹 Nitpick comments (12)
packages/webui/components/ErrorBanner.tsx (2)

21-26: Use a type guard to simplify/narrow context 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

📥 Commits

Reviewing files that changed from the base of the PR and between aab6f32 and 5ca2ff6.

⛔ 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 like Operation 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 both ErrorMessage and Issue. 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 from content to message 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 callers

Replace 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

Copy link

@coderabbitai coderabbitai bot left a 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: Default stream 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 for config 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() on name:

-        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 and defaultOnly 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-empty sessionId.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5ca2ff6 and 9beab77.

📒 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 like Operation 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 contains baseURL occurrences; standardize naming.

Nice fix aligning schema to baseUrl. Found baseURL still used; update to baseUrl (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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant