diff --git a/apps/sim/app/(auth)/signup/signup-form.tsx b/apps/sim/app/(auth)/signup/signup-form.tsx index 670d4434b0..5aeb59fa67 100644 --- a/apps/sim/app/(auth)/signup/signup-form.tsx +++ b/apps/sim/app/(auth)/signup/signup-form.tsx @@ -359,15 +359,6 @@ function SignupFormContent({ } } - try { - await client.emailOtp.sendVerificationOtp({ - email: emailValue, - type: 'sign-in', - }) - } catch (otpErr) { - logger.warn('Failed to send sign-in OTP after signup; user can press Resend', otpErr) - } - router.push('/verify?fromSignup=true') } catch (error) { logger.error('Signup error:', error) diff --git a/apps/sim/app/(auth)/verify/use-verification.ts b/apps/sim/app/(auth)/verify/use-verification.ts index f59c0446cd..3ef05231a4 100644 --- a/apps/sim/app/(auth)/verify/use-verification.ts +++ b/apps/sim/app/(auth)/verify/use-verification.ts @@ -93,7 +93,7 @@ export function useVerification({ try { const normalizedEmail = email.trim().toLowerCase() - const response = await client.signIn.emailOtp({ + const response = await client.emailOtp.verifyEmail({ email: normalizedEmail, otp, }) @@ -169,7 +169,7 @@ export function useVerification({ client.emailOtp .sendVerificationOtp({ email: normalizedEmail, - type: 'sign-in', + type: 'email-verification', }) .then(() => {}) .catch(() => { diff --git a/apps/sim/app/api/logs/stats/route.ts b/apps/sim/app/api/logs/stats/route.ts index 686b10d50c..af5583b358 100644 --- a/apps/sim/app/api/logs/stats/route.ts +++ b/apps/sim/app/api/logs/stats/route.ts @@ -99,13 +99,14 @@ export async function GET(request: NextRequest) { const totalMs = Math.max(1, endTime.getTime() - startTime.getTime()) const segmentMs = Math.max(60000, Math.floor(totalMs / params.segmentCount)) + const startTimeIso = startTime.toISOString() const statsQuery = await db .select({ workflowId: workflowExecutionLogs.workflowId, workflowName: workflow.name, segmentIndex: - sql`FLOOR(EXTRACT(EPOCH FROM (${workflowExecutionLogs.startedAt} - ${startTime}::timestamp)) * 1000 / ${segmentMs})`.as( + sql`FLOOR(EXTRACT(EPOCH FROM (${workflowExecutionLogs.startedAt} - ${startTimeIso}::timestamp)) * 1000 / ${segmentMs})`.as( 'segment_index' ), totalExecutions: sql`COUNT(*)`.as('total_executions'), @@ -129,12 +130,7 @@ export async function GET(request: NextRequest) { ) ) .where(whereCondition) - .groupBy( - workflowExecutionLogs.workflowId, - workflow.name, - sql`FLOOR(EXTRACT(EPOCH FROM (${workflowExecutionLogs.startedAt} - ${startTime}::timestamp)) * 1000 / ${segmentMs})` - ) - .orderBy(workflowExecutionLogs.workflowId, sql`segment_index`) + .groupBy(workflowExecutionLogs.workflowId, workflow.name, sql`segment_index`) const workflowMap = new Map< string, diff --git a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts index 5637ad1f4c..0fff019545 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts @@ -80,6 +80,7 @@ const updateNotificationSchema = z levelFilter: levelFilterSchema.optional(), triggerFilter: triggerFilterSchema.optional(), includeFinalOutput: z.boolean().optional(), + includeTraceSpans: z.boolean().optional(), includeRateLimits: z.boolean().optional(), includeUsageData: z.boolean().optional(), alertConfig: alertConfigSchema.optional(), @@ -146,6 +147,7 @@ export async function GET(request: NextRequest, { params }: RouteParams) { levelFilter: subscription.levelFilter, triggerFilter: subscription.triggerFilter, includeFinalOutput: subscription.includeFinalOutput, + includeTraceSpans: subscription.includeTraceSpans, includeRateLimits: subscription.includeRateLimits, includeUsageData: subscription.includeUsageData, webhookConfig: subscription.webhookConfig, @@ -220,6 +222,7 @@ export async function PUT(request: NextRequest, { params }: RouteParams) { if (data.triggerFilter !== undefined) updateData.triggerFilter = data.triggerFilter if (data.includeFinalOutput !== undefined) updateData.includeFinalOutput = data.includeFinalOutput + if (data.includeTraceSpans !== undefined) updateData.includeTraceSpans = data.includeTraceSpans if (data.includeRateLimits !== undefined) updateData.includeRateLimits = data.includeRateLimits if (data.includeUsageData !== undefined) updateData.includeUsageData = data.includeUsageData if (data.alertConfig !== undefined) updateData.alertConfig = data.alertConfig @@ -257,6 +260,7 @@ export async function PUT(request: NextRequest, { params }: RouteParams) { levelFilter: subscription.levelFilter, triggerFilter: subscription.triggerFilter, includeFinalOutput: subscription.includeFinalOutput, + includeTraceSpans: subscription.includeTraceSpans, includeRateLimits: subscription.includeRateLimits, includeUsageData: subscription.includeUsageData, webhookConfig: subscription.webhookConfig, diff --git a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts index ade0689ae0..f549bc98b2 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts @@ -92,6 +92,22 @@ function buildTestPayload(subscription: typeof workspaceNotificationSubscription data.usage = { currentPeriodCost: 2.45, limit: 20, percentUsed: 12.25, isExceeded: false } } + if (subscription.includeTraceSpans && subscription.notificationType === 'webhook') { + data.traceSpans = [ + { + name: 'test-block', + startTime: timestamp, + endTime: timestamp + 150, + duration: 150, + status: 'success', + blockId: 'block_test_1', + blockType: 'agent', + blockName: 'Test Agent', + children: [], + }, + ] + } + return { payload, timestamp } } diff --git a/apps/sim/app/api/workspaces/[id]/notifications/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/route.ts index ef630045cd..36d33204ea 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/route.ts @@ -83,6 +83,7 @@ const createNotificationSchema = z levelFilter: levelFilterSchema.default(['info', 'error']), triggerFilter: triggerFilterSchema.default([...CORE_TRIGGER_TYPES]), includeFinalOutput: z.boolean().default(false), + includeTraceSpans: z.boolean().default(false), includeRateLimits: z.boolean().default(false), includeUsageData: z.boolean().default(false), alertConfig: alertConfigSchema.optional(), @@ -137,6 +138,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ levelFilter: workspaceNotificationSubscription.levelFilter, triggerFilter: workspaceNotificationSubscription.triggerFilter, includeFinalOutput: workspaceNotificationSubscription.includeFinalOutput, + includeTraceSpans: workspaceNotificationSubscription.includeTraceSpans, includeRateLimits: workspaceNotificationSubscription.includeRateLimits, includeUsageData: workspaceNotificationSubscription.includeUsageData, webhookConfig: workspaceNotificationSubscription.webhookConfig, @@ -220,7 +222,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ } } - // Encrypt webhook secret if provided let webhookConfig = data.webhookConfig || null if (webhookConfig?.secret) { const { encrypted } = await encryptSecret(webhookConfig.secret) @@ -238,6 +239,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ levelFilter: data.levelFilter, triggerFilter: data.triggerFilter, includeFinalOutput: data.includeFinalOutput, + includeTraceSpans: data.includeTraceSpans, includeRateLimits: data.includeRateLimits, includeUsageData: data.includeUsageData, alertConfig: data.alertConfig || null, @@ -263,6 +265,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ levelFilter: subscription.levelFilter, triggerFilter: subscription.triggerFilter, includeFinalOutput: subscription.includeFinalOutput, + includeTraceSpans: subscription.includeTraceSpans, includeRateLimits: subscription.includeRateLimits, includeUsageData: subscription.includeUsageData, webhookConfig: subscription.webhookConfig, diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx index 684b0394f3..93938ca5ce 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx @@ -136,6 +136,7 @@ export function NotificationSettings({ levelFilter: ['info', 'error'] as LogLevel[], triggerFilter: [...CORE_TRIGGER_TYPES] as CoreTriggerType[], includeFinalOutput: false, + includeTraceSpans: false, includeRateLimits: false, includeUsageData: false, webhookUrl: '', @@ -202,6 +203,7 @@ export function NotificationSettings({ levelFilter: ['info', 'error'], triggerFilter: [...CORE_TRIGGER_TYPES], includeFinalOutput: false, + includeTraceSpans: false, includeRateLimits: false, includeUsageData: false, webhookUrl: '', @@ -420,6 +422,8 @@ export function NotificationSettings({ levelFilter: formData.levelFilter, triggerFilter: formData.triggerFilter, includeFinalOutput: formData.includeFinalOutput, + // Trace spans only available for webhooks (too large for email/Slack) + includeTraceSpans: activeTab === 'webhook' ? formData.includeTraceSpans : false, includeRateLimits: formData.includeRateLimits, includeUsageData: formData.includeUsageData, alertConfig, @@ -471,6 +475,7 @@ export function NotificationSettings({ levelFilter: subscription.levelFilter as LogLevel[], triggerFilter: subscription.triggerFilter as CoreTriggerType[], includeFinalOutput: subscription.includeFinalOutput, + includeTraceSpans: subscription.includeTraceSpans, includeRateLimits: subscription.includeRateLimits, includeUsageData: subscription.includeUsageData, webhookUrl: subscription.webhookConfig?.url || '', @@ -826,6 +831,10 @@ export function NotificationSettings({ { const labels: Record = { includeFinalOutput: 'Final Output', + includeTraceSpans: 'Trace Spans', includeRateLimits: 'Rate Limits', includeUsageData: 'Usage Data', } const selected = [ formData.includeFinalOutput && 'includeFinalOutput', + formData.includeTraceSpans && activeTab === 'webhook' && 'includeTraceSpans', formData.includeRateLimits && 'includeRateLimits', formData.includeUsageData && 'includeUsageData', ].filter(Boolean) as string[] diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx index 81fd828842..382fe5e513 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx @@ -52,7 +52,10 @@ function isDefaultDescription(desc: string | null | undefined, workflowName: str if (!desc) return true const normalized = desc.toLowerCase().trim() return ( - normalized === '' || normalized === 'new workflow' || normalized === workflowName.toLowerCase() + normalized === '' || + normalized === 'new workflow' || + normalized === 'your first workflow - start building here!' || + normalized === workflowName.toLowerCase() ) } @@ -685,9 +688,31 @@ console.log(data);` {/* Endpoint URL (shown when agent exists) */} {existingAgent && endpoint && (
- +
+ + + + + + + {urlCopied ? 'Copied' : 'Copy'} + + +
{baseUrl.replace(/^https?:\/\//, '')}/api/a2a/serve/ @@ -696,30 +721,8 @@ console.log(data);` - - - - - - {urlCopied ? 'Copied' : 'Copy'} - -

diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx index e254c7ce1c..330cc8ac77 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx @@ -415,7 +415,7 @@ export function ChatDeploy({ > Cancel - @@ -532,7 +532,8 @@ function IdentifierInput({

) : ( isValid && - value && ( + value && + value !== originalIdentifier && (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx index b736760fea..236af44e07 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx @@ -138,10 +138,12 @@ export function McpDeploy({ const [toolName, setToolName] = useState(() => sanitizeToolName(workflowName)) const [toolDescription, setToolDescription] = useState(() => { + const normalizedDesc = workflowDescription?.toLowerCase().trim() const isDefaultDescription = !workflowDescription || workflowDescription === workflowName || - workflowDescription.toLowerCase() === 'new workflow' + normalizedDesc === 'new workflow' || + normalizedDesc === 'your first workflow - start building here!' return isDefaultDescription ? '' : workflowDescription }) @@ -193,10 +195,12 @@ export function McpDeploy({ setToolName(toolInfo.tool.toolName) const loadedDescription = toolInfo.tool.toolDescription || '' + const normalizedLoadedDesc = loadedDescription.toLowerCase().trim() const isDefaultDescription = !loadedDescription || loadedDescription === workflowName || - loadedDescription.toLowerCase() === 'new workflow' + normalizedLoadedDesc === 'new workflow' || + normalizedLoadedDesc === 'your first workflow - start building here!' setToolDescription(isDefaultDescription ? '' : loadedDescription) const schema = toolInfo.tool.parameterSchema as Record | undefined diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx index c0e6756712..98a61c1d1f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx @@ -734,7 +734,7 @@ export function DeployModal({ )} */} - + {workflowId && ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/api-keys.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/api-keys.tsx index 02147cb817..f10cff2a00 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/api-keys.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/api-keys.tsx @@ -414,7 +414,7 @@ export function ApiKeys({ onOpenChange, registerCloseHandler }: ApiKeysProps) { Cancel