Skip to content

Commit c2ac24f

Browse files
committed
Include rid
1 parent 75a3e2c commit c2ac24f

File tree

12 files changed

+122
-4
lines changed

12 files changed

+122
-4
lines changed

apps/sim/app/api/mothership/chat/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export async function POST(req: NextRequest) {
279279
role: 'assistant' as const,
280280
content: result.content,
281281
timestamp: new Date().toISOString(),
282+
...(result.requestId ? { requestId: result.requestId } : {}),
282283
}
283284
if (result.toolCalls.length > 0) {
284285
assistantMessage.toolCalls = result.toolCalls

apps/sim/app/workspace/[workspaceId]/home/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { MessageActions } from './message-actions'
12
export { MessageContent } from './message-content'
23
export { MothershipView } from './mothership-view'
34
export { QueuedMessages } from './queued-messages'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { MessageActions } from './message-actions'
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use client'
2+
3+
import { useCallback, useState } from 'react'
4+
import { Check, Copy, Ellipsis, Hash } from 'lucide-react'
5+
import {
6+
Popover,
7+
PopoverContent,
8+
PopoverItem,
9+
PopoverScrollArea,
10+
PopoverTrigger,
11+
} from '@/components/emcn'
12+
13+
interface MessageActionsProps {
14+
content: string
15+
requestId?: string
16+
}
17+
18+
export function MessageActions({ content, requestId }: MessageActionsProps) {
19+
const [open, setOpen] = useState(false)
20+
const [copied, setCopied] = useState<'message' | 'request' | null>(null)
21+
22+
const copyToClipboard = useCallback(async (text: string, type: 'message' | 'request') => {
23+
try {
24+
await navigator.clipboard.writeText(text)
25+
setCopied(type)
26+
setTimeout(() => setCopied(null), 1500)
27+
} catch {
28+
// Silently fail
29+
}
30+
setOpen(false)
31+
}, [])
32+
33+
return (
34+
<Popover variant='default' size='sm' open={open} onOpenChange={setOpen}>
35+
<PopoverTrigger asChild>
36+
<button
37+
type='button'
38+
className='rounded-md p-1 text-[var(--text-icon)] opacity-0 transition-opacity hover:bg-[var(--surface-3)] group-hover/msg:opacity-100 data-[state=open]:opacity-100'
39+
onClick={(e) => e.stopPropagation()}
40+
>
41+
<Ellipsis className='h-[14px] w-[14px]' strokeWidth={2} />
42+
</button>
43+
</PopoverTrigger>
44+
<PopoverContent
45+
side='bottom'
46+
align='end'
47+
sideOffset={4}
48+
maxHeight={120}
49+
style={{ width: '160px', minWidth: '160px' }}
50+
>
51+
<PopoverScrollArea>
52+
<PopoverItem onClick={() => copyToClipboard(content, 'message')} disabled={!content}>
53+
{copied === 'message' ? (
54+
<Check className='h-[13px] w-[13px]' />
55+
) : (
56+
<Copy className='h-[13px] w-[13px]' />
57+
)}
58+
<span>Copy Message</span>
59+
</PopoverItem>
60+
<PopoverItem
61+
onClick={() => requestId && copyToClipboard(requestId, 'request')}
62+
disabled={!requestId}
63+
>
64+
{copied === 'request' ? (
65+
<Check className='h-[13px] w-[13px]' />
66+
) : (
67+
<Hash className='h-[13px] w-[13px]' />
68+
)}
69+
<span>Copy Request ID</span>
70+
</PopoverItem>
71+
</PopoverScrollArea>
72+
</PopoverContent>
73+
</Popover>
74+
)
75+
}

apps/sim/app/workspace/[workspaceId]/home/home.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks'
1717
import type { ChatContext } from '@/stores/panel'
1818
import { useSidebarStore } from '@/stores/sidebar/store'
1919
import {
20+
MessageActions,
2021
MessageContent,
2122
MothershipView,
2223
QueuedMessages,
@@ -414,7 +415,12 @@ export function Home({ chatId }: HomeProps = {}) {
414415
const isLastMessage = index === messages.length - 1
415416

416417
return (
417-
<div key={msg.id} className='pb-4'>
418+
<div key={msg.id} className='group/msg relative pb-4'>
419+
{!isThisStreaming && (msg.content || msg.contentBlocks?.length) && (
420+
<div className='-top-1 absolute right-0 z-10'>
421+
<MessageActions content={msg.content} requestId={msg.requestId} />
422+
</div>
423+
)}
418424
<MessageContent
419425
blocks={msg.contentBlocks || []}
420426
fallbackContent={msg.content}

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ export function useChat(
509509
let activeSubagent: string | undefined
510510
let runningText = ''
511511
let lastContentSource: 'main' | 'subagent' | null = null
512+
let streamRequestId: string | undefined
512513

513514
streamingContentRef.current = ''
514515
streamingBlocksRef.current = []
@@ -526,14 +527,21 @@ export function useChat(
526527
const flush = () => {
527528
if (isStale()) return
528529
streamingBlocksRef.current = [...blocks]
529-
const snapshot = { content: runningText, contentBlocks: [...blocks] }
530+
const snapshot: Partial<ChatMessage> = {
531+
content: runningText,
532+
contentBlocks: [...blocks],
533+
}
534+
if (streamRequestId) snapshot.requestId = streamRequestId
530535
setMessages((prev) => {
531536
if (expectedGen !== undefined && streamGenRef.current !== expectedGen) return prev
532537
const idx = prev.findIndex((m) => m.id === assistantId)
533538
if (idx >= 0) {
534539
return prev.map((m) => (m.id === assistantId ? { ...m, ...snapshot } : m))
535540
}
536-
return [...prev, { id: assistantId, role: 'assistant' as const, ...snapshot }]
541+
return [
542+
...prev,
543+
{ id: assistantId, role: 'assistant' as const, content: '', ...snapshot },
544+
]
537545
})
538546
}
539547

@@ -597,6 +605,14 @@ export function useChat(
597605
}
598606
break
599607
}
608+
case 'request_id': {
609+
const rid = typeof parsed.data === 'string' ? parsed.data : undefined
610+
if (rid) {
611+
streamRequestId = rid
612+
flush()
613+
}
614+
break
615+
}
600616
case 'content': {
601617
const chunk = typeof parsed.data === 'string' ? parsed.data : (parsed.content ?? '')
602618
if (chunk) {

apps/sim/app/workspace/[workspaceId]/home/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface QueuedMessage {
3333
*/
3434
export type SSEEventType =
3535
| 'chat_id'
36+
| 'request_id'
3637
| 'title_updated'
3738
| 'content'
3839
| 'reasoning' // openai reasoning - render as thinking text
@@ -199,6 +200,7 @@ export interface ChatMessage {
199200
contentBlocks?: ContentBlock[]
200201
attachments?: ChatMessageAttachment[]
201202
contexts?: ChatMessageContext[]
203+
requestId?: string
202204
}
203205

204206
export const SUBAGENT_LABELS: Record<SubagentName, string> = {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/copilot-message.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { type FC, memo, useCallback, useMemo, useRef, useState } from 'react'
44
import { RotateCcw } from 'lucide-react'
55
import { Button } from '@/components/emcn'
6+
import { MessageActions } from '@/app/workspace/[workspaceId]/home/components/message-actions'
67
import {
78
OptionsSelector,
89
parseSpecialTags,
@@ -409,9 +410,14 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
409410
if (isAssistant) {
410411
return (
411412
<div
412-
className={`w-full max-w-full flex-none overflow-hidden [max-width:var(--panel-max-width)] ${isDimmed ? 'opacity-40' : 'opacity-100'}`}
413+
className={`group/msg relative w-full max-w-full flex-none overflow-hidden [max-width:var(--panel-max-width)] ${isDimmed ? 'opacity-40' : 'opacity-100'}`}
413414
style={{ '--panel-max-width': `${panelWidth - 16}px` } as React.CSSProperties}
414415
>
416+
{!isStreaming && message.content && (
417+
<div className='-top-1 absolute right-0 z-10'>
418+
<MessageActions content={message.content} requestId={message.requestId} />
419+
</div>
420+
)}
415421
<div className='max-w-full space-y-[4px] px-[2px] pb-[4px]'>
416422
{/* Content blocks in chronological order */}
417423
{memoizedContentBlocks || (isStreaming && <div className='min-h-0' />)}

apps/sim/lib/copilot/orchestrator/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export async function orchestrateCopilotStream(
7676
contentBlocks: context.contentBlocks,
7777
toolCalls: buildToolCallSummaries(context),
7878
chatId: context.chatId,
79+
requestId: context.requestId,
7980
errors: context.errors.length ? context.errors : undefined,
8081
usage: context.usage,
8182
cost: context.cost,

apps/sim/lib/copilot/orchestrator/sse/handlers/handlers.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ export const sseHandlers: Record<string, SSEHandler> = {
187187
execContext.chatId = chatId
188188
}
189189
},
190+
request_id: (event, context) => {
191+
const rid = typeof event.data === 'string' ? event.data : undefined
192+
if (rid) {
193+
context.requestId = rid
194+
}
195+
},
190196
title_updated: () => {},
191197
tool_result: (event, context) => {
192198
const data = getEventData(event)

0 commit comments

Comments
 (0)