Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
),

table: ({ children }: React.TableHTMLAttributes<HTMLTableElement>) => (
<div className='my-4 max-w-full overflow-x-auto'>
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-sm'>
<div className='my-3 max-w-full overflow-x-auto'>
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-xs'>
{children}
</table>
</div>
Expand All @@ -346,12 +346,12 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
</tr>
),
th: ({ children }: React.ThHTMLAttributes<HTMLTableCellElement>) => (
<th className='border-[var(--border-1)] border-r px-4 py-2 align-top font-base text-[var(--text-secondary)] last:border-r-0 dark:font-[470]'>
<th className='border-[var(--border-1)] border-r px-2.5 py-1.5 align-top font-base text-[var(--text-secondary)] last:border-r-0 dark:font-[470]'>
{children}
</th>
),
td: ({ children }: React.TdHTMLAttributes<HTMLTableCellElement>) => (
<td className='break-words border-[var(--border-1)] border-r px-4 py-2 align-top font-base text-[var(--text-primary)] last:border-r-0 dark:font-[470]'>
<td className='break-words border-[var(--border-1)] border-r px-2.5 py-1.5 align-top font-base text-[var(--text-primary)] last:border-r-0 dark:font-[470]'>
{children}
</td>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export function ThinkingBlock({
)}
>
{/* Render markdown during streaming with thinking text styling */}
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-none [&_*]:!m-0 [&_*]:!p-0 [&_*]:!mb-0 [&_*]:!mt-0 [&_p]:!m-0 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_code]:!text-[11px] [&_ul]:!pl-4 [&_ul]:!my-0 [&_ol]:!pl-4 [&_ol]:!my-0 [&_li]:!my-0 [&_li]:!py-0 [&_br]:!leading-[0.5] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)] leading-none'>
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-[1.3] [&_p]:!m-0 [&_p]:!mb-1 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h1]:!m-0 [&_h1]:!mb-1 [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h2]:!m-0 [&_h2]:!mb-1 [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_h3]:!m-0 [&_h3]:!mb-1 [&_code]:!text-[11px] [&_ul]:!pl-5 [&_ul]:!my-1 [&_ol]:!pl-6 [&_ol]:!my-1 [&_li]:!my-0.5 [&_li]:!py-0 [&_br]:!leading-[0.5] [&_table]:!my-2 [&_th]:!px-2 [&_th]:!py-1 [&_th]:!text-[11px] [&_td]:!px-2 [&_td]:!py-1 [&_td]:!text-[11px] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)]'>
<CopilotMarkdownRenderer content={content} />
<span className='ml-1 inline-block h-2 w-1 animate-pulse bg-[var(--text-muted)]' />
</div>
Expand Down Expand Up @@ -286,7 +286,7 @@ export function ThinkingBlock({
)}
>
{/* Use markdown renderer for completed content */}
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-none [&_*]:!m-0 [&_*]:!p-0 [&_*]:!mb-0 [&_*]:!mt-0 [&_p]:!m-0 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_code]:!text-[11px] [&_ul]:!pl-4 [&_ul]:!my-0 [&_ol]:!pl-4 [&_ol]:!my-0 [&_li]:!my-0 [&_li]:!py-0 [&_br]:!leading-[0.5] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)] leading-none'>
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-[1.3] [&_p]:!m-0 [&_p]:!mb-1 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h1]:!m-0 [&_h1]:!mb-1 [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h2]:!m-0 [&_h2]:!mb-1 [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_h3]:!m-0 [&_h3]:!mb-1 [&_code]:!text-[11px] [&_ul]:!pl-5 [&_ul]:!my-1 [&_ol]:!pl-6 [&_ol]:!my-1 [&_li]:!my-0.5 [&_li]:!py-0 [&_br]:!leading-[0.5] [&_table]:!my-2 [&_th]:!px-2 [&_th]:!py-1 [&_th]:!text-[11px] [&_td]:!px-2 [&_td]:!py-1 [&_td]:!text-[11px] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)]'>
<CopilotMarkdownRenderer content={content} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function getBlockRingStyles(options: BlockRingOptions): {
!isPending &&
!isDeletedBlock &&
diffStatus === 'new' &&
'ring-[var(--brand-tertiary)]',
'ring-[var(--brand-tertiary-2)]',
!isActive &&
!isPending &&
!isDeletedBlock &&
Expand Down
50 changes: 50 additions & 0 deletions apps/sim/lib/copilot/tools/client/other/search-library-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { BookOpen, Loader2, MinusCircle, XCircle } from 'lucide-react'
import {
BaseClientTool,
type BaseClientToolMetadata,
ClientToolCallState,
} from '@/lib/copilot/tools/client/base-tool'

export class SearchLibraryDocsClientTool extends BaseClientTool {
static readonly id = 'search_library_docs'

constructor(toolCallId: string) {
super(toolCallId, SearchLibraryDocsClientTool.id, SearchLibraryDocsClientTool.metadata)
}

static readonly metadata: BaseClientToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Reading docs', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Reading docs', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Reading docs', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Read docs', icon: BookOpen },
[ClientToolCallState.error]: { text: 'Failed to read docs', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted reading docs', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped reading docs', icon: MinusCircle },
},
getDynamicText: (params, state) => {
const libraryName = params?.library_name
if (libraryName && typeof libraryName === 'string') {
switch (state) {
case ClientToolCallState.success:
return `Read ${libraryName} docs`
case ClientToolCallState.executing:
case ClientToolCallState.generating:
case ClientToolCallState.pending:
return `Reading ${libraryName} docs`
case ClientToolCallState.error:
return `Failed to read ${libraryName} docs`
case ClientToolCallState.aborted:
return `Aborted reading ${libraryName} docs`
case ClientToolCallState.rejected:
return `Skipped reading ${libraryName} docs`
}
}
return undefined
},
}

async execute(): Promise<void> {
return
}
}
19 changes: 16 additions & 3 deletions apps/sim/stores/panel/copilot/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { RememberDebugClientTool } from '@/lib/copilot/tools/client/other/rememb
import { ResearchClientTool } from '@/lib/copilot/tools/client/other/research'
import { SearchDocumentationClientTool } from '@/lib/copilot/tools/client/other/search-documentation'
import { SearchErrorsClientTool } from '@/lib/copilot/tools/client/other/search-errors'
import { SearchLibraryDocsClientTool } from '@/lib/copilot/tools/client/other/search-library-docs'
import { SearchOnlineClientTool } from '@/lib/copilot/tools/client/other/search-online'
import { SearchPatternsClientTool } from '@/lib/copilot/tools/client/other/search-patterns'
import { SleepClientTool } from '@/lib/copilot/tools/client/other/sleep'
Expand Down Expand Up @@ -116,6 +117,7 @@ const CLIENT_TOOL_INSTANTIATORS: Record<string, (id: string) => any> = {
get_trigger_blocks: (id) => new GetTriggerBlocksClientTool(id),
search_online: (id) => new SearchOnlineClientTool(id),
search_documentation: (id) => new SearchDocumentationClientTool(id),
search_library_docs: (id) => new SearchLibraryDocsClientTool(id),
search_patterns: (id) => new SearchPatternsClientTool(id),
search_errors: (id) => new SearchErrorsClientTool(id),
remember_debug: (id) => new RememberDebugClientTool(id),
Expand Down Expand Up @@ -174,6 +176,7 @@ export const CLASS_TOOL_METADATA: Record<string, BaseClientToolMetadata | undefi
get_trigger_blocks: (GetTriggerBlocksClientTool as any)?.metadata,
search_online: (SearchOnlineClientTool as any)?.metadata,
search_documentation: (SearchDocumentationClientTool as any)?.metadata,
search_library_docs: (SearchLibraryDocsClientTool as any)?.metadata,
search_patterns: (SearchPatternsClientTool as any)?.metadata,
search_errors: (SearchErrorsClientTool as any)?.metadata,
remember_debug: (RememberDebugClientTool as any)?.metadata,
Expand Down Expand Up @@ -2433,9 +2436,10 @@ export const useCopilotStore = create<CopilotStore>()(

// If already sending a message, queue this one instead
if (isSendingMessage) {
get().addToQueue(message, { fileAttachments, contexts })
get().addToQueue(message, { fileAttachments, contexts, messageId })
logger.info('[Copilot] Message queued (already sending)', {
queueLength: get().messageQueue.length + 1,
originalMessageId: messageId,
})
return
}
Expand Down Expand Up @@ -3161,8 +3165,12 @@ export const useCopilotStore = create<CopilotStore>()(
// Process next message in queue if any
const nextInQueue = get().messageQueue[0]
if (nextInQueue) {
// Use originalMessageId if available (from edit/resend), otherwise use queue entry id
const messageIdToUse = nextInQueue.originalMessageId || nextInQueue.id
logger.info('[Queue] Processing next queued message', {
id: nextInQueue.id,
originalMessageId: nextInQueue.originalMessageId,
messageIdToUse,
queueLength: get().messageQueue.length,
})
// Remove from queue and send
Expand All @@ -3173,7 +3181,7 @@ export const useCopilotStore = create<CopilotStore>()(
stream: true,
fileAttachments: nextInQueue.fileAttachments,
contexts: nextInQueue.contexts,
messageId: nextInQueue.id,
messageId: messageIdToUse,
})
}, 100)
}
Expand Down Expand Up @@ -3615,10 +3623,12 @@ export const useCopilotStore = create<CopilotStore>()(
fileAttachments: options?.fileAttachments,
contexts: options?.contexts,
queuedAt: Date.now(),
originalMessageId: options?.messageId,
}
set({ messageQueue: [...get().messageQueue, queuedMessage] })
logger.info('[Queue] Message added to queue', {
id: queuedMessage.id,
originalMessageId: options?.messageId,
queueLength: get().messageQueue.length,
})
},
Expand Down Expand Up @@ -3659,12 +3669,15 @@ export const useCopilotStore = create<CopilotStore>()(
await new Promise((resolve) => setTimeout(resolve, 50))
}

// Use originalMessageId if available (from edit/resend), otherwise use queue entry id
const messageIdToUse = message.originalMessageId || message.id

// Send the message
await get().sendMessage(message.content, {
stream: true,
fileAttachments: message.fileAttachments,
contexts: message.contexts,
messageId: message.id,
messageId: messageIdToUse,
})
},

Expand Down
4 changes: 4 additions & 0 deletions apps/sim/stores/panel/copilot/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export interface QueuedMessage {
fileAttachments?: MessageFileAttachment[]
contexts?: ChatContext[]
queuedAt: number
/** Original messageId to use when processing (for edit/resend flows) */
originalMessageId?: string
}

// Contexts attached to a user message
Expand Down Expand Up @@ -249,6 +251,8 @@ export interface CopilotActions {
options?: {
fileAttachments?: MessageFileAttachment[]
contexts?: ChatContext[]
/** Original messageId to preserve (for edit/resend flows) */
messageId?: string
}
) => void
removeFromQueue: (id: string) => void
Expand Down