Skip to content

Commit 35c490d

Browse files
committed
address bugbot comments"
1 parent 8f18ccf commit 35c490d

3 files changed

Lines changed: 42 additions & 24 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -173,26 +173,28 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
173173
return groupKey(name, undefined)
174174
}
175175

176-
const ensureGroup = (name: string, parentToolCallId: string | undefined): AgentGroupSegment => {
176+
const ensureGroup = (
177+
name: string,
178+
parentToolCallId: string | undefined
179+
): { group: AgentGroupSegment; created: boolean } => {
177180
const key = resolveGroupKey(name, parentToolCallId)
178-
let g = groupsByKey.get(key)
179-
if (!g) {
180-
g = {
181-
type: 'agent_group',
182-
// Suffix with segments.length so a later flushLanes / explicit delete
183-
// followed by re-ensure for the same key produces a fresh React key
184-
// instead of colliding with the stranded prior segment.
185-
id: `agent-${key}-${segments.length}`,
186-
agentName: name,
187-
agentLabel: resolveAgentLabel(name),
188-
items: [],
189-
isDelegating: false,
190-
isOpen: false,
191-
}
192-
segments.push(g)
193-
groupsByKey.set(key, g)
181+
const existing = groupsByKey.get(key)
182+
if (existing) return { group: existing, created: false }
183+
const group: AgentGroupSegment = {
184+
type: 'agent_group',
185+
// Suffix with segments.length so a later flushLanes / explicit delete
186+
// followed by re-ensure for the same key produces a fresh React key
187+
// instead of colliding with the stranded prior segment.
188+
id: `agent-${key}-${segments.length}`,
189+
agentName: name,
190+
agentLabel: resolveAgentLabel(name),
191+
items: [],
192+
isDelegating: false,
193+
isOpen: false,
194194
}
195-
return g
195+
segments.push(group)
196+
groupsByKey.set(key, group)
197+
return { group, created: true }
196198
}
197199

198200
const findGroupForSubagentChunk = (
@@ -302,7 +304,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
302304
// arrives after this subagent finishes should render below the lane,
303305
// not get back-filled into the mothership group sitting above it.
304306
groupsByKey.delete(groupKey('mothership', undefined))
305-
const g = ensureGroup(key, block.parentToolCallId)
307+
const { group: g } = ensureGroup(key, block.parentToolCallId)
306308
if (inheritedDelegation) g.isDelegating = true
307309
g.isOpen = block.endedAt === undefined
308310
activeGroupKey = resolveGroupKey(key, block.parentToolCallId)
@@ -318,7 +320,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
318320

319321
if (isDispatch) {
320322
groupsByKey.delete(groupKey('mothership', undefined))
321-
const g = ensureGroup(tc.name, tc.id)
323+
const { group: g } = ensureGroup(tc.name, tc.id)
322324
g.isDelegating = isDelegatingTool(tc)
323325
g.isOpen = g.isDelegating
324326
continue
@@ -327,13 +329,16 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
327329
const tool = toToolData(tc)
328330

329331
if (tc.calledBy) {
330-
const g = ensureGroup(tc.calledBy, block.parentToolCallId)
332+
const { group: g, created } = ensureGroup(tc.calledBy, block.parentToolCallId)
331333
g.isDelegating = false
332-
if (block.parentToolCallId) g.isOpen = true
334+
// Only mark the lane open when we just created it. Late tool_calls
335+
// arriving after a subagent_end (out-of-order persistence, replay,
336+
// partial state hand-off) must NOT reopen a closed lane.
337+
if (created && block.parentToolCallId) g.isOpen = true
333338
g.items.push({ type: 'tool', data: tool })
334339
activeGroupKey = resolveGroupKey(tc.calledBy, block.parentToolCallId)
335340
} else {
336-
const g = ensureGroup('mothership', undefined)
341+
const { group: g } = ensureGroup('mothership', undefined)
337342
g.items.push({ type: 'tool', data: tool })
338343
}
339344
continue

apps/sim/lib/copilot/request/go/stream.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,11 @@ export async function runStreamLoop(
405405
context.subAgentParentStack.length > 0
406406
? context.subAgentParentStack[context.subAgentParentStack.length - 1]
407407
: undefined
408-
if (toolCallId && subagentName) {
408+
if (toolCallId) {
409+
// The matching subagent block is uniquely keyed by parentToolCallId.
410+
// Don't gate on subagentName: end events occasionally arrive without
411+
// payload.agent, but the toolCallId is enough to close the lane and
412+
// free the parent slot for future invocations of the same id.
409413
for (let i = context.contentBlocks.length - 1; i >= 0; i--) {
410414
const b = context.contentBlocks[i]
411415
if (

apps/sim/lib/copilot/request/handlers/text.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ export function handleTextEvent(scope: ToolScope): StreamHandler {
2222
const parentToolCallId = getScopedParentToolCallId(event, context)
2323
if (!parentToolCallId) return
2424
if (event.payload.channel === MothershipStreamV1TextChannel.thinking) {
25+
if (
26+
context.currentSubagentThinkingBlock &&
27+
context.currentSubagentThinkingBlock.parentToolCallId !== parentToolCallId
28+
) {
29+
// Lane changed mid-stream (interleaved parallel subagents) — flush
30+
// the prior block so the merged content stays attributed to the
31+
// correct lane.
32+
flushSubagentThinkingBlock(context)
33+
}
2534
if (!context.currentSubagentThinkingBlock) {
2635
context.currentSubagentThinkingBlock = {
2736
type: 'subagent_thinking',

0 commit comments

Comments
 (0)