diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 7985328c08..2417e6acb5 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -91,7 +91,12 @@ export function AgentPhoneIcon(props: SVGProps) { export function CrowdStrikeIcon(props: SVGProps) { return ( - + ) { export function FirecrawlIcon(props: SVGProps) { return ( - + ) { export function SerperIcon(props: SVGProps) { return ( - + ) { export function NotionIcon(props: SVGProps) { return ( - + @@ -1171,27 +1176,13 @@ export function GrafanaIcon(props: SVGProps) { const gradientId = `grafana_gradient_${id}` return ( - + - + @@ -1504,7 +1495,7 @@ export function ProspeoIcon(props: SVGProps) { {...props} width='1em' height='1em' - viewBox='0 0 32 32' + viewBox='1.17 1.178 29.66 29.643' fill='none' xmlns='http://www.w3.org/2000/svg' > @@ -2184,27 +2175,8 @@ export function MistralIcon(props: SVGProps) { export function BrainIcon(props: SVGProps) { return ( - - - - - - - - - - + + ) } @@ -2256,14 +2228,19 @@ export function BrandfetchIcon(props: SVGProps) { export function BrightDataIcon(props: SVGProps) { return ( - + ) @@ -2346,8 +2323,8 @@ export function ExtendIcon(props: SVGProps) { export function EvernoteIcon(props: SVGProps) { return ( - - + + ) } @@ -2542,7 +2519,7 @@ export function TelegramIcon(props: SVGProps) { ) { export function QuiverIcon(props: SVGProps) { return ( - + ) { } export function MicrosoftOneDriveIcon(props: SVGProps) { + const id = useId() return ( - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } @@ -4413,37 +4569,47 @@ export function Neo4jIcon(props: SVGProps) { export function CalendlyIcon(props: SVGProps) { return ( - - - - - - - - + + + + + ) } @@ -4735,8 +4901,8 @@ export function ZendeskIcon(props: SVGProps) { export function ZoomIcon(props: SVGProps) { return ( ) } @@ -4747,7 +4913,7 @@ export function ZoomInfoIcon(props: SVGProps) { return (
{trailing}
} ) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx index fe6a6bfd3d..065385a9f0 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx @@ -77,7 +77,13 @@ export function resolveCellRender({ // Value wins over pending-upstream: a finished column stays finished even // while other blocks in the group are still running. An empty string is not // a value — it falls through so a completed enrichment can show "Not found". - if (!isEmpty) return { kind: 'value', text: stringifyValue(value) } + // A value that's wholly a resource/URL string renders as a chip/link (any + // column type — workflow output is free-form); otherwise the plain `value` + // kind keeps the typewriter reveal for streaming text. + if (!isEmpty) { + const text = stringifyValue(value) + return resolveLinkKind(text, currentWorkspaceId) ?? { kind: 'value', text } + } if (inFlight && !(groupHasBlockErrors && !blockRunning)) { // A `pending` cell whose jobId starts with `paused-` is mid-pause @@ -109,21 +115,7 @@ export function resolveCellRender({ if (column.type === 'date') return { kind: 'date', text: String(value) } if (column.type === 'string') { const text = stringifyValue(value) - if (currentWorkspaceId) { - const resource = extractSimResourceInfo(text) - if (resource && resource.workspaceId === currentWorkspaceId) { - return { - kind: 'sim-resource', - workspaceId: resource.workspaceId, - resourceType: resource.resourceType, - resourceId: resource.resourceId, - href: resource.href, - } - } - } - const urlInfo = extractUrlInfo(text) - if (urlInfo) return { kind: 'url', text, href: urlInfo.href, domain: urlInfo.domain } - return { kind: 'text', text } + return resolveLinkKind(text, currentWorkspaceId) ?? { kind: 'text', text } } return { kind: 'text', text: stringifyValue(value) } } @@ -134,6 +126,45 @@ function stringifyValue(value: unknown): string { return JSON.stringify(value) } +/** Returns a `sim-resource` cell kind when `text` is a URL pointing to a + * resource in the current workspace, else null. Shared by plain string cells + * and workflow-output value cells so both surface in-workspace resource links + * as tagged chips. */ +function resolveSimResourceKind( + text: string, + currentWorkspaceId: string | undefined +): Extract | null { + if (!currentWorkspaceId) return null + const resource = extractSimResourceInfo(text) + if (!resource || resource.workspaceId !== currentWorkspaceId) return null + return { + kind: 'sim-resource', + workspaceId: resource.workspaceId, + resourceType: resource.resourceType, + resourceId: resource.resourceId, + href: resource.href, + } +} + +/** + * Promotes a cell value that is wholly a resource/URL string to a chip + * (in-workspace resource) or a favicon link, else null. Shared by plain string + * cells and workflow-output value cells. Workflow outputs apply this regardless + * of `column.type` (their type defaults to `json`, so gating on `string` would + * miss URL outputs); a stringified object never matches the whole-string URL + * check, so it stays JSON/text. + */ +function resolveLinkKind( + text: string, + currentWorkspaceId: string | undefined +): Extract | null { + const simKind = resolveSimResourceKind(text, currentWorkspaceId) + if (simKind) return simKind + const urlInfo = extractUrlInfo(text) + if (urlInfo) return { kind: 'url', text, href: urlInfo.href, domain: urlInfo.domain } + return null +} + const BARE_DOMAIN_RE = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/ function extractUrlInfo(text: string): { href: string; domain: string } | null { diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx index 466835d041..9eb5a8de8e 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx @@ -478,14 +478,14 @@ export function Table({ } /> )} - {/* Sort + filter render in both modes. In embedded (mothership) mode there's - no ResourceHeader, so the run/stop control rides in the options bar's - `extras` slot — keeping the bar populated whether or not a run is live. */} + {/* Sort + filter render in both modes (left-aligned). In embedded (mothership) + mode there's no ResourceHeader, so the run/stop control rides in the options + bar's right-aligned `trailing` slot — opposite the left-aligned filter/sort. */} setFilterOpen((prev) => !prev)} filterActive={filterOpen || !!queryOptions.filter} - extras={ + trailing={ embedded && selection.totalRunning > 0 ? ( (null) const { data: workspaceCredentials } = useWorkspaceCredentials({ workspaceId, enabled: isOpen }) useRegisterGlobalCommands([ @@ -248,10 +251,7 @@ export function WorkflowSearchReplace() { ) useEffect(() => { - if (!isOpen) { - usePanelEditorSearchStore.getState().setActiveSearchTarget(null) - return - } + if (!isOpen) return searchInputRef.current?.focus() searchInputRef.current?.select() }, [isOpen]) @@ -311,10 +311,7 @@ export function WorkflowSearchReplace() { return [] }, [activeMatch, hydratedMatches]) - const eligibleMatchIds = useMemo( - () => replaceAllTargetMatches.map((match) => match.id), - [replaceAllTargetMatches] - ) + const eligibleMatchIds = replaceAllTargetMatches.map((match) => match.id) const controlTargetMatches = activeMatch ? [activeMatch] : [] const usesResourceReplacement = controlTargetMatches.some(isConstrainedResourceMatch) const resourceReplacementContextKey = @@ -324,23 +321,20 @@ export function WorkflowSearchReplace() { const replacement = resourceReplacementContextKey ? (resourceReplacementByContext[resourceReplacementContextKey] ?? '') : textReplacement - const handleReplacementChange = useCallback( - (nextReplacement: string) => { - if (!resourceReplacementContextKey) { - setReplacement(nextReplacement) - return - } + const handleReplacementChange = (nextReplacement: string) => { + if (!resourceReplacementContextKey) { + setReplacement(nextReplacement) + return + } - setResourceReplacementByContext((current) => ({ - ...current, - [resourceReplacementContextKey]: nextReplacement, - })) - }, - [resourceReplacementContextKey, setReplacement] - ) - const compatibleResourceOptions = useMemo( - () => getCompatibleResourceReplacementOptions(controlTargetMatches, resourceOptions), - [controlTargetMatches, resourceOptions] + setResourceReplacementByContext((current) => ({ + ...current, + [resourceReplacementContextKey]: nextReplacement, + })) + } + const compatibleResourceOptions = getCompatibleResourceReplacementOptions( + controlTargetMatches, + resourceOptions ) const hasReplacement = replacement.trim().length > 0 const activeReplacementIssue = activeMatch @@ -359,34 +353,51 @@ export function WorkflowSearchReplace() { }) : 'No replaceable matches.' - const applySubflowUpdate = useCallback( - (update: WorkflowSearchReplaceSubflowUpdate) => { - if (update.fieldId === WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.iterations) { - if (typeof update.nextValue !== 'number') return - collaborativeUpdateIterationCount(update.blockId, update.blockType, update.nextValue) - return - } + const applySubflowUpdate = (update: WorkflowSearchReplaceSubflowUpdate) => { + if (update.fieldId === WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.iterations) { + if (typeof update.nextValue !== 'number') return + collaborativeUpdateIterationCount(update.blockId, update.blockType, update.nextValue) + return + } - collaborativeUpdateIterationCollection( - update.blockId, - update.blockType, - String(update.nextValue) - ) - }, - [collaborativeUpdateIterationCollection, collaborativeUpdateIterationCount] - ) + collaborativeUpdateIterationCollection( + update.blockId, + update.blockType, + String(update.nextValue) + ) + } useEffect(() => { - if (!isOpen) return + if (!isOpen) { + prevIsOpenRef.current = false + usePanelEditorSearchStore.getState().setActiveSearchTarget(null) + return + } + + const justOpened = !prevIsOpenRef.current + prevIsOpenRef.current = true + const queryChanged = prevQueryRef.current !== query + prevQueryRef.current = query if (hydratedMatches.length === 0) { + afterReplaceIndexRef.current = null if (activeMatchId) setActiveMatchId(null) usePanelEditorSearchStore.getState().setActiveSearchTarget(null) return } if (!activeMatchId || !hydratedMatches.some((match) => match.id === activeMatchId)) { - handleSelectMatch(hydratedMatches[0].id) + const replaceIndex = afterReplaceIndexRef.current + afterReplaceIndexRef.current = null + + if (queryChanged || justOpened) { + handleSelectMatch(hydratedMatches[0].id) + } else if (replaceIndex !== null) { + handleSelectMatch(hydratedMatches[Math.min(replaceIndex, hydratedMatches.length - 1)].id) + } else { + setActiveMatchId(null) + usePanelEditorSearchStore.getState().setActiveSearchTarget(null) + } return } @@ -401,14 +412,19 @@ export function WorkflowSearchReplace() { const handleMoveActiveMatch = (delta: number) => { if (hydratedMatches.length === 0) return - const currentIndex = activeMatchIndex >= 0 ? activeMatchIndex : 0 - const nextIndex = (currentIndex + delta + hydratedMatches.length) % hydratedMatches.length + if (activeMatchIndex < 0) { + handleSelectMatch(hydratedMatches[delta > 0 ? 0 : hydratedMatches.length - 1].id) + return + } + const nextIndex = (activeMatchIndex + delta + hydratedMatches.length) % hydratedMatches.length handleSelectMatch(hydratedMatches[nextIndex].id) } - const handleApply = (matchIds: string[]) => { + const handleApply = (matchIds: string[], replaceActiveIndex?: number) => { if (!workflowId || isApplying || searchReadOnly) return + if (replaceActiveIndex !== undefined) afterReplaceIndexRef.current = replaceActiveIndex setIsApplying(true) + let committed = false try { const selectedIds = new Set(matchIds) @@ -505,14 +521,16 @@ export function WorkflowSearchReplace() { message: `Replaced ${replacedCount} field${replacedCount === 1 ? '' : 's'}.`, workflowId, }) + committed = true } finally { setIsApplying(false) + if (!committed) afterReplaceIndexRef.current = null } } const handleReplaceActive = () => { if (!activeMatch) return - handleApply([activeMatch.id]) + handleApply([activeMatch.id], activeMatchIndex) } const handleReplaceAll = () => { @@ -522,7 +540,9 @@ export function WorkflowSearchReplace() { const matchCountLabel = hydratedMatches.length === 0 ? 'No results' - : `${activeMatchIndex >= 0 ? activeMatchIndex + 1 : 1} of ${hydratedMatches.length}` + : activeMatchIndex >= 0 + ? `${activeMatchIndex + 1} of ${hydratedMatches.length}` + : `0 of ${hydratedMatches.length}` return (
diff --git a/apps/sim/blocks/blocks/cursor.ts b/apps/sim/blocks/blocks/cursor.ts index b6ef6a5241..c4ef939e76 100644 --- a/apps/sim/blocks/blocks/cursor.ts +++ b/apps/sim/blocks/blocks/cursor.ts @@ -33,6 +33,9 @@ export const CursorBlock: BlockConfig = { { label: 'Delete Agent', id: 'cursor_delete_agent' }, { label: 'List Artifacts', id: 'cursor_list_artifacts' }, { label: 'Download Artifact', id: 'cursor_download_artifact' }, + { label: 'List Models', id: 'cursor_list_models' }, + { label: 'List Repositories', id: 'cursor_list_repositories' }, + { label: 'Get API Key Info', id: 'cursor_get_api_key_info' }, ], value: () => 'cursor_launch_agent', }, @@ -183,6 +186,9 @@ export const CursorBlock: BlockConfig = { 'cursor_delete_agent', 'cursor_list_artifacts', 'cursor_download_artifact', + 'cursor_list_models', + 'cursor_list_repositories', + 'cursor_get_api_key_info', ], config: { tool: (params) => params.operation || 'cursor_launch_agent', @@ -236,6 +242,9 @@ export const CursorV2Block: BlockConfig = { 'cursor_delete_agent_v2', 'cursor_list_artifacts_v2', 'cursor_download_artifact_v2', + 'cursor_list_models_v2', + 'cursor_list_repositories_v2', + 'cursor_get_api_key_info_v2', ], config: { tool: createVersionedToolSelector({ @@ -253,11 +262,19 @@ export const CursorV2Block: BlockConfig = { source: { type: 'json', description: 'Agent source repository info' }, target: { type: 'json', description: 'Agent target branch/PR info' }, summary: { type: 'string', description: 'Agent summary' }, - createdAt: { type: 'string', description: 'Agent creation timestamp' }, + createdAt: { type: 'string', description: 'Creation timestamp (agent or API key)' }, agents: { type: 'json', description: 'Array of agent objects (list operation)' }, nextCursor: { type: 'string', description: 'Pagination cursor (list operation)' }, messages: { type: 'json', description: 'Conversation messages (get conversation operation)' }, artifacts: { type: 'json', description: 'List of artifact files (list artifacts operation)' }, file: { type: 'file', description: 'Downloaded artifact file (download artifact operation)' }, + models: { type: 'json', description: 'Available model names (list models operation)' }, + repositories: { + type: 'json', + description: + 'Accessible repositories [{owner, name, repository}] (list repositories operation)', + }, + apiKeyName: { type: 'string', description: 'API key name (api key info operation)' }, + userEmail: { type: 'string', description: 'Key owner email (api key info operation)' }, }, } diff --git a/apps/sim/blocks/blocks/devin.ts b/apps/sim/blocks/blocks/devin.ts index 291722e7d7..7460d29315 100644 --- a/apps/sim/blocks/blocks/devin.ts +++ b/apps/sim/blocks/blocks/devin.ts @@ -2,6 +2,14 @@ import { DevinIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +const SESSION_OBJECT_OPERATIONS = [ + 'create_session', + 'get_session', + 'send_message', + 'archive_session', + 'terminate_session', +] as const + export const DevinBlock: BlockConfig = { type: 'devin', name: 'Devin', @@ -32,6 +40,13 @@ export const DevinBlock: BlockConfig = { { label: 'Get Session', id: 'get_session' }, { label: 'List Sessions', id: 'list_sessions' }, { label: 'Send Message', id: 'send_message' }, + { label: 'List Session Messages', id: 'list_session_messages' }, + { label: 'List Session Attachments', id: 'list_session_attachments' }, + { label: 'Get Session Tags', id: 'get_session_tags' }, + { label: 'Append Session Tags', id: 'append_session_tags' }, + { label: 'Replace Session Tags', id: 'replace_session_tags' }, + { label: 'Archive Session', id: 'archive_session' }, + { label: 'Terminate Session', id: 'terminate_session' }, ], value: () => 'create_session', }, @@ -43,6 +58,13 @@ export const DevinBlock: BlockConfig = { password: true, required: true, }, + { + id: 'orgId', + title: 'Organization ID', + type: 'short-input', + placeholder: 'Enter your Devin organization ID (org-...)', + required: true, + }, { id: 'prompt', title: 'Prompt', @@ -86,16 +108,19 @@ RULES: title: 'Tags', type: 'short-input', placeholder: 'Comma-separated tags', - condition: { field: 'operation', value: 'create_session' }, - mode: 'advanced', + required: { field: 'operation', value: ['append_session_tags', 'replace_session_tags'] }, + condition: { + field: 'operation', + value: ['create_session', 'append_session_tags', 'replace_session_tags'], + }, }, { id: 'sessionId', title: 'Session ID', type: 'short-input', placeholder: 'Enter session ID', - required: { field: 'operation', value: ['get_session', 'send_message'] }, - condition: { field: 'operation', value: ['get_session', 'send_message'] }, + required: { field: 'operation', value: ['create_session', 'list_sessions'], not: true }, + condition: { field: 'operation', value: ['create_session', 'list_sessions'], not: true }, }, { id: 'message', @@ -109,8 +134,28 @@ RULES: id: 'limit', title: 'Limit', type: 'short-input', - placeholder: 'Number of sessions (1-200, default: 100)', - condition: { field: 'operation', value: 'list_sessions' }, + placeholder: 'Max results (1-200, default: 100)', + condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] }, + mode: 'advanced', + }, + { + id: 'after', + title: 'After Cursor', + type: 'short-input', + placeholder: 'Pagination cursor from a previous response', + condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] }, + mode: 'advanced', + }, + { + id: 'terminateArchive', + title: 'Archive Instead of Terminate', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + value: () => 'false', + condition: { field: 'operation', value: 'terminate_session' }, mode: 'advanced', }, ], @@ -120,15 +165,30 @@ RULES: 'devin_get_session', 'devin_list_sessions', 'devin_send_message', + 'devin_list_session_messages', + 'devin_list_session_attachments', + 'devin_get_session_tags', + 'devin_append_session_tags', + 'devin_replace_session_tags', + 'devin_archive_session', + 'devin_terminate_session', ], config: { tool: (params) => `devin_${params.operation}`, params: (params) => { if (params.maxAcuLimit != null && params.maxAcuLimit !== '') { - params.maxAcuLimit = Number(params.maxAcuLimit) + const parsed = Number(params.maxAcuLimit) + params.maxAcuLimit = Number.isFinite(parsed) ? parsed : undefined } if (params.limit != null && params.limit !== '') { - params.limit = Number(params.limit) + const parsed = Number(params.limit) + params.limit = Number.isFinite(parsed) ? parsed : undefined + } + if (params.terminateArchive != null && params.terminateArchive !== '') { + params.archive = + typeof params.terminateArchive === 'boolean' + ? params.terminateArchive + : params.terminateArchive === 'true' } return params }, @@ -139,51 +199,121 @@ RULES: sessionId: { type: 'string', description: 'Session ID' }, message: { type: 'string', description: 'Message to send to the session' }, apiKey: { type: 'string', description: 'Devin API key' }, + orgId: { type: 'string', description: 'Devin organization ID' }, playbookId: { type: 'string', description: 'Playbook ID to guide the session' }, maxAcuLimit: { type: 'number', description: 'Maximum ACU limit' }, - tags: { type: 'string', description: 'Comma-separated tags' }, - limit: { type: 'number', description: 'Number of sessions to return' }, + tags: { type: 'string', description: 'Tags (comma-separated string or array of strings)' }, + limit: { type: 'number', description: 'Maximum number of results to return' }, + after: { type: 'string', description: 'Pagination cursor for the next page' }, + terminateArchive: { + type: 'string', + description: 'Whether to archive instead of terminate the session', + }, }, outputs: { - sessionId: { type: 'string', description: 'Session identifier' }, - url: { type: 'string', description: 'URL to view the session in Devin UI' }, + sessionId: { + type: 'string', + description: 'Session identifier', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + url: { + type: 'string', + description: 'URL to view the session in Devin UI', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, status: { type: 'string', description: 'Session status (new, claimed, running, exit, error, suspended, resuming)', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, }, statusDetail: { type: 'string', description: 'Detailed status (working, waiting_for_user, finished, etc.)', - condition: { field: 'operation', value: 'list_sessions', not: true }, + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + title: { + type: 'string', + description: 'Session title', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + createdAt: { + type: 'number', + description: 'Creation timestamp (Unix)', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + updatedAt: { + type: 'number', + description: 'Last updated timestamp (Unix)', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, }, - title: { type: 'string', description: 'Session title' }, - createdAt: { type: 'number', description: 'Creation timestamp (Unix)' }, - updatedAt: { type: 'number', description: 'Last updated timestamp (Unix)' }, acusConsumed: { type: 'number', description: 'ACUs consumed', - condition: { field: 'operation', value: 'list_sessions', not: true }, + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + tags: { + type: 'json', + description: 'Session tags (array of strings)', + condition: { + field: 'operation', + value: [ + ...SESSION_OBJECT_OPERATIONS, + 'get_session_tags', + 'append_session_tags', + 'replace_session_tags', + ], + }, }, - tags: { type: 'json', description: 'Session tags' }, pullRequests: { type: 'json', - description: 'Pull requests created during the session', - condition: { field: 'operation', value: 'list_sessions', not: true }, + description: 'Pull requests created during the session ([{pr_url, pr_state}])', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, }, structuredOutput: { type: 'json', description: 'Structured output from the session', - condition: { field: 'operation', value: 'list_sessions', not: true }, + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, }, playbookId: { type: 'string', description: 'Associated playbook ID', - condition: { field: 'operation', value: 'list_sessions', not: true }, + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, + }, + isArchived: { + type: 'boolean', + description: 'Whether the session is archived', + condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] }, }, sessions: { type: 'json', - description: 'List of sessions', + description: + 'List of sessions ([{sessionId, url, status, statusDetail, title, tags, acusConsumed, pullRequests, playbookId, isArchived, ...}])', condition: { field: 'operation', value: 'list_sessions' }, }, + messages: { + type: 'json', + description: 'Messages in the session ([{eventId, source, message, createdAt}])', + condition: { field: 'operation', value: 'list_session_messages' }, + }, + attachments: { + type: 'json', + description: 'Session attachments ([{attachmentId, name, url, source, contentType}])', + condition: { field: 'operation', value: 'list_session_attachments' }, + }, + endCursor: { + type: 'string', + description: 'Pagination cursor for the next page, or null if last page', + condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] }, + }, + hasNextPage: { + type: 'boolean', + description: 'Whether more results are available', + condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] }, + }, + total: { + type: 'number', + description: 'Total number of results, if provided', + condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] }, + }, }, } diff --git a/apps/sim/blocks/blocks/google_sheets.ts b/apps/sim/blocks/blocks/google_sheets.ts index ef61d86444..eef0c84d0a 100644 --- a/apps/sim/blocks/blocks/google_sheets.ts +++ b/apps/sim/blocks/blocks/google_sheets.ts @@ -462,9 +462,15 @@ Return ONLY the range string - no sheet name, no explanations, no quotes.`, type: 'dropdown', options: [ { label: 'Contains', id: 'contains' }, + { label: 'Does Not Contain', id: 'not_contains' }, { label: 'Exact Match', id: 'exact' }, + { label: 'Not Equal To', id: 'not_equals' }, { label: 'Starts With', id: 'starts_with' }, { label: 'Ends With', id: 'ends_with' }, + { label: 'Greater Than', id: 'gt' }, + { label: 'Greater Than or Equal', id: 'gte' }, + { label: 'Less Than', id: 'lt' }, + { label: 'Less Than or Equal', id: 'lte' }, ], condition: { field: 'operation', value: 'read' }, mode: 'advanced', @@ -503,7 +509,7 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`, { label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' }, { label: "Raw (Don't parse formulas)", id: 'RAW' }, ], - condition: { field: 'operation', value: ['write', 'batch_update'] }, + condition: { field: 'operation', value: ['write', 'update', 'batch_update'] }, }, // Update-specific Fields { @@ -896,11 +902,15 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`, type: 'string', description: 'Destination spreadsheet ID for copy', }, - filterColumn: { type: 'string', description: 'Column header name to filter on' }, + filterColumn: { + type: 'string', + description: 'Column header name to filter the read rows on (within the read range)', + }, filterValue: { type: 'string', description: 'Value to match against the filter column' }, filterMatchType: { type: 'string', - description: 'Match type: contains, exact, starts_with, or ends_with', + description: + 'Match type: contains, not_contains, exact, not_equals, starts_with, ends_with, gt, gte, lt, or lte', }, }, outputs: { @@ -920,6 +930,12 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`, description: 'Cell values as 2D array', condition: { field: 'operation', value: 'read' }, }, + filter: { + type: 'json', + description: + 'Filter summary (present only when a filter was requested): applied, column, matchType, columnFound, matchedRows, totalRows', + condition: { field: 'operation', value: 'read' }, + }, // Write/Update/Append outputs updatedRange: { type: 'string', diff --git a/apps/sim/blocks/blocks/greptile.ts b/apps/sim/blocks/blocks/greptile.ts index 76cb142cf3..83de267de4 100644 --- a/apps/sim/blocks/blocks/greptile.ts +++ b/apps/sim/blocks/blocks/greptile.ts @@ -14,7 +14,7 @@ export const GreptileBlock: BlockConfig = { category: 'tools', integrationType: IntegrationType.DeveloperTools, tags: ['version-control', 'knowledge-base'], - bgColor: '#e5e5e5', + bgColor: '#5DE195', icon: GreptileIcon, subBlocks: [ { @@ -89,7 +89,7 @@ export const GreptileBlock: BlockConfig = { // type: 'switch', // condition: { field: 'operation', value: 'greptile_search' }, // }, - // Index operation inputs + // Index & Status shared inputs { id: 'remote', title: 'Git Remote', @@ -99,14 +99,14 @@ export const GreptileBlock: BlockConfig = { { label: 'GitLab', id: 'gitlab' }, ], value: () => 'github', - condition: { field: 'operation', value: 'greptile_index_repo' }, + condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] }, }, { id: 'repository', title: 'Repository', type: 'short-input', placeholder: 'owner/repo', - condition: { field: 'operation', value: 'greptile_index_repo' }, + condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] }, required: true, }, { @@ -114,9 +114,10 @@ export const GreptileBlock: BlockConfig = { title: 'Branch', type: 'short-input', placeholder: 'main', - condition: { field: 'operation', value: 'greptile_index_repo' }, + condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] }, required: true, }, + // Index-only inputs { id: 'reload', title: 'Force Re-index', @@ -129,34 +130,6 @@ export const GreptileBlock: BlockConfig = { type: 'switch', condition: { field: 'operation', value: 'greptile_index_repo' }, }, - // Status operation inputs - { - id: 'remote', - title: 'Git Remote', - type: 'dropdown', - options: [ - { label: 'GitHub', id: 'github' }, - { label: 'GitLab', id: 'gitlab' }, - ], - value: () => 'github', - condition: { field: 'operation', value: 'greptile_status' }, - }, - { - id: 'repository', - title: 'Repository', - type: 'short-input', - placeholder: 'owner/repo', - condition: { field: 'operation', value: 'greptile_status' }, - required: true, - }, - { - id: 'branch', - title: 'Branch', - type: 'short-input', - placeholder: 'main', - condition: { field: 'operation', value: 'greptile_status' }, - required: true, - }, // API Keys (common) { id: 'apiKey', diff --git a/apps/sim/blocks/blocks/quiver.ts b/apps/sim/blocks/blocks/quiver.ts index 4f09fb0c37..d1be1b79a4 100644 --- a/apps/sim/blocks/blocks/quiver.ts +++ b/apps/sim/blocks/blocks/quiver.ts @@ -14,7 +14,7 @@ export const QuiverBlock: BlockConfig = { category: 'tools', integrationType: IntegrationType.Design, tags: ['image-generation'], - bgColor: '#000000', + bgColor: '#FFFFFF', icon: QuiverIcon, authMode: AuthMode.ApiKey, subBlocks: [ diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 7985328c08..2417e6acb5 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -91,7 +91,12 @@ export function AgentPhoneIcon(props: SVGProps) { export function CrowdStrikeIcon(props: SVGProps) { return ( - + ) { export function FirecrawlIcon(props: SVGProps) { return ( - + ) { export function SerperIcon(props: SVGProps) { return ( - + ) { export function NotionIcon(props: SVGProps) { return ( - + @@ -1171,27 +1176,13 @@ export function GrafanaIcon(props: SVGProps) { const gradientId = `grafana_gradient_${id}` return ( - + - + @@ -1504,7 +1495,7 @@ export function ProspeoIcon(props: SVGProps) { {...props} width='1em' height='1em' - viewBox='0 0 32 32' + viewBox='1.17 1.178 29.66 29.643' fill='none' xmlns='http://www.w3.org/2000/svg' > @@ -2184,27 +2175,8 @@ export function MistralIcon(props: SVGProps) { export function BrainIcon(props: SVGProps) { return ( - - - - - - - - - - + + ) } @@ -2256,14 +2228,19 @@ export function BrandfetchIcon(props: SVGProps) { export function BrightDataIcon(props: SVGProps) { return ( - + ) @@ -2346,8 +2323,8 @@ export function ExtendIcon(props: SVGProps) { export function EvernoteIcon(props: SVGProps) { return ( - - + + ) } @@ -2542,7 +2519,7 @@ export function TelegramIcon(props: SVGProps) { ) { export function QuiverIcon(props: SVGProps) { return ( - + ) { } export function MicrosoftOneDriveIcon(props: SVGProps) { + const id = useId() return ( - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } @@ -4413,37 +4569,47 @@ export function Neo4jIcon(props: SVGProps) { export function CalendlyIcon(props: SVGProps) { return ( - - - - - - - - + + + + + ) } @@ -4735,8 +4901,8 @@ export function ZendeskIcon(props: SVGProps) { export function ZoomIcon(props: SVGProps) { return ( ) } @@ -4747,7 +4913,7 @@ export function ZoomInfoIcon(props: SVGProps) { return (