Skip to content

Commit 5db5c1c

Browse files
committed
Fix edit workflow returning bad state
1 parent debcd76 commit 5db5c1c

File tree

2 files changed

+59
-26
lines changed

2 files changed

+59
-26
lines changed

apps/sim/lib/copilot/tools/client/workflow/edit-workflow.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ export class EditWorkflowClientTool extends BaseClientTool {
3838
super(toolCallId, EditWorkflowClientTool.id, EditWorkflowClientTool.metadata)
3939
}
4040

41+
async markToolComplete(status: number, message?: any, data?: any): Promise<boolean> {
42+
const logger = createLogger('EditWorkflowClientTool')
43+
logger.info('markToolComplete payload', {
44+
toolCallId: this.toolCallId,
45+
toolName: this.name,
46+
status,
47+
message,
48+
data,
49+
})
50+
return super.markToolComplete(status, message, data)
51+
}
52+
4153
/**
4254
* Get sanitized workflow JSON from a workflow state, merge subblocks, and sanitize for copilot
4355
* This matches what get_user_workflow returns
@@ -173,21 +185,13 @@ export class EditWorkflowClientTool extends BaseClientTool {
173185
async execute(args?: EditWorkflowArgs): Promise<void> {
174186
const logger = createLogger('EditWorkflowClientTool')
175187

188+
if (this.hasExecuted) {
189+
logger.info('execute skipped (already executed)', { toolCallId: this.toolCallId })
190+
return
191+
}
192+
176193
// Use timeout protection to ensure tool always completes
177194
await this.executeWithTimeout(async () => {
178-
if (this.hasExecuted) {
179-
logger.info('execute skipped (already executed)', { toolCallId: this.toolCallId })
180-
// Even if skipped, ensure we mark complete with current workflow state
181-
if (!this.hasBeenMarkedComplete()) {
182-
const currentWorkflowJson = this.getCurrentWorkflowJsonSafe(logger)
183-
await this.markToolComplete(
184-
200,
185-
'Tool already executed',
186-
currentWorkflowJson ? { userWorkflow: currentWorkflowJson } : undefined
187-
)
188-
}
189-
return
190-
}
191195
this.hasExecuted = true
192196
logger.info('execute called', { toolCallId: this.toolCallId, argsProvided: !!args })
193197
this.setState(ClientToolCallState.executing)

apps/sim/stores/panel/copilot/store.ts

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,25 @@ const sseHandlers: Record<string, SSEHandler> = {
13441344
context.contentBlocks.push(context.currentTextBlock)
13451345
}
13461346

1347+
const splitTrailingPartialTag = (
1348+
text: string,
1349+
tags: string[]
1350+
): { text: string; remaining: string } => {
1351+
const partialIndex = text.lastIndexOf('<')
1352+
if (partialIndex < 0) {
1353+
return { text, remaining: '' }
1354+
}
1355+
const possibleTag = text.substring(partialIndex)
1356+
const matchesTagStart = tags.some((tag) => tag.startsWith(possibleTag))
1357+
if (!matchesTagStart) {
1358+
return { text, remaining: '' }
1359+
}
1360+
return {
1361+
text: text.substring(0, partialIndex),
1362+
remaining: possibleTag,
1363+
}
1364+
}
1365+
13471366
while (contentToProcess.length > 0) {
13481367
// Handle design_workflow tags (takes priority over other content processing)
13491368
if (context.isInDesignWorkflowBlock) {
@@ -1363,13 +1382,17 @@ const sseHandlers: Record<string, SSEHandler> = {
13631382
hasProcessedContent = true
13641383
} else {
13651384
// Still in design_workflow block, accumulate content
1366-
context.designWorkflowContent += contentToProcess
1385+
const { text, remaining } = splitTrailingPartialTag(contentToProcess, ['</design_workflow>'])
1386+
context.designWorkflowContent += text
13671387

13681388
// Update store with partial content for streaming effect (available in all modes)
13691389
set({ streamingPlanContent: context.designWorkflowContent })
13701390

1371-
contentToProcess = ''
1391+
contentToProcess = remaining
13721392
hasProcessedContent = true
1393+
if (remaining) {
1394+
break
1395+
}
13731396
}
13741397
continue
13751398
}
@@ -1491,18 +1514,24 @@ const sseHandlers: Record<string, SSEHandler> = {
14911514
contentToProcess = contentToProcess.substring(endMatch.index + endMatch[0].length)
14921515
hasProcessedContent = true
14931516
} else {
1494-
if (context.currentThinkingBlock) {
1495-
context.currentThinkingBlock.content += contentToProcess
1496-
} else {
1497-
context.currentThinkingBlock = contentBlockPool.get()
1498-
context.currentThinkingBlock.type = THINKING_BLOCK_TYPE
1499-
context.currentThinkingBlock.content = contentToProcess
1500-
context.currentThinkingBlock.timestamp = Date.now()
1501-
context.currentThinkingBlock.startTime = Date.now()
1502-
context.contentBlocks.push(context.currentThinkingBlock)
1517+
const { text, remaining } = splitTrailingPartialTag(contentToProcess, ['</thinking>'])
1518+
if (text) {
1519+
if (context.currentThinkingBlock) {
1520+
context.currentThinkingBlock.content += text
1521+
} else {
1522+
context.currentThinkingBlock = contentBlockPool.get()
1523+
context.currentThinkingBlock.type = THINKING_BLOCK_TYPE
1524+
context.currentThinkingBlock.content = text
1525+
context.currentThinkingBlock.timestamp = Date.now()
1526+
context.currentThinkingBlock.startTime = Date.now()
1527+
context.contentBlocks.push(context.currentThinkingBlock)
1528+
}
1529+
hasProcessedContent = true
1530+
}
1531+
contentToProcess = remaining
1532+
if (remaining) {
1533+
break
15031534
}
1504-
contentToProcess = ''
1505-
hasProcessedContent = true
15061535
}
15071536
} else {
15081537
const startMatch = thinkingStartRegex.exec(contentToProcess)

0 commit comments

Comments
 (0)