Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0b9019d
v0.6.23: MCP fixes, remove local state in favor of server state, moth…
waleedlatif1 Apr 4, 2026
a54dcbe
v0.6.24: copilot feedback wiring, captcha fixes
waleedlatif1 Apr 4, 2026
28af223
v0.6.25: cloudwatch, cloudformation, live kb sync, linear fixes, post…
waleedlatif1 Apr 5, 2026
d889f32
v0.6.26: ui improvements, multiple response blocks, docx previews, ol…
waleedlatif1 Apr 5, 2026
316bc8c
v0.6.27: new triggers, mothership improvements, files archive, queuei…
waleedlatif1 Apr 7, 2026
3f508e4
v0.6.28: new docs, delete confirmation standardization, dagster integ…
waleedlatif1 Apr 7, 2026
d6ec115
v0.6.29: login improvements, posthog telemetry (#4026)
TheodoreSpeaks Apr 7, 2026
d7da35b
v0.6.30: slack trigger enhancements, connectors performance improveme…
waleedlatif1 Apr 8, 2026
cf233bb
v0.6.31: elevenlabs voice, trigger.dev fixes, cloud whitelabeling for…
waleedlatif1 Apr 8, 2026
f8f3758
v0.6.32: BYOK fixes, ui improvements, cloudwatch tools, jsm tools ext…
waleedlatif1 Apr 9, 2026
3c8bb40
v0.6.33: polling improvements, jsm forms tools, credentials reactquer…
waleedlatif1 Apr 9, 2026
d33acf4
v0.6.34: trigger.dev fixes, CI speedup, atlassian error extractor
waleedlatif1 Apr 9, 2026
4f40c4c
v0.6.35: additional jira fields, HITL docs, logs cleanup efficiency
waleedlatif1 Apr 10, 2026
cbfab1c
v0.6.36: new chunkers, sockets state machine, google sheets/drive/cal…
waleedlatif1 Apr 11, 2026
4309d06
v0.6.37: audit logs page, isolated-vm worker rotation, permission gro…
waleedlatif1 Apr 12, 2026
8b57476
v0.6.38: models page
waleedlatif1 Apr 12, 2026
e3d0e74
v0.6.39: billing fixes, tools audit, landing fix
waleedlatif1 Apr 13, 2026
0ac0539
v0.6.40: mothership tool loop, new skills, agiloft, STS, IAM integrat…
waleedlatif1 Apr 14, 2026
3838b6e
v0.6.41: webhooks fix, workers removal
waleedlatif1 Apr 14, 2026
fc07922
v0.6.42: mothership nested file reads, search modal improvements
waleedlatif1 Apr 14, 2026
3a1b1a8
v0.6.43: mothership billing idempotency, env var resolution fixes
waleedlatif1 Apr 14, 2026
46ffc49
v0.6.44: streamdown, mothership intelligence, excel extension
waleedlatif1 Apr 15, 2026
010435c
v0.6.45: superagent, csp, brightdata integration, gemini response for…
Sg312 Apr 15, 2026
c0bc62c
Merge pull request #4190 from simstudioai/staging
icecrasher321 Apr 16, 2026
387cc97
v0.6.46: mothership queueing, web vitals
waleedlatif1 Apr 16, 2026
2dbc7fd
v0.6.47: files focusing, documentation, opus 4.7
waleedlatif1 Apr 16, 2026
8a50f18
v0.6.48: import csv into tables, subflow fixes, CSP updates
waleedlatif1 Apr 16, 2026
dcf3302
v0.6.49: deploy sockets event, resolver, logs improvements, monday.co…
waleedlatif1 Apr 17, 2026
bc09865
v0.6.50: ppt/doc/pdf worker isolation, docs, chat, sidebar improvements
icecrasher321 Apr 18, 2026
5f56e46
v0.6.51: tables improvements, billing fixes, 404 pages, code hygiene
waleedlatif1 Apr 20, 2026
ca3bbf1
v0.6.52: data retention, docs updates, slack manifest generator, secu…
waleedlatif1 Apr 22, 2026
bbf400f
v0.6.53: permissions groups migration, docs updates
waleedlatif1 Apr 22, 2026
7c619e7
Merge pull request #4261 from simstudioai/staging
icecrasher321 Apr 22, 2026
64cfda5
v0.6.54: mothership tracing, db pool size increase
icecrasher321 Apr 22, 2026
7ca736a
v0.6.55: standardize monorepo conventions, api key hash, thinking tex…
waleedlatif1 Apr 23, 2026
6066fc1
v0.6.56: data retention improvements, tables column double click resi…
waleedlatif1 Apr 24, 2026
3422f64
Merge pull request #4285 from simstudioai/staging
waleedlatif1 Apr 24, 2026
595c4c3
Merge pull request #4293 from simstudioai/staging
TheodoreSpeaks Apr 24, 2026
d6c1bc2
v0.6.58: queue abort state machine improvement, contributing guide
icecrasher321 Apr 25, 2026
58a3ae2
v0.6.59: gpt 5.5, security hardening, parallel subagents rendering
icecrasher321 Apr 27, 2026
489f2d3
v0.6.60: copilot security improvements, slack canvas ops, retention j…
icecrasher321 Apr 27, 2026
6aa3fe3
v0.6.61: SAP integration, live URLs for browser use, 5xx error catego…
icecrasher321 Apr 29, 2026
ecbf5e5
Merge pull request #4342 from simstudioai/staging
TheodoreSpeaks Apr 29, 2026
2aaf2b7
v0.6.62: firecrawl parse, new gmail tools, trace improvements, tool f…
waleedlatif1 May 2, 2026
d445b9c
v0.6.63: knowledgebase UI, folder search in mothership
waleedlatif1 May 2, 2026
4bc6a17
v0.6.64: table limits env vars, workspace files improvements, integra…
waleedlatif1 May 3, 2026
5be12f8
v0.6.65: memory fix, image uploads in files
waleedlatif1 May 3, 2026
4253e57
v0.6.66: child trace spans, reranker controls, attachment previews, l…
waleedlatif1 May 5, 2026
8d6b615
v0.6.67: VFS upload fix, posthog/copilot correlation, exa date filter…
TheodoreSpeaks May 5, 2026
efcd51a
v0.6.68: atlassian service accounts, 30 day wait block, markdown rend…
waleedlatif1 May 6, 2026
8d934f3
v0.6.69: security hardening, nextjs upgrade, SAP Concur, Emailbison i…
waleedlatif1 May 7, 2026
5ea80a8
v0.6.70: legacy workflow sanitization
icecrasher321 May 7, 2026
3cc581e
v0.6.71: build error fix
icecrasher321 May 7, 2026
273e608
Merge pull request #4496 from simstudioai/staging
TheodoreSpeaks May 7, 2026
94389b5
feat(deploy-modal): add logs section + URL-driven log selection
stylessh May 8, 2026
9cce6fd
refactor(logs): extract status utilities to @/lib/logs/status
stylessh May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions apps/sim/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata, Viewport } from 'next'
import Script from 'next/script'
import { PublicEnvScript } from 'next-runtime-env'
import { NuqsAdapter } from 'nuqs/adapters/next/app'
import { BrandedLayout } from '@/components/branded-layout'
import { PostHogProvider } from '@/app/_shell/providers/posthog-provider'
import { generateBrandedMetadata, generateThemeCSS } from '@/ee/whitelabeling'
Expand Down Expand Up @@ -258,11 +259,13 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<PostHogProvider>
<ThemeProvider>
<QueryProvider>
<SessionProvider>
<TooltipProvider>
<BrandedLayout>{children}</BrandedLayout>
</TooltipProvider>
</SessionProvider>
<NuqsAdapter>
<SessionProvider>
<TooltipProvider>
<BrandedLayout>{children}</BrandedLayout>
</TooltipProvider>
</SessionProvider>
</NuqsAdapter>
</QueryProvider>
</ThemeProvider>
</PostHogProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type { WorkflowLogRow } from '@/lib/api/contracts/logs'
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
import { cn } from '@/lib/core/utils/cn'
import { filterHiddenOutputKeys } from '@/lib/logs/execution/trace-spans/trace-spans'
import { getDisplayStatus, StatusBadge, TriggerBadge } from '@/lib/logs/status'
import type { TraceSpan } from '@/lib/logs/types'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import {
Expand All @@ -38,9 +39,6 @@ import {
DELETED_WORKFLOW_COLOR,
DELETED_WORKFLOW_LABEL,
formatDate,
getDisplayStatus,
StatusBadge,
TriggerBadge,
} from '@/app/workspace/[workspaceId]/logs/utils'
import { useCodeViewerFeatures } from '@/hooks/use-code-viewer'
import { usePermissionConfig } from '@/hooks/use-permission-config'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ import { Badge, buttonVariants, Loader } from '@/components/emcn'
import type { WorkflowLogSummary } from '@/lib/api/contracts/logs'
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
import { cn } from '@/lib/core/utils/cn'
import { getDisplayStatus, StatusBadge, TriggerBadge } from '@/lib/logs/status'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import {
DELETED_WORKFLOW_COLOR,
DELETED_WORKFLOW_LABEL,
formatDate,
getDisplayStatus,
LOG_COLUMNS,
StatusBadge,
TriggerBadge,
} from '@/app/workspace/[workspaceId]/logs/utils'

const LOG_ROW_HEIGHT = 44 as const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import {
import { cn } from '@/lib/core/utils/cn'
import { hasActiveFilters } from '@/lib/logs/filters'
import { getTriggerOptions } from '@/lib/logs/get-trigger-options'
import { type LogStatus, STATUS_CONFIG } from '@/lib/logs/status'
import { captureEvent } from '@/lib/posthog/client'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import { type LogStatus, STATUS_CONFIG } from '@/app/workspace/[workspaceId]/logs/utils'
import { getBlock } from '@/blocks/registry'
import { useFolderMap } from '@/hooks/queries/folders'
import { useWorkflows } from '@/hooks/queries/workflows'
Expand Down
74 changes: 55 additions & 19 deletions apps/sim/app/workspace/[workspaceId]/logs/logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'r
import { formatDuration } from '@sim/utils/formatting'
import { useQueryClient } from '@tanstack/react-query'
import { useParams } from 'next/navigation'
import { parseAsString, useQueryState } from 'nuqs'
import { useShallow } from 'zustand/react/shallow'
import {
Bell,
Expand Down Expand Up @@ -280,10 +281,9 @@ export default function Logs() {
selectedLogId: null,
isSidebarOpen: false,
})
const [pendingExecutionId, setPendingExecutionId] = useState<string | null>(() =>
typeof window !== 'undefined'
? new URLSearchParams(window.location.search).get('executionId')
: null
const [executionIdParam, setExecutionIdParam] = useQueryState(
'executionId',
parseAsString.withOptions({ history: 'replace' })
)

const [searchQuery, setSearchQuery] = useState(() => {
Expand All @@ -299,6 +299,7 @@ export default function Logs() {
const logsRef = useRef<WorkflowLogSummary[]>([])
const selectedLogIndexRef = useRef(-1)
const selectedLogIdRef = useRef<string | null>(null)
const isSidebarOpenRef = useRef(false)
const shouldScrollIntoViewRef = useRef(false)
const logsRefetchRef = useRef<() => void>(() => {})
const activeLogRefetchRef = useRef<() => void>(() => {})
Expand Down Expand Up @@ -412,6 +413,7 @@ export default function Logs() {
logsRef.current = logs
selectedLogIndexRef.current = selectedLogIndex
selectedLogIdRef.current = selectedLogId
isSidebarOpenRef.current = isSidebarOpen
logsRefetchRef.current = logsQuery.refetch
activeLogRefetchRef.current = selectedDetailQuery.refetch
logsQueryRef.current = {
Expand All @@ -420,18 +422,26 @@ export default function Logs() {
fetchNextPage: logsQuery.fetchNextPage,
}

const deepLinkQuery = useLogByExecutionId(workspaceId, pendingExecutionId)
const deepLinkQuery = useLogByExecutionId(workspaceId, executionIdParam)
const prevExecutionIdParamRef = useRef<string | null>(executionIdParam)

useEffect(() => {
if (!pendingExecutionId) return
const prev = prevExecutionIdParamRef.current
prevExecutionIdParamRef.current = executionIdParam

if (!executionIdParam) {
if (prev && isSidebarOpenRef.current) {
dispatch({ type: 'CLOSE_SIDEBAR' })
}
return
}
const resolvedId = deepLinkQuery.data?.id
if (resolvedId) {
if (resolvedId && (resolvedId !== selectedLogIdRef.current || !isSidebarOpenRef.current)) {
dispatch({ type: 'TOGGLE_LOG', logId: resolvedId })
Comment on lines 438 to 440
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Deep-link won't reopen sidebar for same execution ID after manual close

The guard resolvedId !== selectedLogIdRef.current prevents dispatching TOGGLE_LOG when the IDs match. Because selectedLogIdRef retains the last-selected log id even after handleCloseSidebar (which only changes isSidebarOpen, not the selected id), navigating back to /logs?executionId=<X> after manually closing the panel for log X will leave the sidebar closed — the effect sees resolvedId === selectedLogIdRef.current and silently no-ops. The guard is necessary to avoid double-opening on data refetch, but it needs an additional check: if the sidebar is currently closed it should still open.

setPendingExecutionId(null)
} else if (deepLinkQuery.isError) {
setPendingExecutionId(null)
setExecutionIdParam(null)
}
}, [pendingExecutionId, deepLinkQuery.data, deepLinkQuery.isError])
}, [executionIdParam, deepLinkQuery.data, deepLinkQuery.isError, setExecutionIdParam])

useEffect(() => {
const timers = refreshTimersRef.current
Expand All @@ -445,31 +455,50 @@ export default function Logs() {
setStoreSearchQuery(debouncedSearchQuery)
}, [debouncedSearchQuery, setStoreSearchQuery])

const handleLogClick = useCallback((rowId: string) => {
dispatch({ type: 'TOGGLE_LOG', logId: rowId })
}, [])
const handleLogClick = useCallback(
(rowId: string) => {
const isClosing = selectedLogIdRef.current === rowId && isSidebarOpenRef.current
dispatch({ type: 'TOGGLE_LOG', logId: rowId })
if (isClosing) {
setExecutionIdParam(null)
} else {
const log = logsRef.current.find((l) => l.id === rowId)
setExecutionIdParam(log?.executionId ?? null)
}
},
[setExecutionIdParam]
)

const handleNavigateNext = useCallback(() => {
const idx = selectedLogIndexRef.current
const currentLogs = logsRef.current
if (idx >= 0 && idx < currentLogs.length - 1) {
const next = currentLogs[idx + 1]
shouldScrollIntoViewRef.current = true
dispatch({ type: 'SELECT_LOG', logId: currentLogs[idx + 1].id })
dispatch({ type: 'SELECT_LOG', logId: next.id })
if (isSidebarOpenRef.current) {
setExecutionIdParam(next.executionId ?? null)
}
}
}, [])
}, [setExecutionIdParam])

const handleNavigatePrev = useCallback(() => {
const idx = selectedLogIndexRef.current
if (idx > 0) {
const prev = logsRef.current[idx - 1]
shouldScrollIntoViewRef.current = true
dispatch({ type: 'SELECT_LOG', logId: logsRef.current[idx - 1].id })
dispatch({ type: 'SELECT_LOG', logId: prev.id })
if (isSidebarOpenRef.current) {
setExecutionIdParam(prev.executionId ?? null)
}
}
}, [])
}, [setExecutionIdParam])

const handleCloseSidebar = useCallback(() => {
dispatch({ type: 'CLOSE_SIDEBAR' })
setExecutionIdParam(null)
activeLogTabRef.current = 'overview'
}, [])
}, [setExecutionIdParam])

const handleActiveTabChange = useCallback((tab: string) => {
activeLogTabRef.current = tab
Expand Down Expand Up @@ -722,13 +751,20 @@ export default function Logs() {

if (e.key === 'Enter' && selectedLogIdRef.current) {
e.preventDefault()
const willOpen = !isSidebarOpenRef.current
dispatch({ type: 'TOGGLE_SIDEBAR' })
if (willOpen) {
const log = logsRef.current.find((l) => l.id === selectedLogIdRef.current)
setExecutionIdParam(log?.executionId ?? null)
} else {
setExecutionIdParam(null)
}
}
}

window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [handleNavigateNext, handleNavigatePrev])
}, [handleNavigateNext, handleNavigatePrev, setExecutionIdParam])

const handleCloseContextMenu = useCallback(() => setContextMenuOpen(false), [])
const handleOpenNotificationSettings = useCallback(() => setIsNotificationSettingsOpen(true), [])
Expand Down
128 changes: 0 additions & 128 deletions apps/sim/app/workspace/[workspaceId]/logs/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import React from 'react'
import { formatDuration } from '@sim/utils/formatting'
import { format } from 'date-fns'
import { Badge } from '@/components/emcn'
import type { WorkflowLogDetail } from '@/lib/api/contracts/logs'
import { getIntegrationMetadata } from '@/lib/logs/get-trigger-options'
import { getBlock } from '@/blocks/registry'
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'

export const LOG_COLUMNS = {
workflow: { width: 'w-[22%]', minWidth: 'min-w-[140px]', label: 'Workflow' },
Expand All @@ -19,129 +14,6 @@ export const LOG_COLUMNS = {
export const DELETED_WORKFLOW_LABEL = 'Deleted Workflow'
export const DELETED_WORKFLOW_COLOR = 'var(--text-tertiary)'

export type LogStatus = 'error' | 'pending' | 'running' | 'info' | 'cancelled' | 'cancelling'

/**
* Maps raw status string to LogStatus for display.
* @param status - Raw status from API
* @returns Normalized LogStatus value
*/
export function getDisplayStatus(status: string | null | undefined): LogStatus {
switch (status) {
case 'running':
return 'running'
case 'pending':
return 'pending'
case 'cancelling':
return 'cancelling'
case 'cancelled':
return 'cancelled'
case 'failed':
return 'error'
default:
return 'info'
}
}

export const STATUS_CONFIG: Record<
LogStatus,
{
variant: React.ComponentProps<typeof Badge>['variant']
label: string
color: string
/** Whether this status appears as a filter option. Intermediary states (e.g. cancelling) are excluded. */
filterable: boolean
}
> = {
error: { variant: 'red', label: 'Error', color: 'var(--text-error)', filterable: true },
pending: { variant: 'amber', label: 'Pending', color: '#f59e0b', filterable: true },
running: { variant: 'amber', label: 'Running', color: '#f59e0b', filterable: true },
cancelling: { variant: 'amber', label: 'Cancelling...', color: '#f59e0b', filterable: false },
cancelled: { variant: 'orange', label: 'Cancelled', color: '#f97316', filterable: true },
info: {
variant: 'gray',
label: 'Info',
color: 'var(--terminal-status-info-color)',
filterable: true,
},
}

const TRIGGER_VARIANT_MAP: Record<string, React.ComponentProps<typeof Badge>['variant']> = {
manual: 'gray-secondary',
api: 'blue',
schedule: 'green',
chat: 'purple',
webhook: 'orange',
mcp: 'cyan',
a2a: 'teal',
copilot: 'pink',
mothership: 'pink',
workflow: 'blue-secondary',
}

interface StatusBadgeProps {
status: LogStatus
}

/**
* Renders a colored badge indicating log execution status.
* @param props - Component props containing the status
* @returns A Badge with dot indicator and status label
*/
export function StatusBadge({ status }: StatusBadgeProps) {
const config = STATUS_CONFIG[status]
return React.createElement(
Badge,
{ variant: config.variant, dot: true, size: 'sm' },
config.label
)
}

interface TriggerBadgeProps {
trigger: string
}

/**
* Renders a colored badge indicating the workflow trigger type.
* Core triggers display with their designated colors; integrations show with icons.
* @param props - Component props containing the trigger type
* @returns A Badge with appropriate styling for the trigger type
*/
export function TriggerBadge({ trigger }: TriggerBadgeProps) {
const metadata = getIntegrationMetadata(trigger)
const isIntegration = !(CORE_TRIGGER_TYPES as readonly string[]).includes(trigger)
const block = isIntegration ? getBlock(trigger) : null
const IconComponent = block?.icon

const coreVariant = TRIGGER_VARIANT_MAP[trigger]
if (coreVariant) {
return React.createElement(
Badge,
{ variant: coreVariant, size: 'sm', className: 'whitespace-nowrap' },
metadata.label
)
}

if (IconComponent) {
return React.createElement(
Badge,
{
variant: 'gray-secondary',
size: 'sm',
icon: IconComponent,
className: 'whitespace-nowrap',
},
metadata.label
)
}

return React.createElement(
Badge,
{ variant: 'gray-secondary', size: 'sm', className: 'whitespace-nowrap' },
metadata.label
)
}

interface LogWithDuration {
totalDurationMs?: number | string
duration?: number | string
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { Logs } from './logs'
export { Versions } from './versions'
Loading
Loading