onNotFound(resource.id) : undefined}
+ />
+ )
case 'generic':
return (
@@ -618,10 +627,20 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) {
interface EmbeddedLogProps {
logId: string
+ onNotFound?: () => void
}
-function EmbeddedLog({ logId }: EmbeddedLogProps) {
- const { data: log, isLoading } = useLogDetail(logId)
+function EmbeddedLog({ logId, onNotFound }: EmbeddedLogProps) {
+ const { data: log, isLoading, error } = useLogDetail(logId)
+
+ const onNotFoundRef = useRef(onNotFound)
+ onNotFoundRef.current = onNotFound
+
+ useEffect(() => {
+ if (isApiClientError(error) && error.status === 404) {
+ onNotFoundRef.current?.()
+ }
+ }, [error])
if (isLoading) return LOADING_SKELETON
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx
index fcfb08ff948..4eb7227c850 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx
@@ -128,6 +128,7 @@ export const MothershipView = memo(
previewSession={previewForActive}
genericResourceData={active.type === 'generic' ? genericResourceData : undefined}
previewContextKey={chatId}
+ onNotFound={(resourceId) => onRemoveResource('log', resourceId)}
/>
) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
index b4dccd4d4df..0799193d148 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
@@ -1420,6 +1420,18 @@ export function useChat(
const removeResource = useCallback((resourceType: MothershipResourceType, resourceId: string) => {
setResources((prev) => prev.filter((r) => !(r.type === resourceType && r.id === resourceId)))
setActiveResourceId((prev) => (prev === resourceId ? null : prev))
+
+ const persistChatId = chatIdRef.current ?? selectedChatIdRef.current
+ if (persistChatId) {
+ // boundary-raw-fetch: fire-and-forget side-effect; intentionally avoids requestJson's response parsing/throw semantics so a transient failure cannot interrupt the caller
+ fetch('/api/mothership/chat/resources', {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ chatId: persistChatId, resourceType, resourceId }),
+ }).catch((err) => {
+ logger.warn('Failed to persist resource removal', err)
+ })
+ }
}, [])
const reorderResources = useCallback((newOrder: MothershipResource[]) => {
diff --git a/apps/sim/hooks/queries/logs.ts b/apps/sim/hooks/queries/logs.ts
index bd5b0e5e695..d2f4bbfa4e9 100644
--- a/apps/sim/hooks/queries/logs.ts
+++ b/apps/sim/hooks/queries/logs.ts
@@ -7,6 +7,7 @@ import {
useQuery,
useQueryClient,
} from '@tanstack/react-query'
+import { isApiClientError } from '@/lib/api/client/errors'
import { requestJson } from '@/lib/api/client/request'
import {
cancelWorkflowExecutionContract,
@@ -194,6 +195,8 @@ export function useLogDetail(logId: string | undefined, options?: UseLogDetailOp
enabled: Boolean(logId) && (options?.enabled ?? true),
refetchInterval: options?.refetchInterval ?? false,
staleTime: 30 * 1000,
+ retry: (failureCount, err) =>
+ !(isApiClientError(err) && err.status === 404) && failureCount < 3,
})
}
diff --git a/apps/sim/lib/copilot/resources/extraction.ts b/apps/sim/lib/copilot/resources/extraction.ts
index 29ca644a21a..6cba5a3bee0 100644
--- a/apps/sim/lib/copilot/resources/extraction.ts
+++ b/apps/sim/lib/copilot/resources/extraction.ts
@@ -7,7 +7,6 @@ import {
FunctionExecute,
GenerateImage,
GenerateVisualization,
- GetWorkflowLogs,
Knowledge,
KnowledgeBase,
UserTable,
@@ -30,7 +29,6 @@ const RESOURCE_TOOL_NAMES: Set = new Set([
Knowledge.id,
GenerateVisualization.id,
GenerateImage.id,
- GetWorkflowLogs.id,
])
export function isResourceToolName(toolName: string): boolean {
@@ -214,19 +212,6 @@ export function extractResourcesFromToolResult(
return resources
}
- case GetWorkflowLogs.id: {
- const entries = Array.isArray(output) ? output : Array.isArray(result.data) ? result.data : []
- const resources: ChatResource[] = []
- for (const entry of entries) {
- const rec = asRecord(entry)
- const logId = rec.id as string | undefined
- if (logId) {
- resources.push({ type: 'log', id: logId, title: 'Log' })
- }
- }
- return resources
- }
-
default:
return []
}