fix(copilot): fix state message sent on move to background (#871)

* Initial fix

* Add execution start time to message

* Lint
This commit is contained in:
Siddharth Ganesan
2025-08-04 19:23:44 -07:00
committed by GitHub
parent 48b32a346c
commit 221a473ccc
6 changed files with 60 additions and 13 deletions

View File

@@ -78,6 +78,19 @@ async function updateToolCallStatus(
message: message || null,
timestamp: new Date().toISOString(),
}
// Log what we're about to update in Redis
logger.info('About to update Redis with tool call data', {
toolCallId,
key,
toolCallData,
serializedData: JSON.stringify(toolCallData),
providedStatus: status,
providedMessage: message,
messageIsUndefined: message === undefined,
messageIsNull: message === null,
})
await redis.set(key, JSON.stringify(toolCallData), 'EX', 86400) // Keep 24 hour expiry
logger.info('Tool call status updated in Redis', {

View File

@@ -98,6 +98,18 @@ async function pollRedisForTool(
}
if (status !== 'pending') {
// Log the message found in redis prominently - always log, even if message is null/undefined
logger.info('Redis poller found non-pending status', {
toolCallId,
foundMessage: message,
messageType: typeof message,
messageIsNull: message === null,
messageIsUndefined: message === undefined,
status,
duration: Date.now() - startTime,
rawRedisValue: redisValue,
})
logger.info('Tool call status resolved', {
toolCallId,
status,

View File

@@ -139,6 +139,14 @@ export class RunWorkflowTool extends BaseTool {
// Note: toolCall.state is already set to 'executing' by clientAcceptTool
// Capture the execution timestamp
const executionStartTime = new Date().toISOString()
// Store execution start time in context for background notifications
if (options?.context) {
options.context.executionStartTime = executionStartTime
}
// Use the standalone execution utility with full logging support
// This works for both deployed and non-deployed workflows
const result = await executeWorkflowWithFullLogging({
@@ -151,8 +159,12 @@ export class RunWorkflowTool extends BaseTool {
// Check if execution was successful
if (result && (!('success' in result) || result.success !== false)) {
// Notify server of success
await this.notify(toolCall.id, 'success', 'Workflow execution completed successfully')
// Notify server of success with execution timestamp
await this.notify(
toolCall.id,
'success',
`Workflow execution completed successfully. Started at: ${executionStartTime}`
)
options?.onStateChange?.('success')

View File

@@ -262,8 +262,9 @@ export function InlineToolCall({ toolCall, onStateChange, context }: InlineToolC
// Set tool state to background
setToolCallState(toolCall, 'background')
// Notify the backend about background state
await notifyServerTool(toolCall.id, toolCall.name, 'background')
// Notify the backend about background state with execution start time if available
const executionStartTime = context?.executionStartTime
await notifyServerTool(toolCall.id, toolCall.name, 'background', executionStartTime)
// Track that this tool was moved to background
if (context) {

View File

@@ -32,19 +32,21 @@ const SERVER_TOOL_MAPPINGS: Partial<Record<ToolState, NotificationStatus>> = {
export async function notifyServerTool(
toolId: string,
toolName: string,
toolState: ToolState
toolState: ToolState,
executionStartTime?: string
): Promise<void> {
const notificationStatus = SERVER_TOOL_MAPPINGS[toolState]
if (!notificationStatus) {
throw new Error(`Invalid tool state: ${toolState}`)
}
await notify(toolId, toolName, toolState)
await notify(toolId, toolName, toolState, executionStartTime)
}
export async function notify(
toolId: string,
toolName: string,
toolState: ToolState
toolState: ToolState,
executionStartTime?: string
): Promise<void> {
// toolState must be in STATE_MAPPINGS
const notificationStatus = STATE_MAPPINGS[toolState]
@@ -55,8 +57,15 @@ export async function notify(
// Get the state message from tool metadata
const metadata = toolRegistry.getToolMetadata(toolId)
let stateMessage = metadata?.stateMessages?.[notificationStatus]
// If no message from metadata, provide default messages
if (!stateMessage) {
stateMessage = ''
if (notificationStatus === 'background') {
const timeInfo = executionStartTime ? ` Started at: ${executionStartTime}.` : ''
stateMessage = `The user has moved tool execution to the background and it is not complete, it will run asynchronously.${timeInfo}`
} else {
stateMessage = ''
}
}
// Call backend confirm route
@@ -68,9 +77,7 @@ export async function notify(
body: JSON.stringify({
toolCallId: toolId,
status: notificationStatus,
toolName,
toolState,
stateMessage,
message: stateMessage,
}),
})
}

View File

@@ -61,8 +61,10 @@ export function ToolConfirmation({
})
} else {
// For server tools, use the notification system
const toolState = action === 'run' ? 'accepted' : 'rejected'
const uiState = action === 'run' ? 'accepted' : 'rejected'
const toolState =
action === 'run' ? 'accepted' : action === 'background' ? 'background' : 'rejected'
const uiState =
action === 'run' ? 'accepted' : action === 'background' ? 'background' : 'rejected'
// Update UI state
onStateChange(uiState)