Skip to content

Commit 2525c4b

Browse files
committed
refactor(mothership): pass a chat send as one SendPayload
A send's content, attachments, contexts, mode and search settings travel as one SendPayload instead of positional parameters (createQueuedMessage, sendMothershipMessage) and about ten hand-spread copies; sendPayload() is the one place that drops the fields a send does not set.
1 parent 197a9ce commit 2525c4b

8 files changed

Lines changed: 81 additions & 180 deletions

File tree

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { ChatResourcePanel } from '@/app/workspace/[workspaceId]/home/components
2525
import { RESOURCE_HEADER_CLASSES } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tab-controls'
2626
import { SuggestedActions } from '@/app/workspace/[workspaceId]/home/components/suggested-actions'
2727
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
28+
import { sendPayload } from '@/app/workspace/[workspaceId]/home/hooks/send-queue-policy'
2829
import {
2930
useChatResourcePanel,
3031
useResourcePanelController,
@@ -242,13 +243,13 @@ function HomeContent({ chatId, userName, userId }: HomeProps) {
242243
if (!detail?.message) return
243244
e.preventDefault()
244245
prepareResourceViewForAgentTurn()
245-
sendMessage(detail.message, detail.fileAttachments, detail.contexts, {
246+
const { content, fileAttachments, contexts, ...sendOptions } = sendPayload({
247+
...detail,
248+
content: detail.message,
249+
})
250+
sendMessage(content, fileAttachments, contexts, {
251+
...sendOptions,
246252
...(detail.resumeUserMessageId ? { resumeUserMessageId: detail.resumeUserMessageId } : {}),
247-
...(detail.requestMode ? { requestMode: detail.requestMode } : {}),
248-
...(detail.assistantSearch ? { assistantSearch: detail.assistantSearch } : {}),
249-
...(detail.assistantSearchLevel !== undefined
250-
? { assistantSearchLevel: detail.assistantSearchLevel }
251-
: {}),
252253
})
253254
}
254255
window.addEventListener(MOTHERSHIP_SEND_MESSAGE_EVENT, handler)
@@ -279,15 +280,15 @@ function HomeContent({ chatId, userName, userId }: HomeProps) {
279280
if (!handoff) return
280281
if (handoff.message) {
281282
prepareResourceViewForAgentTurn()
282-
sendMessage(handoff.message, handoff.fileAttachments, handoff.contexts, {
283+
const { content, fileAttachments, contexts, ...sendOptions } = sendPayload({
284+
...handoff,
285+
content: handoff.message,
286+
})
287+
sendMessage(content, fileAttachments, contexts, {
288+
...sendOptions,
283289
...(handoff.resumeUserMessageId
284290
? { resumeUserMessageId: handoff.resumeUserMessageId }
285291
: {}),
286-
...(handoff.requestMode ? { requestMode: handoff.requestMode } : {}),
287-
...(handoff.assistantSearch ? { assistantSearch: handoff.assistantSearch } : {}),
288-
...(handoff.assistantSearchLevel !== undefined
289-
? { assistantSearchLevel: handoff.assistantSearchLevel }
290-
: {}),
291292
})
292293
return
293294
}

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

Lines changed: 51 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ import { reusedRequestId, useMothershipQueueStore } from '@/stores/mothership-qu
138138
import type {
139139
QueuedMothershipMessage,
140140
QueuedSendHandoffSeed,
141-
ScheduledRetry,
142141
} from '@/stores/mothership-queue/types'
143142
import type { ChatContext } from '@/stores/panel'
144143
import { useTableViewPinStore } from '@/stores/table/view-pin/store'
@@ -153,6 +152,7 @@ import type {
153152
MothershipResource,
154153
MothershipResourceType,
155154
QueuedMessage,
155+
SendPayload,
156156
ToolCallInfo,
157157
} from '../types'
158158
import {
@@ -178,6 +178,7 @@ import {
178178
} from './send-handoff'
179179
import {
180180
requeuedFields,
181+
sendPayload,
181182
sendRetry,
182183
type WithdrawalReason,
183184
withoutRequeueFields,
@@ -277,14 +278,8 @@ interface PendingChatAdmission {
277278
}
278279

279280
/** A send an unmount cleanup withdrew, as handed to the next chat surface. */
280-
interface WithdrawnSend {
281-
content: string
282-
fileAttachments?: FileAttachmentForApi[]
283-
contexts?: ChatContext[]
281+
interface WithdrawnSend extends SendPayload {
284282
userMessageId: string
285-
requestMode?: ChatRequestMode
286-
assistantSearch?: WorkspaceSearchFilters
287-
assistantSearchLevel?: AssistantSearchLevel
288283
}
289284

290285
export interface UseChatReturn {
@@ -3391,15 +3386,7 @@ export function useChat(
33913386
)
33923387

33933388
const createQueuedMessage = useCallback(
3394-
(
3395-
message: string,
3396-
fileAttachments?: FileAttachmentForApi[],
3397-
contexts?: ChatContext[],
3398-
resumeUserMessageId?: string,
3399-
requestMode?: ChatRequestMode,
3400-
assistantSearch?: WorkspaceSearchFilters,
3401-
assistantSearchLevel?: AssistantSearchLevel
3402-
): QueuedMothershipMessage => {
3389+
(payload: SendPayload, resumeUserMessageId?: string): QueuedMothershipMessage => {
34033390
const id = generateId()
34043391
const handoffChatId = selectedChatIdRef.current ?? chatIdRef.current
34053392
const cachedActiveStreamId = handoffChatId
@@ -3415,13 +3402,8 @@ export function useChat(
34153402

34163403
return {
34173404
id,
3418-
content: message,
3419-
fileAttachments,
3420-
contexts,
3405+
...sendPayload(payload),
34213406
...(resumeUserMessageId ? { resumeUserMessageId } : {}),
3422-
...(requestMode ? { requestMode } : {}),
3423-
...(assistantSearch ? { assistantSearch } : {}),
3424-
...(assistantSearchLevel !== undefined ? { assistantSearchLevel } : {}),
34253407
...(supersededStreamId || handoffChatId
34263408
? {
34273409
queuedSendHandoff: {
@@ -3610,9 +3592,18 @@ export function useChat(
36103592
if (!admittedThisSend || latestChoice !== effortChoice)
36113593
saveMothershipChatEffort(queryClient, chatId, latestChoice)
36123594
}
3595+
const payload = sendPayload({
3596+
content: message,
3597+
fileAttachments,
3598+
contexts,
3599+
requestMode: options?.requestMode,
3600+
assistantSearch: options?.assistantSearch,
3601+
assistantSearchLevel: options?.assistantSearchLevel,
3602+
})
36133603
const writeQueuedSendHandoff = (chatId?: string) => {
36143604
if (!queuedSendHandoff) return
36153605
if (!chatId && !queuedSendHandoff.supersededStreamId) return
3606+
const { content, ...payloadFields } = payload
36163607
writeQueuedSendHandoffState({
36173608
id: queuedSendHandoff.id,
36183609
...(chatId ? { chatId } : {}),
@@ -3622,14 +3613,8 @@ export function useChat(
36223613
...(queuedSendHandoff.stopRequired ? { stopRequired: true } : {}),
36233614
admissionUnknown,
36243615
userMessageId,
3625-
message,
3626-
...(fileAttachments ? { fileAttachments } : {}),
3627-
...(contexts ? { contexts } : {}),
3628-
...(options?.requestMode ? { requestMode: options.requestMode } : {}),
3629-
...(options?.assistantSearch ? { assistantSearch: options.assistantSearch } : {}),
3630-
...(options?.assistantSearchLevel !== undefined
3631-
? { assistantSearchLevel: options?.assistantSearchLevel }
3632-
: {}),
3616+
message: content,
3617+
...payloadFields,
36333618
requestedAt: Date.now(),
36343619
})
36353620
}
@@ -3802,17 +3787,7 @@ export function useChat(
38023787
settled: new Promise((resolve) => {
38033788
resolveAdmission = resolve
38043789
}),
3805-
send: {
3806-
content: message,
3807-
userMessageId,
3808-
...(fileAttachments ? { fileAttachments } : {}),
3809-
...(contexts ? { contexts } : {}),
3810-
...(options?.requestMode ? { requestMode: options.requestMode } : {}),
3811-
...(options?.assistantSearch ? { assistantSearch: options.assistantSearch } : {}),
3812-
...(options?.assistantSearchLevel !== undefined
3813-
? { assistantSearchLevel: options.assistantSearchLevel }
3814-
: {}),
3815-
},
3790+
send: { ...payload, userMessageId },
38163791
}
38173792
pendingChatAdmissionRef.current = admission
38183793
}
@@ -4297,31 +4272,11 @@ export function useChat(
42974272
(send: WithdrawnSend) => {
42984273
/** The unmount already queued it ahead of its follow-ups; see the unmount cleanup. */
42994274
if (withdrawnHeldAtUnmountRef.current?.delete(send.userMessageId)) return
4300-
if (
4301-
sendMothershipMessage(
4302-
send.content,
4303-
send.contexts,
4304-
send.fileAttachments,
4305-
send.userMessageId,
4306-
send.requestMode,
4307-
send.assistantSearch,
4308-
send.assistantSearchLevel
4309-
)
4310-
) {
4311-
return
4312-
}
4275+
const payload = sendPayload(send)
4276+
if (sendMothershipMessage(payload, send.userMessageId)) return
4277+
const { content, ...payloadFields } = payload
43134278
MothershipHandoffStorage.store(
4314-
{
4315-
message: send.content,
4316-
...(send.contexts?.length ? { contexts: send.contexts } : {}),
4317-
...(send.fileAttachments?.length ? { fileAttachments: send.fileAttachments } : {}),
4318-
resumeUserMessageId: send.userMessageId,
4319-
...(send.requestMode ? { requestMode: send.requestMode } : {}),
4320-
...(send.assistantSearch ? { assistantSearch: send.assistantSearch } : {}),
4321-
...(send.assistantSearchLevel !== undefined
4322-
? { assistantSearchLevel: send.assistantSearchLevel }
4323-
: {}),
4324-
},
4279+
{ message: content, ...payloadFields, resumeUserMessageId: send.userMessageId },
43254280
organizationId ? { organizationId } : workspaceId!
43264281
)
43274282
},
@@ -4365,6 +4320,14 @@ export function useChat(
43654320
}
43664321

43674322
options = { ...options, requestMode: options?.requestMode ?? requestModeRef.current }
4323+
const payload = sendPayload({
4324+
content: message,
4325+
fileAttachments,
4326+
contexts,
4327+
requestMode: options.requestMode,
4328+
assistantSearch: options.assistantSearch,
4329+
assistantSearchLevel: options.assistantSearchLevel,
4330+
})
43684331

43694332
// An in-flight send drains the queue from `finalize`; a pending stop kicks
43704333
// the dispatcher itself, since nothing else will once the stop settles.
@@ -4380,18 +4343,7 @@ export function useChat(
43804343
queuedAheadCount
43814344
)
43824345
) {
4383-
queueStore.enqueue(
4384-
activeChatKey,
4385-
createQueuedMessage(
4386-
message,
4387-
fileAttachments,
4388-
contexts,
4389-
options?.resumeUserMessageId,
4390-
options?.requestMode,
4391-
options?.assistantSearch,
4392-
options?.assistantSearchLevel
4393-
)
4394-
)
4346+
queueStore.enqueue(activeChatKey, createQueuedMessage(payload, options.resumeUserMessageId))
43954347
if (pendingStopPromiseRef.current || (queuedAheadCount > 0 && !sendingRef.current)) {
43964348
void enqueueQueueDispatchRef.current({ type: 'send_head' })
43974349
}
@@ -4407,34 +4359,17 @@ export function useChat(
44074359
whichever one they opened next. Only a send an unmount withdrew from a
44084360
chatless surface, whose key dies with the mount, goes to the
44094361
cross-surface lanes. */
4410-
const withdrawn = {
4411-
content: message,
4412-
fileAttachments,
4413-
contexts,
4414-
userMessageId: result.userMessageId,
4415-
...(options?.requestMode ? { requestMode: options.requestMode } : {}),
4416-
...(options?.assistantSearch ? { assistantSearch: options.assistantSearch } : {}),
4417-
...(options?.assistantSearchLevel !== undefined
4418-
? { assistantSearchLevel: options?.assistantSearchLevel }
4419-
: {}),
4420-
}
44214362
const chatless = activeChatKey.startsWith(PENDING_CHAT_KEY_PREFIX)
44224363
if (result.reason === 'withdrawn' && chatless) {
4423-
handOffWithdrawnSend(withdrawn)
4364+
handOffWithdrawnSend({ ...payload, userMessageId: result.userMessageId })
44244365
return
44254366
}
44264367
/* Back at the head: a direct send only goes out with nothing queued ahead of
4427-
it, so anything queued while its POST was out was written after it. */
4368+
it, so anything queued while its POST was out was written after it. The one
4369+
exception is a held send adopted from a dead mount of this surface in that
4370+
window, which can be older; it lands behind this one. */
44284371
useMothershipQueueStore.getState().insertAt(activeChatKey, 0, {
4429-
...createQueuedMessage(
4430-
message,
4431-
fileAttachments,
4432-
contexts,
4433-
result.userMessageId,
4434-
options?.requestMode,
4435-
options?.assistantSearch,
4436-
options?.assistantSearchLevel
4437-
),
4372+
...createQueuedMessage(payload, result.userMessageId),
44384373
...requeuedFields(result.reason, 0, chatless ? heldSendSurface : undefined),
44394374
admissionUnknown: result.admissionUnknown,
44404375
})
@@ -4634,14 +4569,7 @@ export function useChat(
46344569
/** Recovered sends join the queue so dispatch, failure and retry have one owner. */
46354570
useMothershipQueueStore.getState().insertAt(chatHistory.id, 0, {
46364571
id: handoff.id,
4637-
content: handoff.message,
4638-
fileAttachments: handoff.fileAttachments,
4639-
contexts: handoff.contexts,
4640-
...(handoff.requestMode ? { requestMode: handoff.requestMode } : {}),
4641-
...(handoff.assistantSearch ? { assistantSearch: handoff.assistantSearch } : {}),
4642-
...(handoff.assistantSearchLevel !== undefined
4643-
? { assistantSearchLevel: handoff.assistantSearchLevel }
4644-
: {}),
4572+
...sendPayload({ ...handoff, content: handoff.message }),
46454573
queuedSendHandoff: {
46464574
id: handoff.id,
46474575
chatId: handoff.chatId,
@@ -5037,14 +4965,7 @@ export function useChat(
50374965
if (withdrawn?.reason === 'withdrawn' && chatless) {
50384966
clearQueuedSendHandoffState(msg.id)
50394967
handOffWithdrawnSend({
5040-
content: dispatched.content,
5041-
fileAttachments: dispatched.fileAttachments,
5042-
contexts: dispatched.contexts,
5043-
...(dispatched.requestMode ? { requestMode: dispatched.requestMode } : {}),
5044-
...(dispatched.assistantSearch ? { assistantSearch: dispatched.assistantSearch } : {}),
5045-
...(dispatched.assistantSearchLevel !== undefined
5046-
? { assistantSearchLevel: dispatched.assistantSearchLevel }
5047-
: {}),
4968+
...sendPayload(dispatched),
50484969
userMessageId: withdrawn.userMessageId,
50494970
})
50504971
return
@@ -5083,27 +5004,19 @@ export function useChat(
50835004
dispatched = liveMsg
50845005
activeQueuedSendHandoff = options.queuedSendHandoff ?? liveMsg.queuedSendHandoff
50855006

5086-
const sendResult = await startSendMessage(
5087-
liveMsg.content,
5088-
liveMsg.fileAttachments,
5089-
liveMsg.contexts,
5090-
{
5091-
pendingStop: options.pendingStop,
5092-
onOptimisticSendApplied: removeQueuedMessage,
5093-
queuedSendHandoff: activeQueuedSendHandoff,
5094-
...(liveMsg.resumeUserMessageId
5095-
? { resumeUserMessageId: liveMsg.resumeUserMessageId }
5096-
: {}),
5097-
...(liveMsg.admissionUnknown !== undefined
5098-
? { admissionUnknown: liveMsg.admissionUnknown }
5099-
: {}),
5100-
...(liveMsg.requestMode ? { requestMode: liveMsg.requestMode } : {}),
5101-
...(liveMsg.assistantSearch ? { assistantSearch: liveMsg.assistantSearch } : {}),
5102-
...(liveMsg.assistantSearchLevel !== undefined
5103-
? { assistantSearchLevel: liveMsg.assistantSearchLevel }
5104-
: {}),
5105-
}
5106-
)
5007+
const { content, fileAttachments, contexts, ...sendOptions } = sendPayload(liveMsg)
5008+
const sendResult = await startSendMessage(content, fileAttachments, contexts, {
5009+
...sendOptions,
5010+
pendingStop: options.pendingStop,
5011+
onOptimisticSendApplied: removeQueuedMessage,
5012+
queuedSendHandoff: activeQueuedSendHandoff,
5013+
...(liveMsg.resumeUserMessageId
5014+
? { resumeUserMessageId: liveMsg.resumeUserMessageId }
5015+
: {}),
5016+
...(liveMsg.admissionUnknown !== undefined
5017+
? { admissionUnknown: liveMsg.admissionUnknown }
5018+
: {}),
5019+
})
51075020

51085021
if (sendResult !== true) {
51095022
restoreQueuedMessage(
@@ -5400,16 +5313,9 @@ export function useChat(
54005313
const { send } = withdrawing
54015314
queueStore.insertAt(deadKey, 0, {
54025315
id: generateId(),
5403-
content: send.content,
5316+
...sendPayload(send),
54045317
resumeUserMessageId: send.userMessageId,
54055318
admissionUnknown: true,
5406-
...(send.fileAttachments ? { fileAttachments: send.fileAttachments } : {}),
5407-
...(send.contexts ? { contexts: send.contexts } : {}),
5408-
...(send.requestMode ? { requestMode: send.requestMode } : {}),
5409-
...(send.assistantSearch ? { assistantSearch: send.assistantSearch } : {}),
5410-
...(send.assistantSearchLevel !== undefined
5411-
? { assistantSearchLevel: send.assistantSearchLevel }
5412-
: {}),
54135319
})
54145320
withdrawnHeldAtUnmountRef.current ??= new Set()
54155321
withdrawnHeldAtUnmountRef.current.add(send.userMessageId)

‎apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ export function LogDetailsContent({ log, onActiveTabChange }: LogDetailsContentP
475475
const message = workflowName
476476
? `The "${workflowName}" workflow run failed. Investigate the error in this run and help me fix it.`
477477
: 'This workflow run failed. Investigate the error in this run and help me fix it.'
478-
if (sendMothershipMessage(message, [context])) return
478+
if (sendMothershipMessage({ content: message, contexts: [context] })) return
479479
if (MothershipHandoffStorage.store({ message, contexts: [context] }, workspaceId)) {
480480
router.push(`/workspace/${workspaceId}/home`)
481481
}

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ export const Terminal = memo(function Terminal() {
905905
const errorMessage = entry.error ? String(entry.error) : 'Unknown error'
906906
const blockName = entry.blockName || 'Unknown Block'
907907
const message = `${errorMessage}\n\nError in ${blockName}.\n\nPlease fix this.`
908-
sendMothershipMessage(message)
908+
sendMothershipMessage({ content: message })
909909
closeLogRowMenu()
910910
},
911911
[closeLogRowMenu]

0 commit comments

Comments
 (0)