Skip to content

feat: add customer support agent #88

Open
Kumar-s75 wants to merge 1 commit intoLamatic:mainfrom
Kumar-s75:feat/support-triage
Open

feat: add customer support agent #88
Kumar-s75 wants to merge 1 commit intoLamatic:mainfrom
Kumar-s75:feat/support-triage

Conversation

@Kumar-s75
Copy link
Copy Markdown

@Kumar-s75 Kumar-s75 commented Mar 22, 2026

Support Triage Agent

Problem Statement

Support teams lose time manually reviewing incoming tickets, judging urgency, spotting repeated incidents, and deciding who should handle each request. Raw support requests are often noisy, inconsistent, and hard to triage quickly, especially when business impact is unclear.

What this agent does

  • Accepts raw support ticket text plus customer tier, channel, timestamp, and past incident context
  • Classifies the issue into an operational support category
  • Assigns severity as low, medium, high, or critical
  • Flags possible duplicate incidents
  • Recommends the most likely owner/team
  • Identifies SLA risk
  • Generates a concise escalation summary for internal support teams

Tech Stack

  • Next.js + TypeScript + Tailwind CSS
  • One Lamatic Flow: support-triage
  • Deployable on Vercel

Checklist

  • Forked the repository
  • Unique problem statement (not existing in repo)
  • Lamatic account created
  • Kit follows the kits/ folder structure
  • Demo video (coming soon)

What’s included

  • New Support Triage Agent kit under kits/automation/support-triage
  • Lamatic-powered triage workflow that accepts:
    • ticket_text
    • customer_tier
    • channel
    • created_at
    • past_ticket_context
  • Structured output including:
    • category
    • severity
    • priority_reason
    • possible_duplicate
    • recommended_owner
    • sla_risk
    • escalation_summary

Implementation Details

  • Server logic:
    • actions/orchestrate.ts implements executeSupportTriage
    • Calls the Lamatic flow through the Lamatic SDK
    • Handles different Lamatic response shapes and normalizes booleans safely
  • Lamatic client:
    • lib/lamatic-client.ts provides env-driven Lamatic SDK setup
  • Flow configuration:
    • orchestrate.js defines the flow ID, schemas, and API config
    • exported Lamatic flow added under flows/support-triage
  • Frontend:
    • app/page.tsx provides a support ticket triage UI
    • Form includes ticket text, customer tier, channel, created-at, and past ticket context
    • Result panel displays structured triage output and raw JSON
  • Docs/config:
    • .env.example
    • config.json
    • README.md
    • flow README.md, meta.json, inputs.json, and config.json

Environment Variables

  • FLOW_SUPPORT_TRIAGE
  • LAMATIC_API_URL
  • LAMATIC_PROJECT_ID
  • LAMATIC_API_KEY

Notes

  • This submission is for the AgentKit challenge.
  • The problem statement is intentionally focused on support operations and ticket triage rather than chatbot-style

Support Triage Agent Kit

Core Feature:

  • New Support Triage Agent kit in kits/automation/support-triage/ for automated support ticket triage with structured JSON output (category, severity, priority_reason, possible_duplicate, recommended_owner, sla_risk, escalation_summary)

Backend:

  • Lamatic flow integration in flows/support-triage/ using LLM-based ticket analysis
  • Server action executeSupportTriage() for orchestrating flow execution
  • Environment-validated Lamatic client with automatic error handling

Frontend:

  • Next.js application with TypeScript and Tailwind CSS
  • Ticket triage form accepting: ticket_text, customer_tier, channel, created_at, past_ticket_context
  • Results panel displaying structured triage output and raw JSON
  • Complete Shadcn UI component library (75+ components: buttons, forms, dialogs, tables, sidebars, modals, etc.)

Configuration:

  • Kit metadata with OpenAI integration requirement
  • Required environment variables: FLOW_SUPPORT_TRIAGE, LAMATIC_API_URL, LAMATIC_PROJECT_ID, LAMATIC_API_KEY
  • TypeScript, Next.js, PostCSS configs
  • Full React/Tailwind/Radix UI dependency stack

Documentation:

  • README with setup instructions and example input/output
  • Flow metadata and schema definitions

Status:

  • All AgentKit challenge requirements met (repository forked, unique problem, Lamatic configured, proper kit layout)
  • Demo video pending

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

Added a complete Support Triage Agent kit featuring a Next.js application with server-side Lamatic flow orchestration, 60+ reusable Shadcn UI components, custom React hooks for state management, and comprehensive flow configuration for automated support ticket triage and classification.

Changes

Cohort / File(s) Summary
Project Configuration
package.json, tsconfig.json, next.config.mjs, postcss.config.mjs, .npmrc, .gitignore
Build, TypeScript, and Next.js configuration with dependencies for Tailwind, Radix UI, and form utilities.
Core Application
app/layout.tsx, app/globals.css, app/page.tsx, components/header.tsx, components/theme-provider.tsx
Root layout with metadata/fonts, global Oklch design token theming, main triage UI page with form submission and results display, header with branding, and theme provider wrapper.
Server Actions & Orchestration
actions/orchestrate.ts, lib/lamatic-client.ts, orchestrate.js
Server-side execution of Lamatic support-triage flow with input mapping, error handling, and environment validation; Lamatic client initialization with config.
Hooks & Utilities
hooks/use-toast.ts, hooks/use-mobile.ts, lib/utils.ts, components/ui/use-toast.ts, components/ui/use-mobile.tsx
Toast notification state management with reducer and listener registry; mobile breakpoint detection hook; class merging utility (clsx + tailwind-merge).
UI Components – Form & Layout
components/ui/form.tsx, components/ui/field.tsx, components/ui/input.tsx, components/ui/textarea.tsx, components/ui/select.tsx, components/ui/checkbox.tsx, components/ui/radio-group.tsx, components/ui/label.tsx, components/ui/input-group.tsx, components/ui/input-otp.tsx
React Hook Form integration, field layout primitives, text input/textarea, dropdown select, checkboxes, radio buttons, and OTP input controls.
UI Components – Containers & Sections
components/ui/card.tsx, components/ui/dialog.tsx, components/ui/drawer.tsx, components/ui/sheet.tsx, components/ui/popover.tsx, components/ui/alert-dialog.tsx, components/ui/hover-card.tsx, components/ui/sidebar.tsx
Card, modal dialog, side drawer, bottom/side sheet, popover, alert dialog, hover card, and responsive sidebar with collapsible/icon modes and keyboard shortcuts.
UI Components – Navigation & Menus
components/ui/breadcrumb.tsx, components/ui/pagination.tsx, components/ui/tabs.tsx, components/ui/dropdown-menu.tsx, components/ui/context-menu.tsx, components/ui/menubar.tsx, components/ui/navigation-menu.tsx
Breadcrumb, pagination, tab navigation, dropdown menus, context menus, menu bars, and navigation menu with nested structure support.
UI Components – Data & Visualization
components/ui/table.tsx, components/ui/carousel.tsx, components/ui/calendar.tsx, components/ui/chart.tsx
Table with scrollable wrapper, image carousel with Embla, date picker calendar, and Recharts-based data visualization with tooltips/legends.
UI Components – Feedback & Status
components/ui/toast.tsx, components/ui/toaster.tsx, components/ui/sonner.tsx, components/ui/skeleton.tsx, components/ui/progress.tsx, components/ui/alert.tsx, components/ui/empty.tsx
Toast notifications (Radix and Sonner), loading skeleton, progress bar, alert boxes, and empty state placeholders.
UI Components – Interactive Controls
components/ui/button.tsx, components/ui/button-group.tsx, components/ui/toggle.tsx, components/ui/toggle-group.tsx, components/ui/switch.tsx, components/ui/accordion.tsx, components/ui/collapsible.tsx, components/ui/command.tsx
Buttons with variants, button groups, toggle switches, toggle groups, accordions, collapsible sections, and command palette.
UI Components – Misc
components/ui/badge.tsx, components/ui/kbd.tsx, components/ui/item.tsx, components/ui/aspect-ratio.tsx, components/ui/avatar.tsx, components/ui/separator.tsx, components/ui/scroll-area.tsx, components/ui/resizable.tsx, components/ui/spinner.tsx
Badge tags, keyboard hints, list items, aspect ratio containers, user avatars, separators, scrollable areas, resizable panels, and loading spinner.
Flow Configuration & Documentation
config.json, flows/support-triage/config.json, flows/support-triage/inputs.json, flows/support-triage/meta.json, flows/support-triage/README.md, README.md
Kit configuration with metadata and flow definition; Lamatic workflow graph with trigger/LLM node/response; input schema for generative model; flow metadata and endpoint documentation.
Component Scaffolding
components.json
Shadcn UI configuration specifying schema, style, rendering mode, Tailwind integration, alias mappings, and icon set.
Documentation Fix
CHALLENGE.md
Typo correction in a single line (understand it quicklyunderstax nd it quickly).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • AgentKit #42 – Lamatic client initialization and environment variable validation patterns may overlap with this PR's lib/lamatic-client.ts setup.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Copy link
Copy Markdown
Contributor

@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: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (16)
kits/automation/support-triage/components/ui/button.tsx-39-56 (1)

39-56: ⚠️ Potential issue | 🟡 Minor

Set a safe default type="button" for native button rendering.

When asChild is false and type is omitted, the browser defaults to submit behavior, which can trigger unintended form submissions. Evidence in the codebase shows this concern is real—the reset button in support-triage/app/page.tsx explicitly sets type="button" to prevent submission.

🔧 Proposed fix
 function Button({
   className,
   variant,
   size,
   asChild = false,
   ...props
 }: React.ComponentProps<'button'> &
   VariantProps<typeof buttonVariants> & {
     asChild?: boolean
   }) {
   const Comp = asChild ? Slot : 'button'
 
   return (
     <Comp
       data-slot="button"
+      type={!asChild ? (props.type ?? 'button') : undefined}
       className={cn(buttonVariants({ variant, size, className }))}
       {...props}
     />
   )
 }
kits/automation/support-triage/components/ui/empty.tsx-71-82 (1)

71-82: ⚠️ Potential issue | 🟡 Minor

Type/element mismatch: EmptyDescription is typed as <p> but renders <div>.

The component accepts React.ComponentProps<'p'> but renders a <div>. This inconsistency could confuse consumers and cause subtle issues with paragraph-specific props.

🔧 Proposed fix

Either change the element to match the type:

 function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
   return (
-    <div
+    <p
       data-slot="empty-description"
       className={cn(
         'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
         className,
       )}
       {...props}
     />
   )
 }

Or update the type to match the element:

-function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
+function EmptyDescription({ className, ...props }: React.ComponentProps<'div'>) {
kits/automation/support-triage/components/ui/context-menu.tsx-128-130 (1)

128-130: ⚠️ Potential issue | 🟡 Minor

Replace invalid Tailwind selector *:[svg] with [&_svg].

The selector data-[variant=destructive]:*:[svg]:!text-destructive on line 129 uses invalid Tailwind syntax. The *:[svg] pattern is malformed. Use [&_svg] instead to target SVG descendants: data-[variant=destructive]:[&_svg]:!text-destructive.

This pattern appears across multiple UI component files and should be corrected consistently.

kits/automation/support-triage/components/ui/sheet.tsx-75-78 (1)

75-78: ⚠️ Potential issue | 🟡 Minor

Set explicit button type on close control.

SheetPrimitive.Close should set type="button" to avoid accidental form submissions when the sheet is used inside a form.

🔧 Proposed fix
-        <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
+        <SheetPrimitive.Close
+          type="button"
+          className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none"
+        >
           <XIcon className="size-4" />
           <span className="sr-only">Close</span>
         </SheetPrimitive.Close>
kits/automation/support-triage/components/ui/field.tsx-128-133 (1)

128-133: ⚠️ Potential issue | 🟡 Minor

FieldTitle has an incorrect data-slot value.

FieldTitle currently sets data-slot="field-label"; it should expose its own slot to avoid selector/test collisions.

🔧 Proposed fix
 function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
   return (
     <div
-      data-slot="field-label"
+      data-slot="field-title"
       className={cn(
         'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
         className,
       )}
       {...props}
     />
   )
 }
kits/automation/support-triage/components/ui/input-group.tsx-70-75 (1)

70-75: ⚠️ Potential issue | 🟡 Minor

Addon click-to-focus misses textarea controls.

On Line 74, focus targeting is limited to input, so grouped textarea flows won’t receive focus when clicking the addon.

🛠️ Proposed fix
-        e.currentTarget.parentElement?.querySelector('input')?.focus()
+        e.currentTarget.parentElement
+          ?.querySelector<HTMLElement>(
+            '[data-slot="input-group-control"], input, textarea',
+          )
+          ?.focus()
kits/automation/support-triage/components/ui/breadcrumb.tsx-52-61 (1)

52-61: ⚠️ Potential issue | 🟡 Minor

Tighten the breadcrumb accessibility semantics.

Line 56 exposes the current page as a disabled pseudo-link even though it's just a <span>, and Lines 90-96 hide the sr-only “More” text by placing it under aria-hidden. The current page should just be text with aria-current, and the ellipsis label needs to stay outside the hidden subtree.

💡 Proposed fix
 function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
   return (
     <span
       data-slot="breadcrumb-page"
-      role="link"
-      aria-disabled="true"
       aria-current="page"
       className={cn('text-foreground font-normal', className)}
       {...props}
     />
   )
 }
@@
     <span
       data-slot="breadcrumb-ellipsis"
       role="presentation"
-      aria-hidden="true"
       className={cn('flex size-9 items-center justify-center', className)}
       {...props}
     >
-      <MoreHorizontal className="size-4" />
+      <MoreHorizontal className="size-4" aria-hidden="true" />
       <span className="sr-only">More</span>
     </span>

Also applies to: 88-97

kits/automation/support-triage/components/ui/chart.tsx-235-238 (1)

235-238: ⚠️ Potential issue | 🟡 Minor

Zero values disappear from the tooltip.

item.value && skips rendering when the datapoint is 0, so valid zero counts/amounts look missing.

💡 Proposed fix
-                    {item.value && (
+                    {item.value !== undefined && item.value !== null && (
                       <span className="text-foreground font-mono font-medium tabular-nums">
                         {item.value.toLocaleString()}
                       </span>
                     )}
kits/automation/support-triage/components/ui/carousel.tsx-96-105 (1)

96-105: ⚠️ Potential issue | 🟡 Minor

Unsubscribe the reInit listener in cleanup too.

This effect registers both reInit and select listeners, but the cleanup function only removes select. Without cleaning up the reInit listener, additional callbacks will accumulate on the Embla API instance during remounts or in Strict Mode.

Proposed fix
    return () => {
+     api?.off('reInit', onSelect)
      api?.off('select', onSelect)
    }
kits/automation/support-triage/flows/support-triage/README.md-59-61 (1)

59-61: ⚠️ Potential issue | 🟡 Minor

Hardcoded absolute path will break for other users.

The path /home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/flows/support-triage/inputs.json is a local development path. Use a relative path instead.

📝 Proposed fix
-This flow requires one private model configuration, documented in [`inputs.json`](/home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/flows/support-triage/inputs.json).
+This flow requires one private model configuration, documented in [`inputs.json`](./inputs.json).
kits/automation/support-triage/orchestrate.js-34-35 (1)

34-35: ⚠️ Potential issue | 🟡 Minor

polling should be a boolean, not a string.

polling: "false" is a string value. If the consuming code uses strict equality (=== false), this check will fail. Use a boolean instead.

🔧 Proposed fix
       mode: "sync",
-      polling: "false",
+      polling: false,
kits/automation/support-triage/package.json-4-7 (1)

4-7: ⚠️ Potential issue | 🟡 Minor

Malformed repository URL.

The repository URL https://github.com/Lamatic/AgentKit/kits/automation/support-triage is not a valid GitHub repository URL. The path after the repo name should use a different format.

🔧 Proposed fix
   "repository": {
     "type": "git",
-    "url": "https://github.com/Lamatic/AgentKit/kits/automation/support-triage"
+    "url": "https://github.com/Lamatic/AgentKit",
+    "directory": "kits/automation/support-triage"
   },
kits/automation/support-triage/flows/support-triage/config.json-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor

Typo: "responeType" should be "responseType".

This may cause unexpected behavior if the Lamatic platform validates this field strictly.

🔧 Proposed fix
-          "responeType": "realtime",
+          "responseType": "realtime",
kits/automation/support-triage/README.md-77-79 (1)

77-79: ⚠️ Potential issue | 🟡 Minor

Hardcoded local filesystem paths in Repository Notes.

These absolute paths reference a local machine and should be converted to relative paths for portability.

🔧 Proposed fix
 ## Repository Notes

-- Main kit UI: [`app/page.tsx`](/home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/app/page.tsx)
-- Server action: [`actions/orchestrate.ts`](/home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/actions/orchestrate.ts)
-- Exported Lamatic flow: [`flows/support-triage/config.json`](/home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/flows/support-triage/config.json)
+- Main kit UI: [`app/page.tsx`](./app/page.tsx)
+- Server action: [`actions/orchestrate.ts`](./actions/orchestrate.ts)
+- Exported Lamatic flow: [`flows/support-triage/config.json`](./flows/support-triage/config.json)
kits/automation/support-triage/README.md-26-26 (1)

26-26: ⚠️ Potential issue | 🟡 Minor

Hardcoded local filesystem path should be relative.

The path /home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/flows/support-triage is a local filesystem path that won't work for other users. Use a relative path instead.

🔧 Proposed fix
-4. Export the flow into [`flows/support-triage`](/home/kumarsaurabh27d/lamatic/AgentKit/kits/automation/support-triage/flows/support-triage).
+4. Export the flow into [`flows/support-triage`](./flows/support-triage).
kits/automation/support-triage/components/ui/use-toast.ts-171-182 (1)

171-182: ⚠️ Potential issue | 🟡 Minor

Remove unnecessary dependency on state in useToast subscription.

The effect on line 174 depends on state, causing the listener to be unsubscribed and re-added on every toast update. Since setState from useState is stable, an empty dependency array prevents this needless churn.

Suggested fix
-  }, [state])
+  }, [])
🧹 Nitpick comments (11)
kits/automation/support-triage/components/ui/collapsible.tsx (1)

5-9: Consider adding forwardRef for ref access to underlying primitives.

The wrapper doesn't forward refs to the underlying CollapsiblePrimitive.Root. Consumers may need ref access for DOM measurements, focus management, or composition with other libraries. Radix UI primitives support ref forwarding via their ref prop.

♻️ Proposed refactor to add forwardRef
-function Collapsible({
+const Collapsible = React.forwardRef<
+  React.ElementRef<typeof CollapsiblePrimitive.Root>,
+  React.ComponentProps<typeof CollapsiblePrimitive.Root>
+>(({
   ...props
-}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
-  return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
-}
+}, ref) => {
+  return <CollapsiblePrimitive.Root ref={ref} data-slot="collapsible" {...props} />
+})
+Collapsible.displayName = "Collapsible"
kits/automation/support-triage/components/ui/sonner.tsx (2)

12-20: Merge className and style props instead of allowing override.

The current implementation spreads {...props} after setting default className and style, which allows consumers to accidentally override defaults. While no current usages pass these props, this makes the component fragile to future changes.

♻️ Proposed refactor
-const Toaster = ({ ...props }: ToasterProps) => {
+const Toaster = ({ className, style, ...props }: ToasterProps) => {
   const { theme = 'system' } = useTheme()
 
   return (
     <Sonner
       theme={theme as ToasterProps['theme']}
-      className="toaster group"
+      className={['toaster group', className].filter(Boolean).join(' ')}
       style={
         {
           '--normal-bg': 'var(--popover)',
           '--normal-text': 'var(--popover-foreground)',
           '--normal-border': 'var(--border)',
+          ...(style as React.CSSProperties),
         } as React.CSSProperties
       }
       {...props}
     />
   )
 }

11-11: Type assertion is safe but optional normalization would improve defensiveness.

The cast on line 11 works correctly because next-themes and Sonner's Toaster accept identical theme values (light | dark | system). However, if custom themes are ever configured in ThemeProvider, the type assertion would silently allow incompatible values to reach Sonner.

Adding theme normalization is optional but provides future-proofing:

♻️ Optional normalization
 const Toaster = ({ ...props }: ToasterProps) => {
   const { theme = 'system' } = useTheme()
+  const sonnerTheme: ToasterProps['theme'] =
+    theme === 'light' || theme === 'dark' || theme === 'system'
+      ? theme
+      : 'system'
 
   return (
     <Sonner
-      theme={theme as ToasterProps['theme']}
+      theme={sonnerTheme}
       className="toaster group"
kits/automation/support-triage/components/ui/use-mobile.tsx (1)

1-19: Consolidate duplicate useIsMobile hook implementations.

This hook duplicates kits/automation/support-triage/hooks/use-mobile.ts. Keep one canonical implementation to avoid drift.

♻️ Proposed simplification
-import * as React from 'react'
-
-const MOBILE_BREAKPOINT = 768
-
-export function useIsMobile() {
-  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
-
-  React.useEffect(() => {
-    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
-    const onChange = () => {
-      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
-    }
-    mql.addEventListener('change', onChange)
-    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
-    return () => mql.removeEventListener('change', onChange)
-  }, [])
-
-  return !!isMobile
-}
+export { useIsMobile } from '@/hooks/use-mobile'
kits/automation/support-triage/flows/support-triage/meta.json (1)

5-8: Empty placeholder fields detected.

The testInput, githubUrl, documentationUrl, and deployUrl fields are empty. Consider populating these before release, or remove them if not needed to avoid confusion.

Would you like me to open an issue to track populating these metadata fields?

kits/automation/support-triage/orchestrate.js (1)

7-7: Consider validating required environment variables.

If FLOW_SUPPORT_TRIAGE, LAMATIC_API_URL, LAMATIC_PROJECT_ID, or LAMATIC_API_KEY are not set, the config will contain undefined values, leading to confusing runtime errors. Consider adding validation or at least documenting the required variables.

🛡️ Example validation approach
const requiredEnvVars = [
  'FLOW_SUPPORT_TRIAGE',
  'LAMATIC_API_URL', 
  'LAMATIC_PROJECT_ID',
  'LAMATIC_API_KEY'
];

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    console.warn(`Warning: ${envVar} is not set`);
  }
}

Also applies to: 38-42

kits/automation/support-triage/flows/support-triage/inputs.json (1)

12-20: Empty credential fields in default configuration.

The provider_name and credential_name fields are empty strings. Ensure the documentation clearly indicates that users must configure these values, or consider removing them from the default if they're always required to be user-provided.

kits/automation/support-triage/app/layout.tsx (2)

6-7: Font variables are loaded but never applied.

_geist and _geistMono are loaded from Google Fonts but their CSS variable classes are not applied to any element. This adds to the font payload without providing any benefit. Either apply the font variables or remove the unused imports.

♻️ Proposed fix to apply font variables
-const _geist = Geist({ subsets: ["latin"] });
-const _geistMono = Geist_Mono({ subsets: ["latin"] });
+const geist = Geist({ subsets: ["latin"], variable: "--font-geist-sans" });
+const geistMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" });

Then in the body:

-      <body className={`font-sans antialiased`}>
+      <body className={`${geist.variable} ${geistMono.variable} font-sans antialiased`}>

9-12: Update metadata to reflect kit branding.

The title and description are generic v0 boilerplate. Consider updating them to match the Support Triage Agent kit identity.

♻️ Suggested metadata update
 export const metadata: Metadata = {
-  title: 'v0 App',
-  description: 'Created with v0',
-  generator: 'v0.app',
+  title: 'Support Triage Agent',
+  description: 'A Lamatic-powered kit that triages incoming support tickets',
kits/automation/support-triage/config.json (1)

23-27: Empty URL placeholders may need attention before release.

The demoUrl, githubUrl, deployUrl, documentationUrl, and imageUrl fields are empty. If these are displayed in a kit gallery or documentation, empty strings may cause broken links or UI issues. Consider either populating these values or removing the keys if optional.

kits/automation/support-triage/lib/lamatic-client.ts (1)

4-20: The validation and client initialization are actually aligned.

The config object is imported from ../orchestrate.js where config.api.* values are assigned directly from the same process.env.* variables being validated (lines 4-14). Since config is initialized at import time, the validation ensures these environment variables are set before the Lamatic client is instantiated.

However, the fallback values (?? "" for endpoint/apiKey and ?? null for projectId) are unnecessary and obscure failures. If the validation passes, the config values will be defined. Remove the fallbacks and validate config.api directly instead:

🔧 Suggested improvement
-if (!process.env.LAMATIC_API_URL || !process.env.LAMATIC_PROJECT_ID || !process.env.LAMATIC_API_KEY) {
+if (!config.api.endpoint || !config.api.projectId || !config.api.apiKey) {
   throw new Error(
-    "All API Credentials in environment variable are not set. Please add it to your .env.local file."
+    "Lamatic API Credentials are not fully set. Please ensure LAMATIC_API_URL, LAMATIC_PROJECT_ID, and LAMATIC_API_KEY are configured."
   )
 }

 export const lamaticClient = new Lamatic({
-  endpoint: config.api.endpoint ?? "",
-  projectId: config.api.projectId ?? null,
-  apiKey: config.api.apiKey ?? "",
+  endpoint: config.api.endpoint,
+  projectId: config.api.projectId,
+  apiKey: config.api.apiKey,
 })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6b687b3-d8e3-4eec-bab6-5801761e418f

📥 Commits

Reviewing files that changed from the base of the PR and between e1ceb47 and c7d1636.

⛔ Files ignored due to path filters (2)
  • kits/automation/support-triage/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (82)
  • CHALLENGE.md
  • kits/automation/support-triage/.gitignore
  • kits/automation/support-triage/.npmrc
  • kits/automation/support-triage/README.md
  • kits/automation/support-triage/actions/orchestrate.ts
  • kits/automation/support-triage/app/globals.css
  • kits/automation/support-triage/app/layout.tsx
  • kits/automation/support-triage/app/page.tsx
  • kits/automation/support-triage/components.json
  • kits/automation/support-triage/components/header.tsx
  • kits/automation/support-triage/components/theme-provider.tsx
  • kits/automation/support-triage/components/ui/accordion.tsx
  • kits/automation/support-triage/components/ui/alert-dialog.tsx
  • kits/automation/support-triage/components/ui/alert.tsx
  • kits/automation/support-triage/components/ui/aspect-ratio.tsx
  • kits/automation/support-triage/components/ui/avatar.tsx
  • kits/automation/support-triage/components/ui/badge.tsx
  • kits/automation/support-triage/components/ui/breadcrumb.tsx
  • kits/automation/support-triage/components/ui/button-group.tsx
  • kits/automation/support-triage/components/ui/button.tsx
  • kits/automation/support-triage/components/ui/calendar.tsx
  • kits/automation/support-triage/components/ui/card.tsx
  • kits/automation/support-triage/components/ui/carousel.tsx
  • kits/automation/support-triage/components/ui/chart.tsx
  • kits/automation/support-triage/components/ui/checkbox.tsx
  • kits/automation/support-triage/components/ui/collapsible.tsx
  • kits/automation/support-triage/components/ui/command.tsx
  • kits/automation/support-triage/components/ui/context-menu.tsx
  • kits/automation/support-triage/components/ui/dialog.tsx
  • kits/automation/support-triage/components/ui/drawer.tsx
  • kits/automation/support-triage/components/ui/dropdown-menu.tsx
  • kits/automation/support-triage/components/ui/empty.tsx
  • kits/automation/support-triage/components/ui/field.tsx
  • kits/automation/support-triage/components/ui/form.tsx
  • kits/automation/support-triage/components/ui/hover-card.tsx
  • kits/automation/support-triage/components/ui/input-group.tsx
  • kits/automation/support-triage/components/ui/input-otp.tsx
  • kits/automation/support-triage/components/ui/input.tsx
  • kits/automation/support-triage/components/ui/item.tsx
  • kits/automation/support-triage/components/ui/kbd.tsx
  • kits/automation/support-triage/components/ui/label.tsx
  • kits/automation/support-triage/components/ui/menubar.tsx
  • kits/automation/support-triage/components/ui/navigation-menu.tsx
  • kits/automation/support-triage/components/ui/pagination.tsx
  • kits/automation/support-triage/components/ui/popover.tsx
  • kits/automation/support-triage/components/ui/progress.tsx
  • kits/automation/support-triage/components/ui/radio-group.tsx
  • kits/automation/support-triage/components/ui/resizable.tsx
  • kits/automation/support-triage/components/ui/scroll-area.tsx
  • kits/automation/support-triage/components/ui/select.tsx
  • kits/automation/support-triage/components/ui/separator.tsx
  • kits/automation/support-triage/components/ui/sheet.tsx
  • kits/automation/support-triage/components/ui/sidebar.tsx
  • kits/automation/support-triage/components/ui/skeleton.tsx
  • kits/automation/support-triage/components/ui/slider.tsx
  • kits/automation/support-triage/components/ui/sonner.tsx
  • kits/automation/support-triage/components/ui/spinner.tsx
  • kits/automation/support-triage/components/ui/switch.tsx
  • kits/automation/support-triage/components/ui/table.tsx
  • kits/automation/support-triage/components/ui/tabs.tsx
  • kits/automation/support-triage/components/ui/textarea.tsx
  • kits/automation/support-triage/components/ui/toast.tsx
  • kits/automation/support-triage/components/ui/toaster.tsx
  • kits/automation/support-triage/components/ui/toggle-group.tsx
  • kits/automation/support-triage/components/ui/toggle.tsx
  • kits/automation/support-triage/components/ui/tooltip.tsx
  • kits/automation/support-triage/components/ui/use-mobile.tsx
  • kits/automation/support-triage/components/ui/use-toast.ts
  • kits/automation/support-triage/config.json
  • kits/automation/support-triage/flows/support-triage/README.md
  • kits/automation/support-triage/flows/support-triage/config.json
  • kits/automation/support-triage/flows/support-triage/inputs.json
  • kits/automation/support-triage/flows/support-triage/meta.json
  • kits/automation/support-triage/hooks/use-mobile.ts
  • kits/automation/support-triage/hooks/use-toast.ts
  • kits/automation/support-triage/lib/lamatic-client.ts
  • kits/automation/support-triage/lib/utils.ts
  • kits/automation/support-triage/next.config.mjs
  • kits/automation/support-triage/orchestrate.js
  • kits/automation/support-triage/package.json
  • kits/automation/support-triage/postcss.config.mjs
  • kits/automation/support-triage/tsconfig.json

Candidates are asked to choose a **unique problem statement**, build a practical contribution, and share it through a pull request and a short walkthrough video.

The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understand it quickly.
The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understax nd it quickly.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix typo in documentation.

The phrase "understax nd it quickly" contains a typo and should be "understand it quickly". This makes the documentation unprofessional and harder to read.

Additionally, this file modification appears unrelated to the PR's stated purpose of adding a Support Triage Agent kit. Please verify whether this change was intentional.

📝 Proposed fix
-The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understax nd it quickly.
+The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understand it quickly.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understax nd it quickly.
The goal is not to make the biggest project possible. The goal is to make something focused, useful, and well explained. A strong submission shows good judgment, clear thinking, and enough polish for someone else to understand it quickly.
🧰 Tools
🪛 LanguageTool

[grammar] ~15-~15: Ensure spelling is correct
Context: ..., and enough polish for someone else to understax nd it quickly. ## ✅ What a strong submiss...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

Comment on lines +25 to +28
# local env files
.env*.local
.env

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden env-file ignores to prevent accidental secret commits

Line 25–28 currently miss variants like .env.production / .env.development. That can leak credentials if those files are used. Prefer ignoring all .env* and explicitly allow only .env.example.

Proposed fix
-# local env files
-.env*.local
-.env
+# local env files
+.env*
+!.env.example
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# local env files
.env*.local
.env
# local env files
.env*
!.env.example

@@ -0,0 +1 @@
force=true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Committed .npmrc files under support-triage:"
fd '^\.npmrc$' kits/automation/support-triage -x sh -c 'echo "--- $1"; cat "$1"' sh {}

echo
echo "Other explicit force usage under the kit:"
rg -n --hidden --glob '!**/node_modules/**' '\bforce\s*=\s*true\b|\bnpm\b.*\s--force\b' kits/automation/support-triage || true

Repository: Lamatic/AgentKit

Length of output: 329


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for .npmrc files under support-triage (including hidden files):"
fd -H '\.npmrc$' kits/automation/support-triage -x sh -c 'echo "File: $1"; cat "$1"' sh {} || echo "No .npmrc found"

echo
echo "Checking for npm force-related configurations:"
rg -H -n 'force\s*=\s*true|npm.*--force' kits/automation/support-triage --glob '!**/node_modules/**' || echo "No force configurations found"

Repository: Lamatic/AgentKit

Length of output: 266


🌐 Web query:

npm force parameter .npmrc behavior what does force=true do

💡 Result:

The force=true parameter in an .npmrc file (or --force CLI flag, or npm_config_force=true environment variable) sets npm's force configuration to true. This removes various protections against side effects, mistakes, performance issues, and malicious input. Specific behaviors include: - Allows unpublishing of entire packages (not just single versions). - Allows clobbering existing values in npm pkg set. - Implicitly sets --yes during npm init. - Allows installing packages with conflicting peerDependencies in the root project. - Allows npm audit fix to install modules outside stated dependency ranges (including SemVer-major changes). - Overrides engine-strict to allow installing packages requiring different Node.js versions. - Lifecycle script failures do not block progress. - Publishing clobbers previously published versions. - Skips cache when requesting from the registry. - Prevents checks against clobbering non-npm files. It can be set in project .npmrc, user ~/.npmrc, global config, or via CLI/env. Using force=true triggers a warning "Recommended protections disabled" on many commands, which can be suppressed with loglevel=error in .npmrc. Default is false; use cautiously as it bypasses safety checks.

Citations:


Remove force=true from the .npmrc file.

This project-level setting applies to all npm invocations in the kit and bypasses critical safety checks. Specifically, it allows installations with conflicting peerDependencies and overrides engine-strict, masking dependency incompatibilities that should fail fast. Keep --force as an explicit one-off CLI override when truly needed instead of a checked-in default.

Proposed fix
-force=true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
force=true

Comment on lines +21 to +31
function parseBoolean(value: unknown) {
if (typeof value === "boolean") {
return value
}

if (typeof value === "string") {
return value.toLowerCase() === "true"
}

return false
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reject invalid workflow output instead of silently coercing it.

Lines 21-31 turn anything other than exact true into false, and Lines 67-73 accept any severity string. That hides schema drift as a successful triage result and can under-prioritize tickets. Validate the booleans and enforce the low|medium|high|critical contract before returning success: true.

Suggested fix
-function parseBoolean(value: unknown) {
+function parseBoolean(value: unknown): boolean | null {
   if (typeof value === "boolean") {
     return value
   }
 
   if (typeof value === "string") {
-    return value.toLowerCase() === "true"
+    const normalized = value.trim().toLowerCase()
+    if (normalized === "true") return true
+    if (normalized === "false") return false
   }
 
-  return false
+  return null
 }
@@
-    return {
+    const severity = String(result.severity ?? "").toLowerCase()
+    const possibleDuplicate = parseBoolean(result.possible_duplicate)
+    const slaRisk = parseBoolean(result.sla_risk)
+
+    if (
+      !["low", "medium", "high", "critical"].includes(severity) ||
+      possibleDuplicate === null ||
+      slaRisk === null
+    ) {
+      throw new Error("Workflow returned an invalid triage payload.")
+    }
+
+    return {
       success: true,
       result: {
         category: String(result.category ?? ""),
-        severity: String(result.severity ?? ""),
+        severity,
         priority_reason: String(result.priority_reason ?? ""),
-        possible_duplicate: parseBoolean(result.possible_duplicate),
+        possible_duplicate: possibleDuplicate,
         recommended_owner: String(result.recommended_owner ?? ""),
-        sla_risk: parseBoolean(result.sla_risk),
+        sla_risk: slaRisk,
         escalation_summary: String(result.escalation_summary ?? ""),
       },
     }

Also applies to: 64-74

Comment on lines +60 to +61
if (!result) {
throw new Error(`No result returned from workflow. Response shape: ${JSON.stringify(response)}`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep raw Lamatic payloads out of returned errors.

Line 61 embeds the whole workflow response in the thrown error, and Lines 76-90 return that message to the browser on the generic path. If Lamatic echoes ticket text or internal metadata, this leaks it directly into the UI. Throw a generic message and log only sanitized diagnostics server-side.

Suggested fix
-    if (!result) {
-      throw new Error(`No result returned from workflow. Response shape: ${JSON.stringify(response)}`)
-    }
+    if (!result) {
+      console.error("Support triage workflow returned no result", {
+        workflowId: supportTriageFlow.workflowId,
+        hasResponse: Boolean(response),
+        hasRawResult: Boolean(rawResult),
+      })
+      throw new Error("Support triage workflow returned no result.")
+    }

Also applies to: 76-90

Comment on lines +264 to +275
<Button
data-sidebar="trigger"
data-slot="sidebar-trigger"
variant="ghost"
size="icon"
className={cn('size-7', className)}
onClick={(event) => {
onClick?.(event)
toggleSidebar()
}}
{...props}
>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

What is the default type of an HTML button inside a form when the type attribute is omitted?

💡 Result:

The default type of an HTML <button> element inside a form, when the type attribute is omitted, is "submit". This causes the button to submit the form when activated.

Citations:


🏁 Script executed:

fd sidebar.tsx --type f

Repository: Lamatic/AgentKit

Length of output: 511


🏁 Script executed:

wc -l kits/automation/support-triage/components/ui/sidebar.tsx

Repository: Lamatic/AgentKit

Length of output: 120


🏁 Script executed:

sed -n '264,275p; 286,303p; 425,436p; 515,522p; 560,576p' kits/automation/support-triage/components/ui/sidebar.tsx | cat -n

Repository: Lamatic/AgentKit

Length of output: 3707


🏁 Script executed:

sed -n '250,280p' kits/automation/support-triage/components/ui/sidebar.tsx | cat -n

Repository: Lamatic/AgentKit

Length of output: 842


🏁 Script executed:

sed -n '276,310p' kits/automation/support-triage/components/ui/sidebar.tsx | cat -n

Repository: Lamatic/AgentKit

Length of output: 1696


🏁 Script executed:

sed -n '415,440p; 505,530p; 550,580p' kits/automation/support-triage/components/ui/sidebar.tsx | cat -n

Repository: Lamatic/AgentKit

Length of output: 3400


Default these sidebar controls to type="button".

Lines 264-275, 286-303, 425-436, 515-522, and 560-576 render button-backed controls without an explicit type. Inside a surrounding form, they default to submit, so clicking a trigger or menu action can submit the form unexpectedly. Set type="button" whenever asChild is false.

For SidebarTrigger, accept a type parameter and pass type={type ?? 'button'} to the Button component. Apply the same pattern to SidebarRail. For SidebarGroupAction, SidebarMenuButton, and SidebarMenuAction, add a conditional type="button" prop when asChild is false.

Comment on lines +77 to +87
<ToastPrimitives.Close
ref={ref}
className={cn(
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
className,
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add an accessible label to ToastClose.

The close button is icon-only and currently has no accessible name. Add an aria-label and/or sr-only text so screen readers can identify the action.

♿ Proposed fix
   <ToastPrimitives.Close
     ref={ref}
+    aria-label="Close notification"
     className={cn(
       'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
       className,
     )}
     toast-close=""
     {...props}
   >
     <X className="h-4 w-4" />
+    <span className="sr-only">Close</span>
   </ToastPrimitives.Close>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ToastPrimitives.Close
ref={ref}
className={cn(
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
className,
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
<ToastPrimitives.Close
ref={ref}
aria-label="Close notification"
className={cn(
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
className,
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</ToastPrimitives.Close>

"values": {
"id": "InstructorLLMNode_833",
"tools": [],
"schema": "{\n \"type\": \"object\",\n \"properties\": {\n \"category\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"severity\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"priority_reason\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"possible_duplicate\": {\n \"type\": \"boolean\",\n \"required\": true\n },\n \"recommended_owner\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"sla_risk\": {\n \"type\": \"boolean\",\n \"required\": true\n },\n \"escalation_summary\": {\n \"type\": \"string\",\n \"required\": true\n }\n }\n}",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Lamatic InstructorLLMNode schema format

💡 Result:

The InstructorLLMNode in Lamatic.ai is a node type for generating structured JSON output from LLMs using predefined schemas, similar to the Generate JSON Node. It appears to integrate Instructor library concepts for reliable structured extraction. Key schema format from Lamatic docs (Generate JSON Node low-code example): nodes: - nodeId: InstructorLLMNode_774 nodeType: InstructorLLMNode nodeName: JSON Generator values: schema: |- { "type": "object", "properties": { "output": { "type": "string" } } } promptTemplate: tell me something about ${{triggerNode_1.output.topic}} attachments: "[]" messages: "[]" generativeModelName: provider_name: mistral type: generator/text credential_name: Mistral API credentialId: 32bf5e3b-a8fc-4697-b95a-b1af3dcf7498 model_name: mistral/mistral-large-2402 needs: - triggerNode_1 Configuration parameters: - schema: JSON Schema (Zod JSON format) defining output structure - promptTemplate/user prompt: Instructions for LLM - systemPrompt: Guides LLM behavior - generativeModelName: Model selection with provider, credential, model_name - messages: Conversation history array - attachments: Optional files Output includes _meta with token usage (prompt_tokens, completion_tokens, total_tokens) and custom schema fields. This node processes input to produce validated JSON matching the schema, useful for consistent data formatting, API responses, analytics.

Citations:


🌐 Web query:

Zod JSON schema format required property syntax

💡 Result:

In Zod, when generating JSON Schema from a Zod schema using z.toJSONSchema, required properties in the resulting JSON Schema use the standard "required" array syntax at the object level. Example from official Zod v4 docs: const schema = z.object({ name: z.string, age: z.number, }); z.toJSONSchema(schema) // => { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, required: [ 'name', 'age' ], additionalProperties: false, } By default, all properties defined in z.object without .optional are required, and they appear in the "required" array. Conversely, to create a Zod schema from JSON Schema with required properties: const jsonSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "number" }, }, required: ["name", "age"], }; const zodSchema = z.fromJSONSchema(jsonSchema); This matches standard JSON Schema syntax where "required" is an array of property names that must be present. In Zod schema definition, properties are required by default (no .optional or .nullable). To make specific previously-optional properties required on an object schema, use the .required method: const Recipe = z.object({ title: z.string, description: z.string.optional, ingredients: z.array(z.string), }); const RequiredRecipe = Recipe.required; // All required // or const RecipeRequiredDescription = Recipe.required({ description: true }); Zod's JSON Schema output correctly reflects this with the "required" array containing non-optional field names.

Citations:


🌐 Web query:

Lamatic InstructorLLMNode Zod schema validation format

💡 Result:

The InstructorLLMNode in Lamatic.ai is a specialized node (nodeType: "InstructorLLMNode") for generating structured JSON outputs from LLMs using schema validation. It leverages the Instructor library (which uses Zod for TypeScript or equivalent JSON schema validation) to enforce structured responses. Key configuration from official Lamatic docs: - nodeId: e.g., "InstructorLLMNode_774" - nodeType: "InstructorLLMNode" - nodeName: e.g., "JSON Generator" - values.schema: JSON schema string for output structure and validation, e.g.: { "type": "object", "properties": { "output": { "type": "string" } } } - values.promptTemplate: User prompt, e.g., "tell me something about ${{triggerNode_1.output.topic}}" - values.generativeModelName: LLM config, e.g., Mistral model with credential - needs: Upstream nodes, e.g., ["triggerNode_1"] This node converts natural language inputs into validated JSON matching the schema, useful for consistent data formatting in AI workflows. Lamatic also mentions a Zod Schema Builder for intuitive schema creation. Zod is a TypeScript-first schema validation library. Schemas are defined like: import { z } from "zod"; const schema = z.object({ output: z.string }); Instructor patches LLM clients to return parsed, validated data per the Zod schema. For full Lamatic docs: https://lamatic.ai/docs/nodes/ai/generate-json-node

Citations:


Fix JSON Schema format: move required to object level.

The schema uses non-standard syntax with "required": true inside each property, but Zod (which Lamatic's InstructorLLMNode uses for validation) expects standard JSON Schema with required as an array of property names at the object level. This schema will fail validation at runtime.

Correct format:

{
  "type": "object",
  "properties": {
    "category": { "type": "string" },
    "severity": { "type": "string" },
    "priority_reason": { "type": "string" },
    "possible_duplicate": { "type": "boolean" },
    "recommended_owner": { "type": "string" },
    "sla_risk": { "type": "boolean" },
    "escalation_summary": { "type": "string" }
  },
  "required": ["category", "severity", "priority_reason", "possible_duplicate", "recommended_owner", "sla_risk", "escalation_summary"]
}

Comment on lines +1 to +191
'use client'

// Inspired by react-hot-toast library
import * as React from 'react'

import type { ToastActionElement, ToastProps } from '@/components/ui/toast'

const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000

type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}

const actionTypes = {
ADD_TOAST: 'ADD_TOAST',
UPDATE_TOAST: 'UPDATE_TOAST',
DISMISS_TOAST: 'DISMISS_TOAST',
REMOVE_TOAST: 'REMOVE_TOAST',
} as const

let count = 0

function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}

type ActionType = typeof actionTypes

type Action =
| {
type: ActionType['ADD_TOAST']
toast: ToasterToast
}
| {
type: ActionType['UPDATE_TOAST']
toast: Partial<ToasterToast>
}
| {
type: ActionType['DISMISS_TOAST']
toastId?: ToasterToast['id']
}
| {
type: ActionType['REMOVE_TOAST']
toastId?: ToasterToast['id']
}

interface State {
toasts: ToasterToast[]
}

const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()

const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
}

const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: 'REMOVE_TOAST',
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)

toastTimeouts.set(toastId, timeout)
}

export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'ADD_TOAST':
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}

case 'UPDATE_TOAST':
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
),
}

case 'DISMISS_TOAST': {
const { toastId } = action

// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}

return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t,
),
}
}
case 'REMOVE_TOAST':
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
}

const listeners: Array<(state: State) => void> = []

let memoryState: State = { toasts: [] }

function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}

type Toast = Omit<ToasterToast, 'id'>

function toast({ ...props }: Toast) {
const id = genId()

const update = (props: ToasterToast) =>
dispatch({
type: 'UPDATE_TOAST',
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })

dispatch({
type: 'ADD_TOAST',
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})

return {
id: id,
dismiss,
update,
}
}

function useToast() {
const [state, setState] = React.useState<State>(memoryState)

React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])

return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
}
}

export { useToast, toast }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use one toast store for the whole kit.

This file duplicates kits/automation/support-triage/components/ui/use-toast.ts. Because each module owns its own memoryState, listeners, and toastTimeouts, a toast() imported from here will not be seen by a <Toaster> subscribed to the other path. Re-export the canonical store instead of keeping two copies.

Minimal replacement
'use client'

export { toast, useToast } from '@/components/ui/use-toast'

Comment on lines +3 to +5
typescript: {
ignoreBuildErrors: true,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid suppressing TypeScript build errors.

Setting ignoreBuildErrors: true allows the build to succeed even when TypeScript detects type errors. This undermines type safety and can mask real bugs that would otherwise be caught at build time. If there are existing type errors, they should be fixed rather than silenced.

🔧 Proposed fix
-  typescript: {
-    ignoreBuildErrors: true,
-  },

If there are legitimate type errors preventing the build, address them directly or add targeted // @ts-expect-error`` comments with explanations for specific edge cases.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
typescript: {
ignoreBuildErrors: true,
},

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants