From e04a4fae47f20cb2629d104967953ec7f1bb6745 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 13 Jul 2026 19:26:15 -0700 Subject: [PATCH 1/3] fix(settings): restore header shell on workspace credit-usage page (#5663) --- .../settings/billing/credit-usage/layout.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 apps/sim/app/workspace/[workspaceId]/settings/billing/credit-usage/layout.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/settings/billing/credit-usage/layout.tsx b/apps/sim/app/workspace/[workspaceId]/settings/billing/credit-usage/layout.tsx new file mode 100644 index 00000000000..c19caf648a1 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/settings/billing/credit-usage/layout.tsx @@ -0,0 +1,19 @@ +import { + SettingsHeaderProvider, + SettingsHeaderShell, +} from '@/app/workspace/[workspaceId]/settings/components/settings-header/settings-header' + +/** + * Credit usage is a static route outside `[section]`, so it does not inherit + * `SettingsSectionLayout`'s chrome. `CreditUsageView` and its loading fallback + * render through `SettingsPanel`, which only registers header config into the + * `SettingsHeaderProvider` context — without this shell the page has no header + * bar, title, or scroll region. + */ +export default function CreditUsageLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} From 09d58ccfd8571ba8ed12465599e7a43176679dfe Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 13 Jul 2026 19:39:36 -0700 Subject: [PATCH 2/3] feat(chat): show block display names and brand icons for schema reads (#5666) --- .../components/agent-group/agent-group.tsx | 1 + .../components/agent-group/tool-call-item.tsx | 28 +++++++++++++++-- .../copilot/tools/client/read-block.test.ts | 30 +++++++++++++++++++ .../lib/copilot/tools/client/read-block.ts | 23 ++++++++++++++ .../copilot/tools/client/store-utils.test.ts | 29 +++++++++++++++++- .../lib/copilot/tools/client/store-utils.ts | 4 +++ apps/sim/vitest.setup.ts | 1 + 7 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 apps/sim/lib/copilot/tools/client/read-block.test.ts create mode 100644 apps/sim/lib/copilot/tools/client/read-block.ts diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx index 9a62f40f7cd..747b684ee2d 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx @@ -130,6 +130,7 @@ export function AgentGroup({ toolName={item.data.toolName} displayTitle={item.data.displayTitle} status={item.data.status} + params={item.data.params} streamingArgs={item.data.streamingArgs} /> ) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx index 90776627f57..e0afc85422e 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx @@ -1,7 +1,9 @@ import { useMemo } from 'react' import { ShimmerText } from '@/components/ui' -import { WorkspaceFile } from '@/lib/copilot/generated/tool-catalog-v1' +import { Read as ReadTool, WorkspaceFile } from '@/lib/copilot/generated/tool-catalog-v1' +import { getReadTargetBlock } from '@/lib/copilot/tools/client/read-block' import { getToolCompletedTitle } from '@/lib/copilot/tools/tool-display' +import { getBareIconStyle } from '@/blocks/icon-color' import type { ToolCallStatus } from '../../../../types' import { resolveToolDisplayState } from '../../utils' @@ -25,6 +27,7 @@ interface ToolCallItemProps { toolName: string displayTitle: string status: ToolCallStatus + params?: Record streamingArgs?: string } @@ -33,8 +36,22 @@ interface ToolCallItemProps { * static label once terminal. For `workspace_file` the title is derived live * from the streaming args; because that path bypasses the completed-title * rewrite in `toToolData`, the past-tense flip is applied here on success. + * A `read` of a block or integration schema shows the block's brand icon + * inline next to its display name (e.g. the Gmail logo before "Read Gmail"). */ -export function ToolCallItem({ toolName, displayTitle, status, streamingArgs }: ToolCallItemProps) { +export function ToolCallItem({ + toolName, + displayTitle, + status, + params, + streamingArgs, +}: ToolCallItemProps) { + const readBlock = useMemo(() => { + if (toolName !== ReadTool.id) return undefined + const path = params?.path + return typeof path === 'string' ? getReadTargetBlock(path) : undefined + }, [toolName, params]) + const liveWorkspaceFileTitle = useMemo(() => { if (toolName !== WorkspaceFile.id || !streamingArgs) return null const titleMatch = streamingArgs.match(/"title"\s*:\s*"([^"]+)"/) @@ -71,8 +88,13 @@ export function ToolCallItem({ toolName, displayTitle, status, streamingArgs }: ? (getToolCompletedTitle(liveTitle) ?? liveTitle) : liveTitle + const BlockIcon = readBlock?.icon + return ( -
+
+ {BlockIcon && ( + + )} {isExecuting ? ( {title} diff --git a/apps/sim/lib/copilot/tools/client/read-block.test.ts b/apps/sim/lib/copilot/tools/client/read-block.test.ts new file mode 100644 index 00000000000..a3da77c0db5 --- /dev/null +++ b/apps/sim/lib/copilot/tools/client/read-block.test.ts @@ -0,0 +1,30 @@ +/** + * @vitest-environment node + */ +import { describe, expect, it, vi } from 'vitest' +import { getReadTargetBlock } from '@/lib/copilot/tools/client/read-block' + +const gmailBlock = { type: 'gmail_v2', name: 'Gmail', icon: () => null } + +vi.mock('@/blocks/registry', () => ({ + getBlock: vi.fn((type: string) => (type === 'gmail_v2' ? gmailBlock : undefined)), + getLatestBlock: vi.fn((baseType: string) => (baseType === 'gmail' ? gmailBlock : undefined)), +})) + +describe('getReadTargetBlock', () => { + it('resolves a block schema read to its block', () => { + expect(getReadTargetBlock('components/blocks/gmail_v2.json')?.name).toBe('Gmail') + }) + + it('resolves integration operation and service-directory reads to the latest service block', () => { + expect(getReadTargetBlock('components/integrations/gmail/send.json')?.name).toBe('Gmail') + expect(getReadTargetBlock('components/integrations/gmail')?.name).toBe('Gmail') + }) + + it('returns undefined for unknown blocks and non-component paths', () => { + expect(getReadTargetBlock('components/blocks/unknown_block.json')).toBeUndefined() + expect(getReadTargetBlock('workflows/My Workflow/meta.json')).toBeUndefined() + expect(getReadTargetBlock('files/gmail_v2.json')).toBeUndefined() + expect(getReadTargetBlock(undefined)).toBeUndefined() + }) +}) diff --git a/apps/sim/lib/copilot/tools/client/read-block.ts b/apps/sim/lib/copilot/tools/client/read-block.ts new file mode 100644 index 00000000000..c4faf4e8f8e --- /dev/null +++ b/apps/sim/lib/copilot/tools/client/read-block.ts @@ -0,0 +1,23 @@ +import { getBlock, getLatestBlock } from '@/blocks/registry' +import type { BlockConfig } from '@/blocks/types' + +/** + * Resolves the block a copilot `read` call targets when the path is a + * component schema — `components/blocks/{type}.json` or + * `components/integrations/{service}/{operation}.json` — so tool rows can show + * the block's display name and brand icon instead of the raw type id + * (e.g. "Gmail" instead of `gmail_v2`). Returns undefined for every other + * path, leaving the generic read-target labeling untouched. + */ +export function getReadTargetBlock(path: string | undefined): BlockConfig | undefined { + if (!path) return undefined + const segments = path.trim().split('/').filter(Boolean) + if (segments[0] !== 'components' || segments.length < 3) return undefined + if (segments[1] === 'blocks' && segments.length === 3) { + return getBlock(segments[2].replace(/\.json$/, '')) + } + if (segments[1] === 'integrations') { + return getLatestBlock(segments[2]) + } + return undefined +} diff --git a/apps/sim/lib/copilot/tools/client/store-utils.test.ts b/apps/sim/lib/copilot/tools/client/store-utils.test.ts index a8873d8bb4f..2fd6e54bcc6 100644 --- a/apps/sim/lib/copilot/tools/client/store-utils.test.ts +++ b/apps/sim/lib/copilot/tools/client/store-utils.test.ts @@ -2,11 +2,18 @@ * @vitest-environment node */ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { Read as ReadTool } from '@/lib/copilot/generated/tool-catalog-v1' import { resolveToolDisplay } from './store-utils' import { ClientToolCallState } from './tool-call-state' +const gmailBlock = { type: 'gmail_v2', name: 'Gmail', icon: () => null } + +vi.mock('@/blocks/registry', () => ({ + getBlock: vi.fn((type: string) => (type === 'gmail_v2' ? gmailBlock : undefined)), + getLatestBlock: vi.fn((baseType: string) => (baseType === 'gmail' ? gmailBlock : undefined)), +})) + describe('resolveToolDisplay', () => { it('uses a friendly label for internal respond tools', () => { expect(resolveToolDisplay('respond', ClientToolCallState.executing)?.text).toBe( @@ -120,6 +127,26 @@ describe('resolveToolDisplay', () => { ).toBe('Read style details for deck.pptx') }) + it('shows the block display name for block and integration schema reads', () => { + expect( + resolveToolDisplay(ReadTool.id, ClientToolCallState.success, { + path: 'components/blocks/gmail_v2.json', + })?.text + ).toBe('Read Gmail') + + expect( + resolveToolDisplay(ReadTool.id, ClientToolCallState.executing, { + path: 'components/integrations/gmail/send.json', + })?.text + ).toBe('Reading Gmail') + + expect( + resolveToolDisplay(ReadTool.id, ClientToolCallState.success, { + path: 'components/blocks/unknown_block.json', + })?.text + ).toBe('Read unknown_block') + }) + it('falls back to a humanized tool label for generic tools', () => { expect(resolveToolDisplay('deploy_api', ClientToolCallState.success)?.text).toBe( 'Executed Deploy Api' diff --git a/apps/sim/lib/copilot/tools/client/store-utils.ts b/apps/sim/lib/copilot/tools/client/store-utils.ts index ec4c1190b2f..7364bbc1f87 100644 --- a/apps/sim/lib/copilot/tools/client/store-utils.ts +++ b/apps/sim/lib/copilot/tools/client/store-utils.ts @@ -4,6 +4,7 @@ import { FileText } from 'lucide-react' import { Read as ReadTool } from '@/lib/copilot/generated/tool-catalog-v1' import { VFS_DIR_TO_RESOURCE } from '@/lib/copilot/resources/types' import { isToolHiddenInUi } from '@/lib/copilot/tools/client/hidden-tools' +import { getReadTargetBlock } from '@/lib/copilot/tools/client/read-block' import { ClientToolCallState } from '@/lib/copilot/tools/client/tool-call-state' import { decodeVfsSegment } from '@/lib/copilot/vfs/path-utils' @@ -98,6 +99,9 @@ function decodeVfsSegmentSafe(segment: string): string { function describeReadTarget(path: string | undefined): string | undefined { if (!path) return undefined + const block = getReadTargetBlock(path) + if (block) return block.name + const segments = path .split('/') .map((segment) => segment.trim()) diff --git a/apps/sim/vitest.setup.ts b/apps/sim/vitest.setup.ts index b9a9ae7d14a..ca6b4fc3922 100644 --- a/apps/sim/vitest.setup.ts +++ b/apps/sim/vitest.setup.ts @@ -88,6 +88,7 @@ vi.mock('@/blocks/registry', () => ({ outputs: {}, })), getAllBlocks: vi.fn(() => ({})), + getLatestBlock: vi.fn(() => undefined), })) vi.mock('@trigger.dev/sdk', () => ({ From 78777132bd4ca251f830e0519b866e4543ef8a50 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 13 Jul 2026 19:39:54 -0700 Subject: [PATCH 3/3] improvement(chat): hide the agent-group viewport scrollbar (#5664) --- .../message-content/components/agent-group/agent-group.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx index 747b684ee2d..cf02091848c 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx @@ -262,7 +262,10 @@ function BoundedViewport({ children, isStreaming }: BoundedViewportProps) { return (
-
+
{children}
{hasOverflow && (