This commit is contained in:
Siddharth Ganesan
2026-03-13 11:39:58 -07:00
parent 33bb01bccb
commit f33cf83fc5
6 changed files with 80 additions and 53 deletions

View File

@@ -31,19 +31,9 @@ function getContextIcon(ctx: ChatContext) {
switch (ctx.kind) {
case 'workflow':
case 'current_workflow':
return (
<WorkflowPillIcon
workflowId={ctx.workflowId}
className='mr-[4px] h-[10px] w-[10px]'
/>
)
return <WorkflowPillIcon workflowId={ctx.workflowId} className='mr-[4px] h-[10px] w-[10px]' />
case 'workflow_block':
return (
<WorkflowPillIcon
workflowId={ctx.workflowId}
className='mr-[4px] h-[10px] w-[10px]'
/>
)
return <WorkflowPillIcon workflowId={ctx.workflowId} className='mr-[4px] h-[10px] w-[10px]' />
case 'knowledge':
return <Database className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
case 'templates':

View File

@@ -32,7 +32,17 @@ type WindowWithSpeech = Window & {
}
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ArrowUp, AtSign, ChevronRight, Folder, Loader2, Mic, Paperclip, Plus, X } from 'lucide-react'
import {
ArrowUp,
AtSign,
ChevronRight,
Folder,
Loader2,
Mic,
Paperclip,
Plus,
X,
} from 'lucide-react'
import { useParams } from 'next/navigation'
import { createPortal } from 'react-dom'
import {
@@ -66,7 +76,15 @@ import {
import { useSession } from '@/lib/auth/auth-client'
import { cn } from '@/lib/core/utils/cn'
import { CHAT_ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
import { ContextPills } from './components'
import {
type AvailableItem,
useAvailableResources,
} from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
import { getResourceConfig } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
import type {
MothershipResource,
MothershipResourceType,
} from '@/app/workspace/[workspaceId]/home/types'
import {
useCaretViewport,
useContextManagement,
@@ -78,17 +96,12 @@ import {
computeMentionHighlightRanges,
extractContextTokens,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/utils'
import type { ChatContext } from '@/stores/panel'
import {
useAvailableResources,
type AvailableItem,
} from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
import { getResourceConfig } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
import type { MothershipResource, MothershipResourceType } from '@/app/workspace/[workspaceId]/home/types'
import { useFolders } from '@/hooks/queries/folders'
import { useFolderStore } from '@/stores/folders/store'
import type { FolderTreeNode } from '@/stores/folders/types'
import type { ChatContext } from '@/stores/panel'
import { useAnimatedPlaceholder } from '../../hooks'
import { ContextPills } from './components'
const TEXTAREA_BASE_CLASSES = cn(
'm-0 box-border h-auto min-h-[24px] w-full resize-none',
@@ -184,9 +197,7 @@ function ResourceMentionMenu({
)
}
// When no query, show all items flat
return availableResources.flatMap(({ type, items }) =>
items.map((item) => ({ type, item }))
)
return availableResources.flatMap(({ type, items }) => items.map((item) => ({ type, item })))
}, [availableResources, query])
// Reset active index when query changes
@@ -263,7 +274,9 @@ function ResourceMentionMenu({
onClick={() => handleSelect({ type, id: item.id, title: item.name })}
className={cn(
'flex cursor-pointer items-center gap-[8px] px-[8px] py-[6px] text-[13px]',
index === activeIndex ? 'bg-[var(--surface-active)]' : 'hover:bg-[var(--surface-active)]'
index === activeIndex
? 'bg-[var(--surface-active)]'
: 'hover:bg-[var(--surface-active)]'
)}
>
{config.renderDropdownItem({ item })}
@@ -292,7 +305,13 @@ interface ResourceTypeFolderProps {
onSelect: (resource: MothershipResource) => void
}
function ResourceTypeFolder({ type, items, config, workspaceId, onSelect }: ResourceTypeFolderProps) {
function ResourceTypeFolder({
type,
items,
config,
workspaceId,
onSelect,
}: ResourceTypeFolderProps) {
const [expanded, setExpanded] = useState(false)
const Icon = config.icon
@@ -578,7 +597,11 @@ export interface FileAttachmentForApi {
interface UserInputProps {
defaultValue?: string
onSubmit: (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => void
onSubmit: (
text: string,
fileAttachments?: FileAttachmentForApi[],
contexts?: ChatContext[]
) => void
isSending: boolean
onStopGeneration: () => void
isInitialView?: boolean
@@ -608,7 +631,11 @@ export function UserInput({
const animatedPlaceholder = useAnimatedPlaceholder(isInitialView)
const placeholder = isInitialView ? animatedPlaceholder : 'Send message to Sim'
const files = useFileAttachments({ userId: userId || session?.user?.id, disabled: false, isLoading: isSending })
const files = useFileAttachments({
userId: userId || session?.user?.id,
disabled: false,
isLoading: isSending,
})
const hasFiles = files.attachedFiles.some((f) => !f.uploading && f.key)
const contextManagement = useContextManagement({ message: value })
@@ -709,10 +736,13 @@ export function UserInput({
}
}, [isInitialView, textareaRef])
const handleContainerClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
if ((e.target as HTMLElement).closest('button')) return
textareaRef.current?.focus()
}, [textareaRef])
const handleContainerClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
if ((e.target as HTMLElement).closest('button')) return
textareaRef.current?.focus()
},
[textareaRef]
)
const handleSubmit = useCallback(() => {
const fileAttachmentsForApi: FileAttachmentForApi[] = files.attachedFiles
@@ -866,7 +896,6 @@ export function UserInput({
[isInitialView]
)
const toggleListening = useCallback(() => {
if (isListening) {
recognitionRef.current?.stop()
@@ -1090,7 +1119,9 @@ export function UserInput({
onClose={() => {
mentionMenu.closeMentionMenu()
}}
query={mentionMenu.getActiveMentionQueryAtPosition(mentionMenu.getCaretPos())?.query ?? ''}
query={
mentionMenu.getActiveMentionQueryAtPosition(mentionMenu.getCaretPos())?.query ?? ''
}
/>,
document.body
)}

View File

@@ -1,8 +1,7 @@
'use client'
import { cn } from '@/lib/core/utils/cn'
import type { ChatMessageContext } from '../types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import type { ChatMessageContext } from '../types'
interface UserMessageContentProps {
content: string
@@ -48,15 +47,10 @@ function MentionHighlight({ context, token }: { context: ChatMessageContext; tok
return null
})
const bgColor = workflowColor
? `${workflowColor}40`
: 'rgba(50, 189, 126, 0.4)'
const bgColor = workflowColor ? `${workflowColor}40` : 'rgba(50, 189, 126, 0.4)'
return (
<span
className='rounded-[4px] py-[1px] px-[2px]'
style={{ backgroundColor: bgColor }}
>
<span className='rounded-[4px] px-[2px] py-[1px]' style={{ backgroundColor: bgColor }}>
{token}
</span>
)

View File

@@ -15,11 +15,17 @@ import {
} from '@/lib/core/utils/browser-storage'
import { persistImportedWorkflow } from '@/lib/workflows/operations/import-export'
import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks'
import { MessageContent, MothershipView, TemplatePrompts, UserInput, UserMessageContent } from './components'
import type { FileAttachmentForApi } from './components/user-input/user-input'
import type { ChatContext } from '@/stores/panel'
import type { MothershipResource, MothershipResourceType } from './types'
import {
MessageContent,
MothershipView,
TemplatePrompts,
UserInput,
UserMessageContent,
} from './components'
import type { FileAttachmentForApi } from './components/user-input/user-input'
import { useAutoScroll, useChat } from './hooks'
import type { MothershipResource, MothershipResourceType } from './types'
const logger = createLogger('Home')
@@ -245,7 +251,7 @@ export function Home({ chatId }: HomeProps = {}) {
(context: ChatContext) => {
let resourceType: MothershipResourceType | null = null
let resourceId: string | null = null
let resourceTitle: string = context.label
const resourceTitle: string = context.label
switch (context.kind) {
case 'workflow':

View File

@@ -20,11 +20,11 @@ import {
taskKeys,
useChatHistory,
} from '@/hooks/queries/tasks'
import type { ChatContext } from '@/stores/panel'
import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
import { useExecutionStream } from '@/hooks/use-execution-stream'
import { useExecutionStore } from '@/stores/execution/store'
import { useFolderStore } from '@/stores/folders/store'
import type { ChatContext } from '@/stores/panel'
import { useTerminalConsoleStore } from '@/stores/terminal'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { invalidateResourceQueries } from '../components/mothership-view/components/resource-registry'
@@ -46,7 +46,11 @@ export interface UseChatReturn {
isSending: boolean
error: string | null
resolvedChatId: string | undefined
sendMessage: (message: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => Promise<void>
sendMessage: (
message: string,
fileAttachments?: FileAttachmentForApi[],
contexts?: ChatContext[]
) => Promise<void>
stopGeneration: () => Promise<void>
resources: MothershipResource[]
activeResourceId: string | null

View File

@@ -3,7 +3,7 @@ import { knowledgeConnector } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import { generateInternalToken } from '@/lib/auth/internal'
import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool'
import type { BaseServerTool, ServerToolContext } from '@/lib/copilot/tools/server/base-tool'
import type { KnowledgeBaseArgs, KnowledgeBaseResult } from '@/lib/copilot/tools/shared/schemas'
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
import {
@@ -40,7 +40,7 @@ export const knowledgeBaseServerTool: BaseServerTool<KnowledgeBaseArgs, Knowledg
name: 'knowledge_base',
async execute(
params: KnowledgeBaseArgs,
context?: { userId: string }
context?: ServerToolContext
): Promise<KnowledgeBaseResult> {
if (!context?.userId) {
logger.error('Unauthorized attempt to access knowledge base - no authenticated user context')
@@ -48,6 +48,8 @@ export const knowledgeBaseServerTool: BaseServerTool<KnowledgeBaseArgs, Knowledg
}
const { operation, args = {} } = params
const workspaceId =
context.workspaceId || ((args as Record<string, unknown>).workspaceId as string | undefined)
try {
switch (operation) {
@@ -59,7 +61,7 @@ export const knowledgeBaseServerTool: BaseServerTool<KnowledgeBaseArgs, Knowledg
}
}
if (!args.workspaceId) {
if (!workspaceId) {
return {
success: false,
message: 'Workspace ID is required for creating a knowledge base',
@@ -71,7 +73,7 @@ export const knowledgeBaseServerTool: BaseServerTool<KnowledgeBaseArgs, Knowledg
{
name: args.name,
description: args.description,
workspaceId: args.workspaceId,
workspaceId,
userId: context.userId,
embeddingModel: 'text-embedding-3-small',
embeddingDimension: 1536,